]> git.neil.brown.name Git - history.git/commitdiff
Import 2.0.40pre2 2.0.40pre2
authorDavid Weinehall <tao@acc.umu.se>
Fri, 23 Nov 2007 20:12:24 +0000 (15:12 -0500)
committerDavid Weinehall <tao@acc.umu.se>
Fri, 23 Nov 2007 20:12:24 +0000 (15:12 -0500)
69 files changed:
Documentation/Configure.help
Documentation/magic-number.txt
Makefile
arch/alpha/config.in
arch/i386/config.in
arch/i386/kernel/process.c
arch/i386/kernel/ptrace.c
arch/i386/kernel/signal.c
arch/i386/kernel/traps.c
arch/i386/math-emu/Makefile
arch/i386/math-emu/README
arch/i386/math-emu/div_Xsig.S
arch/i386/math-emu/div_small.S
arch/i386/math-emu/errors.c
arch/i386/math-emu/exception.h
arch/i386/math-emu/fpu_arith.c
arch/i386/math-emu/fpu_asm.h
arch/i386/math-emu/fpu_aux.c
arch/i386/math-emu/fpu_debug.c [deleted file]
arch/i386/math-emu/fpu_emu.h
arch/i386/math-emu/fpu_entry.c
arch/i386/math-emu/fpu_etc.c
arch/i386/math-emu/fpu_proto.h
arch/i386/math-emu/fpu_system.h
arch/i386/math-emu/fpu_tags.c [new file with mode: 0644]
arch/i386/math-emu/fpu_trig.c
arch/i386/math-emu/get_address.c
arch/i386/math-emu/load_store.c
arch/i386/math-emu/poly.h
arch/i386/math-emu/poly_2xm1.c
arch/i386/math-emu/poly_atan.c
arch/i386/math-emu/poly_l2.c
arch/i386/math-emu/poly_sin.c
arch/i386/math-emu/poly_tan.c
arch/i386/math-emu/reg_add_sub.c
arch/i386/math-emu/reg_compare.c
arch/i386/math-emu/reg_constant.c
arch/i386/math-emu/reg_convert.c [new file with mode: 0644]
arch/i386/math-emu/reg_div.S [deleted file]
arch/i386/math-emu/reg_divide.c [new file with mode: 0644]
arch/i386/math-emu/reg_ld_str.c
arch/i386/math-emu/reg_mul.c
arch/i386/math-emu/reg_norm.S
arch/i386/math-emu/reg_round.S
arch/i386/math-emu/reg_u_add.S
arch/i386/math-emu/reg_u_div.S
arch/i386/math-emu/reg_u_mul.S
arch/i386/math-emu/reg_u_sub.S
arch/i386/math-emu/status_w.h
arch/i386/math-emu/version.h
arch/i386/math-emu/wm_shrx.S
arch/i386/math-emu/wm_sqrt.S
arch/m68k/config.in
arch/mips/config.in
arch/ppc/config.in
arch/sparc/config.in
drivers/net/auto_irq.c
drivers/scsi/pci2000.c
fs/ext2/dir.c
fs/ext2/super.c
include/asm-i386/math_emu.h
include/asm-i386/processor.h
include/linux/skbuff.h
kernel/sched.c
mm/kmalloc.c
net/core/skbuff.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
scripts/mkdep.c

index d07539ae71817636a638c07ee1a6447ded28f9ee..538441a3c926b51b05a50355a8537c67aa987f42 100644 (file)
@@ -4977,6 +4977,39 @@ CONFIG_MEM_STD
   split subject to kernel constraints. If you don't know how it works
   don't pick it.
 
+Memory flooding
+CONFIG_SADISTIC_KMALLOC
+  A common class of errors, both in userspace programs and the kernel,
+  is accessing memory areas after freeing them.  Programs with such
+  errors often work anyway most of the time, since the bug only bites
+  if something else happens to allocate the same area in a short time
+  window.
+  This option will cause the memory allocator to immediately destroy 
+  any data that has been freed within the kernel, causing such bugs to
+  manifest consistently so they may be solved.  Newly allocated memory
+  is also flooded, making failure-to-initialize bugs easier to detect.
+  This feature does not affect bugs in userspace programs.
+  Non-kernel hackers should say N.  If there are any such bugs in the
+  kernel, there is no point provoking them if you are not prepared to
+  fix them.  And it will run slightly slower.
+
+Socket-buffer consistency checking
+CONFIG_SKB_CHECK
+  The networking facilities of Linux use a special system of memory
+  management, known as socket buffers or SKBs.  SKB structures are
+  moderately complicated and could be corrupted by kernel bugs, later 
+  causing puzzling crashes.
+  This option causes SKBs to be rechecked almost every time they are
+  used, leading to earlier detection of such problems.  This will
+  reduce performance -- however it does not affect the likelihood of
+  the kernel crashing.  Non-kernel hackers should say N.
+
+Whole-queue checking
+CONFIG_SKB_CHECK_WHOLE_QUEUE
+  This option is an extension of CONFIG_SKB_CHECK.  Whenever a list
+  header is validated, this mode will traverse the entire list looking
+  for inconsistent pointers.
+
 # need an empty line after last entry, for sed script in Configure.
 
 #
index 1c28f155fac5026ab624cd845ca8ac869506996a..ed55371198c50c316174a2fcd8be2c84b7e3e86a 100644 (file)
@@ -34,7 +34,12 @@ The magic table is current to Linux 1.3.98.
                                        <mec@duracef.shout.net>
                                        6 May 1996
 
+Added SKB-debugging values.  Note that they only appear under 
+CONFIG_SKB_CHECK, and are 12 bytes inside the structure.
 
+                                       Michael Deutschmann
+                                       <michael@talamasca.ocis.net>
+                                       2000-07-24
 
 Magic Name          Number      Structure            File
 ===========================================================================
@@ -60,4 +65,7 @@ STL_BOARDMAGIC      0xa2267f52  stlbrd_t             include/linux/stallion.h
 STL_PORTMAGIC       0x5a7182c9  stlport_t            include/linux/stallion.h
 PCXX_MAGIC          0x5c6df104  struct channel       drivers/char/pcxx.h
 BAYCOM_MAGIC        0x3105bac0  struct baycom_state  drivers/char/baycom.c
+SK_FREED_SKB        0x0de2code  struct sk_buff       include/linux/skbuff.h
+SK_GOOD_SKB         0xdec0ded1  struct sk_buff       include/linux/skbuff.h
+SK_HEAD_SKB         0x12231298  struct sk_buff_head  include/linux/skbuff.h
 
index 097a2ce7b4518d6a48a8b942968571da234d739a..2d2827aff531a243c22592406cbbc694d5009d08 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -300,7 +300,7 @@ modules: include/linux/version.h
 
 modules_install:
        @( \
-       MODLIB=/lib/modules/$(VERSION).$(PATCHLEVEL).$(SUBLEVEL); \
+       MODLIB=/lib/modules/$(KERNELRELEASE); \
        cd modules; \
        MODULES=""; \
        inst_mod() { These="`cat $$1`"; MODULES="$$MODULES $$These"; \
index e729fce2cfd2be336f690c515a937195489d042f..ff5882840cede472a19511a3dc9fbf1c4a7cca96 100644 (file)
@@ -227,6 +227,11 @@ bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
    int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
+bool 'Memory flooding' CONFIG_SADISTIC_KMALLOC
+bool 'Socket-buffer consistency checking' CONFIG_SKB_CHECK
+if [ "$CONFIG_SKB_CHECK" = "y" ]; then
+   bool '  Whole-queue checking' CONFIG_SKB_CHECK_WHOLE_QUEUE
+fi
 endmenu
 
 if [ "$CONFIG_TGA_CONSOLE" = "n" ]; then
index 65d4a2641035ba011460043ae0db0318db4a30cd..e42167b9d829b4ef6a552754f584bb2d950973c5 100644 (file)
@@ -141,4 +141,9 @@ bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
    int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
+bool 'Memory flooding' CONFIG_SADISTIC_KMALLOC
+bool 'Socket-buffer consistency checking' CONFIG_SKB_CHECK
+if [ "$CONFIG_SKB_CHECK" = "y" ]; then
+   bool '  Whole-queue checking' CONFIG_SKB_CHECK_WHOLE_QUEUE
+fi
 endmenu
index 1f3208aa8b2cf68a027682905d1ba9734d3d2272..ff0c7d9342d93397aa4d47f4d2ce71d43145bd10 100644 (file)
@@ -520,35 +520,8 @@ int dump_fpu (struct pt_regs * regs, struct user_i387_struct* fpu)
                /* We dump the emulator state here.
                   We convert it into standard 387 format first.. */
 #ifdef CONFIG_MATH_EMULATION
-               int i;
-               unsigned long top;
-               char (*hardreg)[10];
-               struct i387_soft_struct *soft_fpu = &current->tss.i387.soft;
-               struct fpu_reg* softreg;
-               long int control_word = soft_fpu->cwd;
-
-               fpu->cwd = soft_fpu->cwd;
-               fpu->swd = soft_fpu->swd;
-               fpu->twd = soft_fpu->twd;
-               fpu->fip = soft_fpu->fip;
-               fpu->fcs = soft_fpu->fcs;
-               fpu->foo = soft_fpu->foo;
-               fpu->fos = soft_fpu->fos;
-               hardreg = (char (*)[10]) &fpu->st_space[0];
-               top = (unsigned long) soft_fpu->top % 8;
-               softreg = &soft_fpu->regs[top];
-               for (i = top ; i < 8; i ++) {
-                       softreg_to_hardreg(softreg, *hardreg, control_word);
-                       hardreg++;
-                       softreg++;
-               }
-               softreg = &soft_fpu->regs[0];
-               for (i = 0; i < top; i++) {
-                       softreg_to_hardreg(softreg, *hardreg, control_word);
-                       hardreg++;
-                       softreg++;
-               }
-               fpvalid = 1;   
+               fpvalid = save_i387_soft(&current->tss.i387.soft,
+                                        (struct _fpstate *)fpu);
 #else /* defined(CONFIG_MATH_EMULATION) */
                fpvalid = 0;
 #endif /* !defined(CONFIG_MATH_EMULATION) */
index 7603eff5b222c33096c70e247fc44fb2f666b7e2..d5e0b6b5f815299a8bb32e51593ea510a4390d79 100644 (file)
@@ -295,68 +295,12 @@ static int write_long(struct task_struct * tsk, unsigned long addr,
                put_long(tsk, vma, addr, data);
        return 0;
 }
-#ifdef CONFIG_MATH_EMULATION
-static void write_emulator_word(struct task_struct *child,
-                               unsigned long register_offset,
-                               long data)
-{
-       int i, j;
-       struct i387_soft_struct *soft_fpu;
-       struct fpu_reg *this_fpreg, *next_fpreg;
-       char hard_reg[2][10];
-       int control_word;
-       unsigned long top;
-       i = register_offset / 10;
-       j = register_offset % 10;
-       soft_fpu = &child->tss.i387.soft;
-       top = i + (unsigned long) soft_fpu->top;
-       control_word = soft_fpu->cwd;
-       this_fpreg = &soft_fpu->regs[(top + i) % 8];
-       next_fpreg = &soft_fpu->regs[(top + i + 1) % 8];
-       softreg_to_hardreg(this_fpreg, hard_reg[0], control_word);
-       if (j > 6)
-               softreg_to_hardreg(next_fpreg, hard_reg[1], control_word);
-       *(long *) &hard_reg[0][j] = data;
-       hardreg_to_softreg(hard_reg[0], this_fpreg);
-       if (j > 6)
-               hardreg_to_softreg(hard_reg[1], next_fpreg);
-}
-#endif /* defined(CONFIG_MATH_EMULATION) */
 
 /*
  * Floating point support added to ptrace by Ramon Garcia,
  * ramon@juguete.quim.ucm.es
  */
 
-#ifdef CONFIG_MATH_EMULATION
-
-static unsigned long get_emulator_word(struct task_struct *child,
-                                      unsigned long register_offset)
-{
-       char hard_reg[2][10];
-       int i, j;
-       struct fpu_reg *this_fpreg, *next_fpreg;
-       struct i387_soft_struct *soft_fpu;
-       long int control_word;
-       unsigned long top;
-       unsigned long tmp;
-       i = register_offset / 10;
-       j = register_offset % 10;
-       soft_fpu = &child->tss.i387.soft;
-       top = (unsigned long) soft_fpu->top;
-       this_fpreg = &soft_fpu->regs[(top + i) % 8];
-       next_fpreg = &soft_fpu->regs[(top + i + 1) % 8];
-       control_word = soft_fpu->cwd;
-       softreg_to_hardreg(this_fpreg, hard_reg[0], control_word);
-       if (j > 6)
-               softreg_to_hardreg(next_fpreg, hard_reg[1], control_word);
-       tmp = *(long *)
-               &hard_reg[0][j];
-       return tmp;
-}
-
-#endif /* defined(CONFIG_MATH_EMULATION) */
-
 static int putreg(struct task_struct *child,
        unsigned long regno, unsigned long value)
 {
index b5544a69a42c6660beb3142d2205582839b038d1..8180feff5d5dbd32e0970e3240a322b1f444168b 100644 (file)
@@ -69,7 +69,7 @@ static void restore_i387(struct _fpstate *buf)
                restore_i387_hard(buf);
                return;
        }
-       restore_i387_soft(buf);
+       restore_i387_soft(&current->tss.i387.soft, buf);
 #endif 
 }
        
@@ -152,7 +152,8 @@ static struct _fpstate * save_i387(struct _fpstate * buf)
 #else
        if (hard_math)
                return save_i387_hard(buf);
-       return save_i387_soft(buf);
+       save_i387_soft(&current->tss.i387.soft, buf);
+       return buf;
 #endif
 }
 
index 6c30bb32ef80d4dfaf961c412804ba592b7f6cd9..2757dd8fcabce14d717011919ab1ea9ab7d21ccc 100644 (file)
@@ -62,21 +62,22 @@ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
 #define get_seg_byte(seg,addr) ({ \
 register unsigned char __res; \
 __asm__("push %%fs; mov %%ax, %%fs; movb %%fs:%2, %%al; pop %%fs" \
-       : "=a" (__res)
+       : "=a" (__res) \
        : "0" (seg), "m" (*(addr))); \
 __res;})
 
 #define get_seg_long(seg,addr) ({ \
 register unsigned long __res; \
 __asm__("push %%fs; mov %%ax, %%fs; movl %%fs:%2, %%eax; pop %%fs" \
-       : "=a" (__res)
+       : "=a" (__res) \
        : "0" (seg), "m" (*(addr))); \
 __res;})
 
 #define _fs() ({ \
 register unsigned short __res; \
-__asm__("mov %%fs, %%ax"
-       : "=a" (__res):); \
+__asm__("mov %%fs, %%ax" \
+       : "=a" (__res) \
+       :); \
 __res;})
 
 void page_exception(void);
@@ -206,7 +207,7 @@ asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
                return;
        }
 
-       /* 
+       /*
         * HACK HACK HACK  :)  Fixing the segment invalid on syscall return
         * barfage for 2.0 has been put into the too-hard basket but having
         * a user producing endless GPFs is unacceptable as well. - Paul G.
@@ -234,13 +235,13 @@ asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
 {
 #ifdef CONFIG_SMP_NMI_INVAL
        smp_flush_tlb_rcv();
-#else
+#else /* CONFIG_SMP_NMI_INVAL */
 #ifndef CONFIG_IGNORE_NMI
        printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n");
        printk("You probably have a hardware problem with your RAM chips or a\n");
        printk("power saving mode enabled.\n");
-#endif
-#endif
+#endif /* !CONFIG_IGNORE_NMI */
+#endif /* !CONFIG_SMP_NMI_INVAL */
 }
 
 asmlinkage void do_debug(struct pt_regs * regs, long error_code)
@@ -344,8 +345,7 @@ asmlinkage void math_state_restore(void)
                __asm__("frstor %0"
                        :
                        : "m" (current->tss.i387));
-       else
-       {
+       else {
                /*
                 *      Our first FPU usage, clean the chip.
                 */
@@ -418,8 +418,7 @@ void trap_init(void)
        struct desc_struct * p;
        static int smptrap=0;
 
-       if(smptrap)
-       {
+       if(smptrap) {
                __asm__("pushfl; andl $0xffffbfff, (%esp); popfl");
                load_ldt(0);
                return;
index 200eb4d6d4ab323e7cd6619bfa3440cfb5952a22..e8e728e0618a50161ab6d1ea51f291306ac11c67 100644 (file)
@@ -13,17 +13,17 @@ CFLAGS      := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin $(MATH_EMULATION)
        $(CC) -D__ASSEMBLY__ $(PARANOID) -c $<
 
 L_OBJS =fpu_entry.o div_small.o errors.o \
-       fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \
+       fpu_arith.o fpu_aux.o fpu_etc.o fpu_tags.o fpu_trig.o \
        load_store.o get_address.o \
        poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \
-       reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o \
-       reg_div.o reg_mul.o reg_norm.o \
+       reg_add_sub.o reg_compare.o reg_constant.o reg_convert.o \
+       reg_ld_str.o \
+       reg_divide.o reg_mul.o reg_norm.o \
        reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \
        reg_round.o \
        wm_shrx.o wm_sqrt.o \
        div_Xsig.o polynom_Xsig.o round_Xsig.o \
-       shr_Xsig.o mul_Xsig.o \
-       fpu_debug.o
+       shr_Xsig.o mul_Xsig.o
 
 include $(TOPDIR)/Rules.make
 
index 5158b4b81e804a5d29490ae7b974dfb14f72fb5b..392dacd77e6d2bf10eaa2bc075ed9e7acf08538c 100644 (file)
@@ -1,9 +1,9 @@
  +---------------------------------------------------------------------------+
  |  wm-FPU-emu   an FPU emulator for 80386 and 80486SX microprocessors.      |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1995,1996                                    |
+ | Copyright (C) 1992,1993,1994,1995,1996,1997,1999,2001                     |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@suburbia.net               |
+ |                       Australia.  E-mail billm@melbpc.org.au              |
  |                                                                           |
  |    This program is free software; you can redistribute it and/or modify   |
  |    it under the terms of the GNU General Public License version 2 as      |
@@ -42,11 +42,14 @@ but is very close.  See "Limitations" later in this file for a list of
 some differences.
 
 Please report bugs, etc to me at:
-       billm@suburbia.net
+       billm@melbpc.org.au
+
+For more information on the emulator and on floating point topics, see
+my web pages, currently at  http://floatingpoint.sourceforge.net/
 
 
 --Bill Metzenthen
-  October 1996
+  August 2001
 
 
 ----------------------- Internals of wm-FPU-emu -----------------------
@@ -95,8 +98,9 @@ form of re-entrancy which is required by the Linux kernel.
 ----------------------- Limitations of wm-FPU-emu -----------------------
 
 There are a number of differences between the current wm-FPU-emu
-(version 1.20) and the 80486 FPU (apart from bugs). Some of the more
-important differences are listed below:
+(version 2.01) and the 80486 FPU (apart from bugs).  The differences
+are fewer than those which applied to the 1.xx series of the emulator.
+Some of the more important differences are listed below:
 
 The Roundup flag does not have much meaning for the transcendental
 functions and its 80486 value with these functions is likely to differ
@@ -122,24 +126,12 @@ and Unnormals. None of these will be generated by an 80486 or by the
 emulator. Do not use them. The emulator treats them differently in
 detail from the way an 80486 does.
 
-The emulator treats PseudoDenormals differently from an 80486. These
-numbers are in fact properly normalised numbers with the exponent
-offset by 1, and the emulator treats them as such. Unlike the 80486,
-the emulator does not generate a Denormal Operand exception for these
-numbers. The arithmetical results produced when using such a number as
-an operand are the same for the emulator and a real 80486 (apart from
-any slight precision difference for the transcendental functions).
-Neither the emulator nor an 80486 produces one of these numbers as the
-result of any arithmetic operation. An 80486 can keep one of these
-numbers in an FPU register with its identity as a PseudoDenormal, but
-the emulator will not; they are always converted to a valid number.
-
 Self modifying code can cause the emulator to fail. An example of such
 code is:
           movl %esp,[%ebx]
          fld1
 The FPU instruction may be (usually will be) loaded into the pre-fetch
-queue of the cpu before the mov instruction is executed. If the
+queue of the CPU before the mov instruction is executed. If the
 destination of the 'movl' overlaps the FPU instruction then the bytes
 in the prefetch queue and memory will be inconsistent when the FPU
 instruction is executed. The emulator will be invoked but will not be
@@ -171,7 +163,7 @@ Speed.
 The speed of floating point computation with the emulator will depend
 upon instruction mix. Relative performance is best for the instructions
 which require most computation. The simple instructions are adversely
-affected by the fpu instruction trap overhead.
+affected by the FPU instruction trap overhead.
 
 
 Timing: Some simple timing tests have been made on the emulator functions.
index ecc32821cbcec7197c8e056b15e043bace1ea720..f77ba3058b31748f898dfbadc7ad8838dbceb8d0 100644 (file)
@@ -55,7 +55,7 @@
        Local storage in a static area:
        Accumulator:    FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
  */
-       .align 2,0
+       .align 4,0
 FPU_accum_3:
        .long   0
 FPU_accum_2:
@@ -79,7 +79,7 @@ ENTRY(div_Xsig)
        movl    %esp,%ebp
 #ifndef NON_REENTRANT_FPU
        subl    $28,%esp
-#endif /* NON_REENTRANT_FPU */
+#endif /* NON_REENTRANT_FPU */ 
 
        pushl   %esi
        pushl   %edi
@@ -164,7 +164,7 @@ LFirst_div_done:
 
 #ifdef PARANOID
        jb      L_bugged_1
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
        /* need to subtract another once of the denom */
        incl    FPU_result_3    /* Correct the answer */
@@ -177,7 +177,7 @@ LFirst_div_done:
 #ifdef PARANOID
        sbbl    $0,FPU_accum_3
        jne     L_bugged_1      /* Must check for non-zero result here */
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
 /*----------------------------------------------------------------------*/
 /* Half of the main problem is done, there is just a reduced numerator
@@ -207,7 +207,7 @@ LPrevent_2nd_overflow:
 
 #ifdef PARANOID
        je      L_bugged_2      /* Can't bump the result to 1.0 */
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
 LDo_2nd_div:
        cmpl    $0,%ecx         /* augmented denom msw */
@@ -248,7 +248,7 @@ LSecond_div_done:
 #ifdef PARANOID
        cmpl    $1,FPU_accum_2
        jne     L_bugged_2
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
        /* need to subtract another once of the denom */
        movl    XsigL(%ebx),%eax
@@ -260,14 +260,14 @@ LSecond_div_done:
 #ifdef PARANOID
        jc      L_bugged_2
        jne     L_bugged_2
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
        addl    $1,FPU_result_2 /* Correct the answer */
        adcl    $0,FPU_result_3
 
 #ifdef PARANOID
        jc      L_bugged_2      /* Must check for non-zero result here */
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
 /*----------------------------------------------------------------------*/
 /* The division is essentially finished here, we just need to perform
@@ -362,4 +362,4 @@ L_bugged_2:
        call    EXCEPTION
        pop     %ebx
        jmp     L_exit
-#endif /* PARANOID */
+#endif /* PARANOID */ 
index 13ab2b7ae5bec7742472b1711d4164b7c567648f..47099628fa4cebdebce228696aa5fe3d698cabb9 100644 (file)
  +---------------------------------------------------------------------------*/
 
 /*---------------------------------------------------------------------------+
- |    unsigned long div_small(unsigned long long *x, unsigned long y)        |
+ |    unsigned long FPU_div_small(unsigned long long *x, unsigned long y)    |
  +---------------------------------------------------------------------------*/
 
 #include "fpu_emu.h"
 
 .text
-ENTRY(div_small)
+ENTRY(FPU_div_small)
        pushl   %ebp
        movl    %esp,%ebp
 
index 9e6f75d57bea78a2de663b5847a9ff7c777f9785..d8766aea8c4f95d48526943306f7c9966fb2e893 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  |  The error handling functions for wm-FPU-emu                              |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1996                                         |
+ | Copyright (C) 1992,1993,1994,1996,2001                                    |
  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
- |                  E-mail   billm@jacobi.maths.monash.edu.au                |
+ |                  E-mail   billm@melbpc.org.au                             |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
 #include <linux/signal.h>
 
-#include <asm/segment.h>
-
+#include "fpu_emu.h"
 #include "fpu_system.h"
 #include "exception.h"
-#include "fpu_emu.h"
 #include "status_w.h"
 #include "control_w.h"
 #include "reg_constant.h"
 
 void Un_impl(void)
 {
-  unsigned char byte1, FPU_modrm;
+  u_char byte1, FPU_modrm;
   unsigned long address = FPU_ORIG_EIP;
 
   RE_ENTRANT_CHECK_OFF;
   /* No need to verify_area(), we have previously fetched these bytes. */
   printk("Unimplemented FPU Opcode at eip=%p : ", (void *) address);
-  if ( FPU_CS == USER_CS )
+  if ( FPU_CS == FPU_USER_CS )
     {
       while ( 1 )
        {
-         byte1 = get_fs_byte((unsigned char *) address);
+         FPU_get_user(byte1, (u_char *) address);
          if ( (byte1 & 0xf8) == 0xd8 ) break;
          printk("[%02x]", byte1);
          address++;
        }
       printk("%02x ", byte1);
-      FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
+      FPU_get_user(FPU_modrm, 1 + (u_char *) address);
       
       if (FPU_modrm >= 0300)
        printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
@@ -77,28 +75,28 @@ void Un_impl(void)
    */
 void FPU_illegal(void)
 {
-  math_abort(FPU_info,SIGILL);
+  math_abort(SIGILL);
 }
 
 
 
-void emu_printall(void)
+void FPU_printall(void)
 {
   int i;
-  static const char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR",
-                              "DeNorm", "Inf", "NaN", "Empty" };
-  unsigned char byte1, FPU_modrm;
+  static const char *tag_desc[] = { "Valid", "Zero", "ERROR", "Empty",
+                              "DeNorm", "Inf", "NaN" };
+  u_char byte1, FPU_modrm;
   unsigned long address = FPU_ORIG_EIP;
 
   RE_ENTRANT_CHECK_OFF;
   /* No need to verify_area(), we have previously fetched these bytes. */
   printk("At %p:", (void *) address);
-  if ( FPU_CS == USER_CS )
+  if ( FPU_CS == FPU_USER_CS )
     {
 #define MAX_PRINTED_BYTES 20
       for ( i = 0; i < MAX_PRINTED_BYTES; i++ )
        {
-         byte1 = get_fs_byte((unsigned char *) address);
+         FPU_get_user(byte1, (u_char *) address);
          if ( (byte1 & 0xf8) == 0xd8 )
            {
              printk(" %02x", byte1);
@@ -111,7 +109,7 @@ void emu_printall(void)
        printk(" [more..]\n");
       else
        {
-         FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
+         FPU_get_user(FPU_modrm, 1 + (u_char *) address);
          
          if (FPU_modrm >= 0300)
            printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
@@ -122,7 +120,7 @@ void emu_printall(void)
     }
   else
     {
-      printk("%04x\n", FPU_CS);
+      printk("%04x (Not USER_CS)\n", FPU_CS);
     }
 
   partial_status = status_word();
@@ -166,29 +164,23 @@ printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d     ef=%d%d%d%d%d%d\n",
   for ( i = 0; i < 8; i++ )
     {
       FPU_REG *r = &st(i);
-      char tagi = r->tag;
+      u_char tagi = FPU_gettagi(i);
       switch (tagi)
        {
-       case TW_Empty:
+       case TAG_Empty:
          continue;
          break;
-       case TW_Zero:
-#if 0
-         printk("st(%d)  %c .0000 0000 0000 0000         ",
-                i, r->sign ? '-' : '+');
-         break;
-#endif
-       case TW_Valid:
-       case TW_NaN:
-/*     case TW_Denormal: */
-       case TW_Infinity:
-         printk("st(%d)  %c .%04lx %04lx %04lx %04lx e%+-6ld ", i,
-                r->sign ? '-' : '+',
+       case TAG_Zero:
+       case TAG_Special:
+         tagi = FPU_Special(r);
+       case TAG_Valid:
+         printk("st(%d)  %c .%04lx %04lx %04lx %04lx e%+-6d ", i,
+                getsign(r) ? '-' : '+',
                 (long)(r->sigh >> 16),
                 (long)(r->sigh & 0xFFFF),
                 (long)(r->sigl >> 16),
                 (long)(r->sigl & 0xFFFF),
-                r->exp - EXP_BIAS + 1);
+                exponent(r) - EXP_BIAS + 1);
          break;
        default:
          printk("Whoops! Error in errors.c: tag%d is %d ", i, tagi);
@@ -262,6 +254,11 @@ static struct {
              0x161  in reg_ld_str.c
              0x162  in reg_ld_str.c
              0x163  in reg_ld_str.c
+             0x164  in reg_ld_str.c
+             0x170  in fpu_tags.c
+             0x171  in fpu_tags.c
+             0x172  in fpu_tags.c
+             0x180  in reg_convert.c
        0x2nn  in an *.S file:
               0x201  in reg_u_add.S
               0x202  in reg_u_div.S
@@ -292,7 +289,7 @@ static struct {
              0x242  in div_Xsig.S
  */
 
-void exception(int n)
+void FPU_exception(int n)
 {
   int i, int_type;
 
@@ -347,11 +344,11 @@ void exception(int n)
       if ( n == EX_INTERNAL )
        {
          printk("FPU emulator: Internal error type 0x%04x\n", int_type);
-         emu_printall();
+         FPU_printall();
        }
 #ifdef PRINT_MESSAGES
       else
-       emu_printall();
+       FPU_printall();
 #endif /* PRINT_MESSAGES */
 
       /*
@@ -363,30 +360,103 @@ void exception(int n)
   RE_ENTRANT_CHECK_ON;
 
 #ifdef __DEBUG__
-  math_abort(FPU_info,SIGFPE);
+  math_abort(SIGFPE);
 #endif /* __DEBUG__ */
 
 }
 
 
-/* Real operation attempted on two operands, one a NaN. */
-/* Returns nz if the exception is unmasked */
-asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest)
+/* Real operation attempted on a NaN. */
+/* Returns < 0 if the exception is unmasked */
+int real_1op_NaN(FPU_REG *a)
 {
-  FPU_REG const *x;
-  int signalling;
+  int signalling, isNaN;
+
+  isNaN = (exponent(a) == EXP_OVER) && (a->sigh & 0x80000000);
 
   /* The default result for the case of two "equal" NaNs (signs may
      differ) is chosen to reproduce 80486 behaviour */
-  x = a;
-  if (a->tag == TW_NaN)
+  signalling = isNaN && !(a->sigh & 0x40000000);
+
+  if ( !signalling )
+    {
+      if ( !isNaN )  /* pseudo-NaN, or other unsupported? */
+       {
+         if ( control_word & CW_Invalid )
+           {
+             /* Masked response */
+             reg_copy(&CONST_QNaN, a);
+           }
+         EXCEPTION(EX_Invalid);
+         return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
+       }
+      return TAG_Special;
+    }
+
+  if ( control_word & CW_Invalid )
+    {
+      /* The masked response */
+      if ( !(a->sigh & 0x80000000) )  /* pseudo-NaN ? */
+       {
+         reg_copy(&CONST_QNaN, a);
+       }
+      /* ensure a Quiet NaN */
+      a->sigh |= 0x40000000;
+    }
+
+  EXCEPTION(EX_Invalid);
+
+  return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
+}
+
+
+/* Real operation attempted on two operands, one a NaN. */
+/* Returns < 0 if the exception is unmasked */
+int real_2op_NaN(FPU_REG const *b, u_char tagb,
+                int deststnr,
+                FPU_REG const *defaultNaN)
+{
+  FPU_REG *dest = &st(deststnr);
+  FPU_REG const *a = dest;
+  u_char taga = FPU_gettagi(deststnr);
+  FPU_REG const *x;
+  int signalling, unsupported;
+
+  if ( taga == TAG_Special )
+    taga = FPU_Special(a);
+  if ( tagb == TAG_Special )
+    tagb = FPU_Special(b);
+
+  /* TW_NaN is also used for unsupported data types. */
+  unsupported = ((taga == TW_NaN)
+                && !((exponent(a) == EXP_OVER) && (a->sigh & 0x80000000)))
+    || ((tagb == TW_NaN)
+       && !((exponent(b) == EXP_OVER) && (b->sigh & 0x80000000)));
+  if ( unsupported )
+    {
+      if ( control_word & CW_Invalid )
+       {
+         /* Masked response */
+         FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr);
+       }
+      EXCEPTION(EX_Invalid);
+      return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
+    }
+
+  if (taga == TW_NaN)
     {
-      if (b->tag == TW_NaN)
+      x = a;
+      if (tagb == TW_NaN)
        {
          signalling = !(a->sigh & b->sigh & 0x40000000);
-         /* find the "larger" */
-         if ( significand(a) < significand(b) )
+         if ( significand(b) > significand(a) )
            x = b;
+         else if ( significand(b) == significand(a) )
+           {
+             /* The default result for the case of two "equal" NaNs (signs may
+                differ) is chosen to reproduce 80486 behaviour */
+             x = defaultNaN;
+           }
        }
       else
        {
@@ -396,7 +466,7 @@ asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest)
     }
   else
 #ifdef PARANOID
-    if (b->tag == TW_NaN)
+    if (tagb == TW_NaN)
 #endif /* PARANOID */
     {
       signalling = !(b->sigh & 0x40000000);
@@ -411,33 +481,32 @@ asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest)
     }
 #endif /* PARANOID */
 
-  if ( !signalling )
+  if ( (!signalling) || (control_word & CW_Invalid) )
     {
-      if ( !(x->sigh & 0x80000000) )  /* pseudo-NaN ? */
-       x = &CONST_QNaN;
-      reg_move(x, dest);
-      return 0;
-    }
+      if ( ! x )
+       x = b;
 
-  if ( control_word & CW_Invalid )
-    {
-      /* The masked response */
       if ( !(x->sigh & 0x80000000) )  /* pseudo-NaN ? */
        x = &CONST_QNaN;
-      reg_move(x, dest);
+
+      FPU_copy_to_regi(x, TAG_Special, deststnr);
+
+      if ( !signalling )
+       return TAG_Special;
+
       /* ensure a Quiet NaN */
       dest->sigh |= 0x40000000;
     }
 
   EXCEPTION(EX_Invalid);
-  
-  return !(control_word & CW_Invalid);
+
+  return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
 }
 
 
 /* Invalid arith operation on Valid registers */
-/* Returns nz if the exception is unmasked */
-asmlinkage int arith_invalid(FPU_REG *dest)
+/* Returns < 0 if the exception is unmasked */
+asmlinkage int arith_invalid(int deststnr)
 {
 
   EXCEPTION(EX_Invalid);
@@ -445,28 +514,31 @@ asmlinkage int arith_invalid(FPU_REG *dest)
   if ( control_word & CW_Invalid )
     {
       /* The masked response */
-      reg_move(&CONST_QNaN, dest);
+      FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr);
     }
   
-  return !(control_word & CW_Invalid);
+  return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Valid;
 
 }
 
 
 /* Divide a finite number by zero */
-asmlinkage int divide_by_zero(int sign, FPU_REG *dest)
+asmlinkage int FPU_divide_by_zero(int deststnr, u_char sign)
 {
+  FPU_REG *dest = &st(deststnr);
+  int tag = TAG_Valid;
 
   if ( control_word & CW_ZeroDiv )
     {
       /* The masked response */
-      reg_move(&CONST_INF, dest);
-      dest->sign = (unsigned char)sign;
+      FPU_copy_to_regi(&CONST_INF, TAG_Special, deststnr);
+      setsign(dest, sign);
+      tag = TAG_Special;
     }
  
   EXCEPTION(EX_ZeroDiv);
 
-  return !(control_word & CW_ZeroDiv);
+  return (!(control_word & CW_ZeroDiv) ? FPU_Exception : 0) | tag;
 
 }
 
@@ -482,7 +554,7 @@ int set_precision_flag(int flags)
     }
   else
     {
-      exception(flags);
+      EXCEPTION(flags);
       return 1;
     }
 }
@@ -494,8 +566,7 @@ asmlinkage void set_precision_flag_up(void)
   if ( control_word & CW_Precision )
     partial_status |= (SW_Precision | SW_C1);   /* The masked response */
   else
-    exception(EX_Precision | SW_C1);
-
+    EXCEPTION(EX_Precision | SW_C1);
 }
 
 
@@ -508,7 +579,7 @@ asmlinkage void set_precision_flag_down(void)
       partial_status |= SW_Precision;
     }
   else
-    exception(EX_Precision);
+    EXCEPTION(EX_Precision);
 }
 
 
@@ -517,32 +588,69 @@ asmlinkage int denormal_operand(void)
   if ( control_word & CW_Denormal )
     {   /* The masked response */
       partial_status |= SW_Denorm_Op;
-      return 0;
+      return TAG_Special;
     }
   else
     {
-      exception(EX_Denormal);
-      return 1;
+      EXCEPTION(EX_Denormal);
+      return TAG_Special | FPU_Exception;
     }
 }
 
 
-asmlinkage int arith_overflow(FPU_REG *dest)
+asmlinkage int arith_overflow(FPU_REG *dest, int negative)
 {
+  int tag = TAG_Valid;
+  int big_real = 0;
 
   if ( control_word & CW_Overflow )
     {
-      char sign;
       /* The masked response */
-/* ###### The response here depends upon the rounding mode */
-      sign = dest->sign;
-      reg_move(&CONST_INF, dest);
-      dest->sign = sign;
+      /* The response here depends upon the rounding mode */
+      switch ( control_word & CW_RC )
+       {
+       case RC_UP:
+         big_real = negative;
+         break;
+       case RC_DOWN:
+         big_real = ! negative;
+         break;
+       case RC_CHOP:
+         big_real = 1;
+         break;
+       default:
+       }
+      if ( big_real )
+       {
+         switch ( control_word & CW_PC )
+           {
+           case PR_24_BITS:
+             significand(dest) = 0xffffff0000000000LL;
+             exponent16(dest) = 0x7ffe;
+             break;
+           case PR_53_BITS:
+             significand(dest) = 0xfffffffffffff800LL;
+             exponent16(dest) = 0x7ffe;
+             break;
+           case PR_64_BITS:
+             significand(dest) = 0xffffffffffffffffLL;
+             exponent16(dest) = 0x7ffe;
+             break;
+           }
+         EXCEPTION(EX_Overflow);
+         EXCEPTION(EX_Precision);
+         return tag;
+       }
+      else
+       {
+         reg_copy(&CONST_INF, dest);
+         tag = TAG_Special;
+       }
     }
   else
     {
       /* Subtract the magic number from the exponent */
-      dest->exp -= (3 * (1 << 13));
+      addexponent(dest, (-3 * (1 << 13)));
     }
 
   EXCEPTION(EX_Overflow);
@@ -553,30 +661,36 @@ asmlinkage int arith_overflow(FPU_REG *dest)
         The roundup bit (C1) is also set because we have
         "rounded" upwards to Infinity. */
       EXCEPTION(EX_Precision | SW_C1);
-      return !(control_word & CW_Precision);
+      return tag;
     }
 
-  return 0;
+  return tag;
 
 }
 
 
 asmlinkage int arith_underflow(FPU_REG *dest)
 {
+  int tag = TAG_Valid;
 
   if ( control_word & CW_Underflow )
     {
       /* The masked response */
-      if ( dest->exp <= EXP_UNDER - 63 )
+      if ( exponent16(dest) <= EXP_UNDER - 63 )
        {
-         reg_move(&CONST_Z, dest);
+         reg_copy(&CONST_Z, dest);
          partial_status &= ~SW_C1;       /* Round down. */
+         tag = TAG_Zero;
+       }
+      else
+       {
+         stdexp(dest);
        }
     }
   else
     {
       /* Add the magic number to the exponent. */
-      dest->exp += (3 * (1 << 13));
+      addexponent(dest, (3 * (1 << 13)) + EXTENDED_Ebias);
     }
 
   EXCEPTION(EX_Underflow);
@@ -584,22 +698,22 @@ asmlinkage int arith_underflow(FPU_REG *dest)
     {
       /* The underflow exception is masked. */
       EXCEPTION(EX_Precision);
-      return !(control_word & CW_Precision);
+      return tag;
     }
 
-  return 0;
+  return tag;
 
 }
 
 
-void stack_overflow(void)
+void FPU_stack_overflow(void)
 {
 
  if ( control_word & CW_Invalid )
     {
       /* The masked response */
-      top--;
-      reg_move(&CONST_QNaN, &st(0));
+      FPU_top--;
+      FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
     }
 
   EXCEPTION(EX_StackOver);
@@ -609,13 +723,13 @@ void stack_overflow(void)
 }
 
 
-void stack_underflow(void)
+void FPU_stack_underflow(void)
 {
 
  if ( control_word & CW_Invalid )
     {
       /* The masked response */
-      reg_move(&CONST_QNaN, &st(0));
+      FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
     }
 
   EXCEPTION(EX_StackUnder);
@@ -625,13 +739,13 @@ void stack_underflow(void)
 }
 
 
-void stack_underflow_i(int i)
+void FPU_stack_underflow_i(int i)
 {
 
  if ( control_word & CW_Invalid )
     {
       /* The masked response */
-      reg_move(&CONST_QNaN, &(st(i)));
+      FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i);
     }
 
   EXCEPTION(EX_StackUnder);
@@ -641,14 +755,14 @@ void stack_underflow_i(int i)
 }
 
 
-void stack_underflow_pop(int i)
+void FPU_stack_underflow_pop(int i)
 {
 
  if ( control_word & CW_Invalid )
     {
       /* The masked response */
-      reg_move(&CONST_QNaN, &(st(i)));
-      pop();
+      FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i);
+      FPU_pop();
     }
 
   EXCEPTION(EX_StackUnder);
index 2b4f514f8b6088078f203d06f912b961ba829a8f..b463f21a811e6545f5cdad53a4a283237d3f9f73 100644 (file)
 
 #ifdef DEBUG
 #define        EXCEPTION(x)    { printk("exception in %s at line %d\n", \
-       __FILE__, __LINE__); exception(x); }
+       __FILE__, __LINE__); FPU_exception(x); }
 #else
-#define        EXCEPTION(x)    exception(x)
+#define        EXCEPTION(x)    FPU_exception(x)
 #endif
 
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLY__ */ 
 
 #endif /* _EXCEPTION_H_ */
index 96e6bd89ba6fece43cb78af05a18fefa302e4aad..fcad7ec0853caa7b21572b8576c14d0f084f337f 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Code to implement the FPU register/register arithmetic instructions       |
  |                                                                           |
- | Copyright (C) 1992,1993                                                   |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1997                                              |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 void fadd__()
 {
   /* fadd st,st(i) */
+  int i = FPU_rm;
   clear_C1();
-  reg_add(&st(0), &st(FPU_rm), &st(0), control_word);
+  FPU_add(&st(i), FPU_gettagi(i), 0, control_word);
 }
 
 
 void fmul__()
 {
   /* fmul st,st(i) */
+  int i = FPU_rm;
   clear_C1();
-  reg_mul(&st(0), &st(FPU_rm), &st(0), control_word);
+  FPU_mul(&st(i), FPU_gettagi(i), 0, control_word);
 }
 
 
@@ -37,7 +39,7 @@ void fsub__()
 {
   /* fsub st,st(i) */
   clear_C1();
-  reg_sub(&st(0), &st(FPU_rm), &st(0), control_word);
+  FPU_sub(0, FPU_rm, control_word);
 }
 
 
@@ -45,7 +47,7 @@ void fsubr_()
 {
   /* fsubr st,st(i) */
   clear_C1();
-  reg_sub(&st(FPU_rm), &st(0), &st(0), control_word);
+  FPU_sub(REV, FPU_rm, control_word);
 }
 
 
@@ -53,7 +55,7 @@ void fdiv__()
 {
   /* fdiv st,st(i) */
   clear_C1();
-  reg_div(&st(0), &st(FPU_rm), &st(0), control_word);
+  FPU_div(0, FPU_rm, control_word);
 }
 
 
@@ -61,7 +63,7 @@ void fdivr_()
 {
   /* fdivr st,st(i) */
   clear_C1();
-  reg_div(&st(FPU_rm), &st(0), &st(0), control_word);
+  FPU_div(REV, FPU_rm, control_word);
 }
 
 
@@ -69,8 +71,9 @@ void fdivr_()
 void fadd_i()
 {
   /* fadd st(i),st */
+  int i = FPU_rm;
   clear_C1();
-  reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
+  FPU_add(&st(i), FPU_gettagi(i), i, control_word);
 }
 
 
@@ -78,27 +81,23 @@ void fmul_i()
 {
   /* fmul st(i),st */
   clear_C1();
-  reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
+  FPU_mul(&st(0), FPU_gettag0(), FPU_rm, control_word);
 }
 
 
 void fsubri()
 {
   /* fsubr st(i),st */
-  /* This is the sense of the 80486 manual
-     reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */
   clear_C1();
-  reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
+  FPU_sub(DEST_RM, FPU_rm, control_word);
 }
 
 
 void fsub_i()
 {
   /* fsub st(i),st */
-  /* This is the sense of the 80486 manual
-     reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */
   clear_C1();
-  reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word);
+  FPU_sub(REV|DEST_RM, FPU_rm, control_word);
 }
 
 
@@ -106,7 +105,7 @@ void fdivri()
 {
   /* fdivr st(i),st */
   clear_C1();
-  reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
+  FPU_div(DEST_RM, FPU_rm, control_word);
 }
 
 
@@ -114,7 +113,7 @@ void fdiv_i()
 {
   /* fdiv st(i),st */
   clear_C1();
-  reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word);
+  FPU_div(REV|DEST_RM, FPU_rm, control_word);
 }
 
 
@@ -122,9 +121,10 @@ void fdiv_i()
 void faddp_()
 {
   /* faddp st(i),st */
+  int i = FPU_rm;
   clear_C1();
-  if ( !reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
-    pop();
+  if ( FPU_add(&st(i), FPU_gettagi(i), i, control_word) >= 0 )
+    FPU_pop();
 }
 
 
@@ -132,8 +132,8 @@ void fmulp_()
 {
   /* fmulp st(i),st */
   clear_C1();
-  if ( !reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
-    pop();
+  if ( FPU_mul(&st(0), FPU_gettag0(), FPU_rm, control_word) >= 0 )
+    FPU_pop();
 }
 
 
@@ -141,22 +141,18 @@ void fmulp_()
 void fsubrp()
 {
   /* fsubrp st(i),st */
-  /* This is the sense of the 80486 manual
-     reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */
   clear_C1();
-  if ( !reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
-    pop();
+  if ( FPU_sub(DEST_RM, FPU_rm, control_word) >= 0 )
+    FPU_pop();
 }
 
 
 void fsubp_()
 {
   /* fsubp st(i),st */
-  /* This is the sense of the 80486 manual
-     reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */
   clear_C1();
-  if ( !reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) )
-    pop();
+  if ( FPU_sub(REV|DEST_RM, FPU_rm, control_word) >= 0 )
+    FPU_pop();
 }
 
 
@@ -164,8 +160,8 @@ void fdivrp()
 {
   /* fdivrp st(i),st */
   clear_C1();
-  if ( !reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
-    pop();
+  if ( FPU_div(DEST_RM, FPU_rm, control_word) >= 0 )
+    FPU_pop();
 }
 
 
@@ -173,7 +169,6 @@ void fdivp_()
 {
   /* fdivp st(i),st */
   clear_C1();
-  if ( !reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) )
-    pop();
+  if ( FPU_div(REV|DEST_RM, FPU_rm, control_word) >= 0 )
+    FPU_pop();
 }
-
index f16afc871775fe3952d8177e5d85c02860863f06..5665f0923c6bd5778d9bfbe698a457c6118ffe4b 100644 (file)
@@ -1,9 +1,9 @@
 /*---------------------------------------------------------------------------+
  |  fpu_asm.h                                                                |
  |                                                                           |
- | Copyright (C) 1992,1995                                                   |
+ | Copyright (C) 1992,1995,1997                                              |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@jacobi.maths.monash.edu.au |
+ |                       Australia.  E-mail billm@suburbia.net               |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
 
 #include <linux/linkage.h>
 
-#define        EXCEPTION       SYMBOL_NAME(exception)
+#define        EXCEPTION       SYMBOL_NAME(FPU_exception)
 
 
 #define PARAM1 8(%ebp)
 #define        PARAM2  12(%ebp)
 #define        PARAM3  16(%ebp)
 #define        PARAM4  20(%ebp)
+#define        PARAM5  24(%ebp)
+#define        PARAM6  28(%ebp)
+#define        PARAM7  32(%ebp)
 
-#define SIGL_OFFSET 8
-#define SIGN(x)        (x)
-#define        TAG(x)  1(x)
-#define        EXP(x)  4(x)
+#define SIGL_OFFSET 0
+#define        EXP(x)  8(x)
 #define SIG(x) SIGL_OFFSET##(x)
 #define        SIGL(x) SIGL_OFFSET##(x)
-#define        SIGH(x) 12(x)
+#define        SIGH(x) 4(x)
 
 #endif /* _FPU_ASM_H_ */
index 0d35fe19b706227228f23f849f38a2b7dab46e35..15df8b7724ca87febed655738ec7f1df933491f1 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Code to implement some of the FPU auxiliary instructions.                 |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1994,1997,2001                                    |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@melbpc.org.au                             |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -32,15 +32,11 @@ void fclex(void)
 /* Needs to be externally visible */
 void finit()
 {
-  int r;
   control_word = 0x037f;
   partial_status = 0;
-  top = 0;            /* We don't keep top in the status word internally. */
-  for (r = 0; r < 8; r++)
-    {
-      regs[r].tag = TW_Empty;
-    }
-  /* The behaviour is different to that detailed in
+  FPU_top = 0;            /* We don't keep top in the status word internally. */
+  fpu_tag_word = 0xffff;
+  /* The behaviour is different from that detailed in
      Section 15.1.6 of the Intel manual */
   operand_address.offset = 0;
   operand_address.selector = 0;
@@ -99,19 +95,27 @@ void fp_nop()
 void fld_i_()
 {
   FPU_REG *st_new_ptr;
+  int i;
+  u_char tag;
 
   if ( STACK_OVERFLOW )
-    { stack_overflow(); return; }
+    { FPU_stack_overflow(); return; }
 
   /* fld st(i) */
-  if ( NOT_EMPTY(FPU_rm) )
-    { reg_move(&st(FPU_rm), st_new_ptr); push(); }
+  i = FPU_rm;
+  if ( NOT_EMPTY(i) )
+    {
+      reg_copy(&st(i), st_new_ptr);
+      tag = FPU_gettagi(i);
+      push();
+      FPU_settag0(tag);
+    }
   else
     {
       if ( control_word & CW_Invalid )
        {
          /* The masked response */
-         stack_underflow();
+         FPU_stack_underflow();
        }
       else
        EXCEPTION(EX_StackUnder);
@@ -124,61 +128,155 @@ void fxch_i()
 {
   /* fxch st(i) */
   FPU_REG t;
-  register FPU_REG *sti_ptr = &st(FPU_rm), *st0_ptr = &st(0);
-
-  if ( st0_ptr->tag == TW_Empty )
+  int i = FPU_rm;
+  FPU_REG *st0_ptr = &st(0), *sti_ptr = &st(i);
+  long tag_word = fpu_tag_word;
+  int regnr = FPU_top & 7, regnri = ((regnr + i) & 7);
+  u_char st0_tag = (tag_word >> (regnr*2)) & 3;
+  u_char sti_tag = (tag_word >> (regnri*2)) & 3;
+
+  if ( st0_tag == TAG_Empty )
     {
-      if ( sti_ptr->tag == TW_Empty )
+      if ( sti_tag == TAG_Empty )
        {
-         stack_underflow();
-         stack_underflow_i(FPU_rm);
+         FPU_stack_underflow();
+         FPU_stack_underflow_i(i);
          return;
        }
       if ( control_word & CW_Invalid )
-       reg_move(sti_ptr, st0_ptr);   /* Masked response */
-      stack_underflow_i(FPU_rm);
+       {
+         /* Masked response */
+         FPU_copy_to_reg0(sti_ptr, sti_tag);
+       }
+      FPU_stack_underflow_i(i);
       return;
     }
-  if ( sti_ptr->tag == TW_Empty )
+  if ( sti_tag == TAG_Empty )
     {
       if ( control_word & CW_Invalid )
-       reg_move(st0_ptr, sti_ptr);   /* Masked response */
-      stack_underflow();
+       {
+         /* Masked response */
+         FPU_copy_to_regi(st0_ptr, st0_tag, i);
+       }
+      FPU_stack_underflow();
       return;
     }
   clear_C1();
-  reg_move(st0_ptr, &t);
-  reg_move(sti_ptr, st0_ptr);
-  reg_move(&t, sti_ptr);
+
+  reg_copy(st0_ptr, &t);
+  reg_copy(sti_ptr, st0_ptr);
+  reg_copy(&t, sti_ptr);
+
+  tag_word &= ~(3 << (regnr*2)) & ~(3 << (regnri*2));
+  tag_word |= (sti_tag << (regnr*2)) | (st0_tag << (regnri*2));
+  fpu_tag_word = tag_word;
 }
 
 
 void ffree_()
 {
   /* ffree st(i) */
-  st(FPU_rm).tag = TW_Empty;
+  FPU_settagi(FPU_rm, TAG_Empty);
 }
 
 
 void ffreep()
 {
   /* ffree st(i) + pop - unofficial code */
-  st(FPU_rm).tag = TW_Empty;
-  pop();
+  FPU_settagi(FPU_rm, TAG_Empty);
+  FPU_pop();
 }
 
 
 void fst_i_()
 {
   /* fst st(i) */
-  reg_move(&st(0), &st(FPU_rm));
+  FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm);
 }
 
 
 void fstp_i()
 {
   /* fstp st(i) */
-  reg_move(&st(0), &st(FPU_rm));
-  pop();
+  FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm);
+  FPU_pop();
+}
+
+
+#ifndef ONLY_486_FPU
+static void fmov_i(void)
+{
+  int i;
+  u_char tag;
+
+  /* for the fcmovxx instructions */
+  i = FPU_rm;
+  if ( NOT_EMPTY(i) )
+    {
+      reg_copy(&st(i), &st(0));
+      tag = FPU_gettagi(i);
+      FPU_settag0(tag);
+    }
+  else
+    {
+      if ( control_word & CW_Invalid )
+       {
+         /* The masked response */
+         FPU_stack_underflow();
+       }
+      else
+       EXCEPTION(EX_StackUnder);
+    }
+
+}
+
+
+void fcmovnb(void)
+{
+  if ( ! (FPU_EFLAGS & CFLAG) )
+    fmov_i();
+}
+
+void fcmovne(void)
+{
+  if ( ! (FPU_EFLAGS & ZFLAG) )
+    fmov_i();
+}
+
+void fcmovnbe(void)
+{
+  if ( ! (FPU_EFLAGS & (CFLAG | ZFLAG)) )
+    fmov_i();
+}
+
+void fcmovnu(void)
+{
+  if ( ! (FPU_EFLAGS & PFLAG) )
+    fmov_i();
+}
+
+void fcmovb (void)
+{
+  if ( (FPU_EFLAGS & CFLAG) )
+    fmov_i();
+}
+
+void fcmove (void)
+{
+  if ( (FPU_EFLAGS & ZFLAG) )
+    fmov_i();
+}
+
+void fcmovbe(void)
+{
+  if ( (FPU_EFLAGS & (CFLAG | ZFLAG)) )
+    fmov_i();
+}
+
+void fcmovu (void)
+{
+  if ( (FPU_EFLAGS & PFLAG) )
+    fmov_i();
 }
 
+#endif /* ONLY_486_FPU */
diff --git a/arch/i386/math-emu/fpu_debug.c b/arch/i386/math-emu/fpu_debug.c
deleted file mode 100644 (file)
index 537e7b6..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-/* Interface with ptrace and core-dumping routines */
-
-
-#include "fpu_system.h"
-#include "exception.h"
-#include "reg_constant.h"
-#include "fpu_emu.h"
-#include "control_w.h"
-#include "status_w.h"
-
-
-#define EXTENDED_Ebias 0x3fff
-#define EXTENDED_Emin (-0x3ffe)  /* smallest valid exponent */
-
-#define DOUBLE_Emax 1023         /* largest valid exponent */
-#define DOUBLE_Ebias 1023
-#define DOUBLE_Emin (-1022)      /* smallest valid exponent */
-
-#define SINGLE_Emax 127          /* largest valid exponent */
-#define SINGLE_Ebias 127
-#define SINGLE_Emin (-126)       /* smallest valid exponent */
-
-
-/* Copy and paste from round_to_int. Original comments maintained */
-/*===========================================================================*/
-
-/* r gets mangled such that sig is int, sign: 
-   it is NOT normalized */
-/* The return value (in eax) is zero if the result is exact,
-   if bits are changed due to rounding, truncation, etc, then
-   a non-zero value is returned */
-/* Overflow is signalled by a non-zero return value (in eax).
-   In the case of overflow, the returned significand always has the
-   largest possible value */
-
-static int round_to_int_cwd(FPU_REG *r, long int user_control_word)
-{
-  char     very_big;
-  unsigned eax;
-
-  if (r->tag == TW_Zero)
-    {
-      /* Make sure that zero is returned */
-      significand(r) = 0;
-      return 0;        /* o.k. */
-    }
-  
-  if (r->exp > EXP_BIAS + 63)
-    {
-      r->sigl = r->sigh = ~0;      /* The largest representable number */
-      return 1;        /* overflow */
-    }
-
-  eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
-  very_big = !(~(r->sigh) | ~(r->sigl));  /* test for 0xfff...fff */
-#define        half_or_more    (eax & 0x80000000)
-#define        frac_part       (eax)
-#define more_than_half  ((eax & 0x80000001) == 0x80000001)
-  switch (user_control_word & CW_RC)
-    {
-    case RC_RND:
-      if ( more_than_half                      /* nearest */
-         || (half_or_more && (r->sigl & 1)) )  /* odd -> even */
-       {
-         if ( very_big ) return 1;        /* overflow */
-         significand(r) ++;
-         return PRECISION_LOST_UP;
-       }
-      break;
-    case RC_DOWN:
-      if (frac_part && r->sign)
-       {
-         if ( very_big ) return 1;        /* overflow */
-         significand(r) ++;
-         return PRECISION_LOST_UP;
-       }
-      break;
-    case RC_UP:
-      if (frac_part && !r->sign)
-       {
-         if ( very_big ) return 1;        /* overflow */
-         significand(r) ++;
-         return PRECISION_LOST_UP;
-       }
-      break;
-    case RC_CHOP:
-      break;
-    }
-
-  return eax ? PRECISION_LOST_DOWN : 0;
-
-}
-
-
-
-/* Conver a number in the emulator format to the
- * hardware format.
- * Taken from the emulator sources, function reg_load_extended
- */
-
-/* Get a long double from the debugger */
-void hardreg_to_softreg(const char hardreg[10],
-                             FPU_REG *soft_reg)
-
-{
-       unsigned long sigl, sigh, exp;
-               
-       sigl = *((unsigned long *) hardreg);
-       sigh = *(1 + (unsigned long *) hardreg);
-       exp = *(4 + (unsigned short *) hardreg);
-       
-       soft_reg->tag = TW_Valid;   /* Default */
-       soft_reg->sigl = sigl;
-       soft_reg->sigh = sigh;
-       if (exp & 0x8000)
-               soft_reg->sign = SIGN_NEG;
-       else
-               soft_reg->sign = SIGN_POS;
-       exp &= 0x7fff;
-       soft_reg->exp = exp - EXTENDED_Ebias + EXP_BIAS;
-       
-       if ( exp == 0 )
-       {
-               if ( !(sigh | sigl) )
-               {
-                       soft_reg->tag = TW_Zero;
-                       return;
-               }
-               /* The number is a de-normal or pseudodenormal. */
-               if (sigh & 0x80000000)
-               {
-                       /* Is a pseudodenormal. */
-                       /* Convert it for internal use. */
-                       /* This is non-80486 behaviour because the number
-                          loses its 'denormal' identity. */
-                       soft_reg->exp++;
-                       return;
-               }
-               else
-               {
-                       /* Is a denormal. */
-                       /* Convert it for internal use. */
-                       soft_reg->exp++;
-                       normalize_nuo(soft_reg);
-                       return;
-               }
-       }
-       else if ( exp == 0x7fff )
-       {
-               if ( !((sigh ^ 0x80000000) | sigl) )
-               {
-                       /* Matches the bit pattern for Infinity. */
-                       soft_reg->exp = EXP_Infinity;
-                       soft_reg->tag = TW_Infinity;
-                       return;
-               }
-               
-               soft_reg->exp = EXP_NaN;
-               soft_reg->tag = TW_NaN;
-               if ( !(sigh & 0x80000000) )
-               {
-                       /* NaNs have the ms bit set to 1. */
-                       /* This is therefore an Unsupported NaN data type. */
-                       /* This is non 80486 behaviour */
-                       /* This should generate an Invalid Operand exception
-                          later, so we convert it to a SNaN */
-                       soft_reg->sigh = 0x80000000;
-                       soft_reg->sigl = 0x00000001;
-                       soft_reg->sign = SIGN_NEG;
-                       return;
-               }
-               return;
-       }
-       
-       if ( !(sigh & 0x80000000) )
-       {
-               /* Unsupported data type. */
-               /* Valid numbers have the ms bit set to 1. */
-               /* Unnormal. */
-               /* Convert it for internal use. */
-               /* This is non-80486 behaviour */
-               /* This should generate an Invalid Operand exception
-                  later, so we convert it to a SNaN */
-               soft_reg->sigh = 0x80000000;
-               soft_reg->sigl = 0x00000001;
-               soft_reg->sign = SIGN_NEG;
-               soft_reg->exp = EXP_NaN;
-               soft_reg->tag = TW_NaN;
-               return;
-       }
-       return;
-}
-
-/* Conver a number in the emulator format to the
- * hardware format.
- * Adapted from function write_to_extended
- */
-
-
-void softreg_to_hardreg(const FPU_REG *rp, char d[10], long int user_control_word)
-{
-       long e;
-       FPU_REG tmp;
-       e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
-
-       /*
-         All numbers except denormals are stored internally in a
-         format which is compatible with the extended real number
-         format.
-         */
-       if (e > 0) {
-               *(unsigned long *) d = rp->sigl;
-               *(unsigned long *) (d + 4) = rp->sigh;
-       } else {
-               /*
-                 The number is a de-normal stored as a normal using our
-                 extra exponent range, or is Zero.
-                 Convert it back to a de-normal, or leave it as Zero.
-                 */
-               reg_move(rp, &tmp);
-               tmp.exp += -EXTENDED_Emin + 63;  /* largest exp to be 63 */
-               round_to_int_cwd(&tmp, user_control_word);
-               e = 0;
-               *(unsigned long *) d= tmp.sigl;
-               *(unsigned long *) (d + 4) = tmp.sigh;
-       }
-       e |= rp->sign == SIGN_POS ? 0 : 0x8000;
-       *(unsigned short *) (d + 8) = e;
-}
-
index 678f87c751b5ff5ee15fb99519052b1db894e4f6..1241e122245d61d1e8ca0d31a3f06532e29ab876 100644 (file)
@@ -1,9 +1,9 @@
 /*---------------------------------------------------------------------------+
  |  fpu_emu.h                                                                |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
+ | Copyright (C) 1992,1993,1994,1997,2001                                    |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
 #ifndef _FPU_EMU_H_
 #define _FPU_EMU_H_
 
-/*
- * Define DENORM_OPERAND to make the emulator detect denormals
- * and use the denormal flag of the status word. Note: this only
- * affects the flag and corresponding interrupt, the emulator
- * will always generate denormals and operate upon them as required.
- */
-#define DENORM_OPERAND
-
 /*
  * Define PECULIAR_486 to get a closer approximation to 80486 behaviour,
  * rather than behaviour which appears to be cleaner.
 #define EXP_BIAS       Const(0)
 #define EXP_OVER       Const(0x4000)    /* smallest invalid large exponent */
 #define        EXP_UNDER       Const(-0x3fff)   /* largest invalid small exponent */
+#define EXP_WAY_UNDER   Const(-0x6000)   /* Below the smallest denormal, but
+                                           still a 16 bit nr. */
 #define EXP_Infinity    EXP_OVER
 #define EXP_NaN         EXP_OVER
 
+#define EXTENDED_Ebias Const(0x3fff)
+#define EXTENDED_Emin (-0x3ffe)  /* smallest valid exponent */
+
 #define SIGN_POS       Const(0)
-#define SIGN_NEG       Const(1)
+#define SIGN_NEG       Const(0x80)
 
-/* Keep the order TW_Valid, TW_Zero, TW_Denormal */
-#define TW_Valid       Const(0)        /* valid */
-#define TW_Zero                Const(1)        /* zero */
+#define SIGN_Positive  Const(0)
+#define SIGN_Negative  Const(0x8000)
+
+
+/* Keep the order TAG_Valid, TAG_Zero, TW_Denormal */
 /* The following fold to 2 (Special) in the Tag Word */
-/* #define TW_Denormal     Const(4) */       /* De-normal */
+#define TW_Denormal     Const(4)        /* De-normal */
 #define TW_Infinity    Const(5)        /* + or - infinity */
 #define        TW_NaN          Const(6)        /* Not a Number */
+#define        TW_Unsupported  Const(7)        /* Not supported by an 80486 */
+
+#define TAG_Valid      Const(0)        /* valid */
+#define TAG_Zero       Const(1)        /* zero */
+#define TAG_Special    Const(2)        /* De-normal, + or - infinity,
+                                          or Not a Number */
+#define TAG_Empty      Const(3)        /* empty */
+
+#define LOADED_DATA    Const(10101)    /* Special st() number to identify
+                                          loaded data (not on stack). */
 
-#define TW_Empty       Const(7)        /* empty */
+/* A few flags (must be >= 0x10). */
+#define REV             0x10
+#define DEST_RM         0x20
+#define LOADED          0x40
+
+#define FPU_Exception   Const(0x80000000)   /* Added to tag returns. */
 
 
 #ifndef __ASSEMBLY__
 
-#include <asm/sigcontext.h>    /* for struct _fpstate */
-#include <asm/math_emu.h>
+#include "fpu_system.h"
 
+#include <asm/sigcontext.h>   /* for struct _fpstate */
+#include <asm/math_emu.h>
 #include <linux/linkage.h>
 
 /*
@@ -67,7 +82,7 @@
  */
 
 #ifdef RE_ENTRANT_CHECKING
-extern char emulating;
+extern u_char emulating;
 #  define RE_ENTRANT_CHECK_OFF emulating = 0
 #  define RE_ENTRANT_CHECK_ON emulating = 1
 #else
@@ -97,18 +112,24 @@ extern char emulating;
 
 struct address {
   unsigned int offset;
-  unsigned short selector;
-  unsigned short opcode:11,
-                empty:5;
+  unsigned int selector:16;
+  unsigned int opcode:11;
+  unsigned int empty:5;
+};
+struct fpu__reg {
+  unsigned sigl;
+  unsigned sigh;
+  short exp;
 };
+
 typedef void (*FUNC)(void);
-typedef struct fpu_reg FPU_REG;
-typedef void (*FUNC_ST0)(FPU_REG *st0_ptr);
-typedef struct { unsigned char address_size, operand_size, segment; }
+typedef struct fpu__reg FPU_REG;
+typedef void (*FUNC_ST0)(FPU_REG *st0_ptr, u_char st0_tag);
+typedef struct { u_char address_size, operand_size, segment; }
         overrides;
 /* This structure is 32 bits: */
 typedef struct { overrides override;
-                unsigned char default_mode; } fpu_addr_modes;
+                u_char default_mode; } fpu_addr_modes;
 /* PROTECTED has a restricted meaning in the emulator; it is used
    to signal that the emulator needs to do special things to ensure
    that protection is respected in a segmented model. */
@@ -117,27 +138,48 @@ typedef struct { overrides override;
 #define VM86      SIXTEEN
 #define PM16      (SIXTEEN | PROTECTED)
 #define SEG32     PROTECTED
-extern unsigned char const data_sizes_16[32];
-
-#define        st(x)   ( regs[((top+x) &7 )] )
+extern u_char const data_sizes_16[32];
 
-#define        STACK_OVERFLOW  (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty)
-#define        NOT_EMPTY(i)    (st(i).tag != TW_Empty)
-#define        NOT_EMPTY_ST0   (st0_tag ^ TW_Empty)
+#define register_base ((u_char *) registers )
+#define fpu_register(x)  ( * ((FPU_REG *)( register_base + 10 * (x & 7) )) )
+#define        st(x)      ( * ((FPU_REG *)( register_base + 10 * ((FPU_top+x) & 7) )) )
 
-#define pop()  { regs[(top++ & 7 )].tag = TW_Empty; }
-#define poppop() { regs[((top + 1) & 7 )].tag \
-                    = regs[(top & 7 )].tag = TW_Empty; \
-                  top += 2; }
+#define        STACK_OVERFLOW  (FPU_stackoverflow(&st_new_ptr))
+#define        NOT_EMPTY(i)    (!FPU_empty_i(i))
 
-/* push() does not affect the tags */
-#define push() { top--; }
+#define        NOT_EMPTY_ST0   (st0_tag ^ TAG_Empty)
 
+#define poppop() { FPU_pop(); FPU_pop(); }
 
-#define reg_move(x, y) { \
-                *(short *)&((y)->sign) = *(const short *)&((x)->sign); \
-                *(long *)&((y)->exp) = *(const long *)&((x)->exp); \
-                *(long long *)&((y)->sigl) = *(const long long *)&((x)->sigl); }
+/* push() does not affect the tags */
+#define push() { FPU_top--; }
+
+#define signbyte(a) (((u_char *)(a))[9])
+#define getsign(a) (signbyte(a) & 0x80)
+#define setsign(a,b) { if (b) signbyte(a) |= 0x80; else signbyte(a) &= 0x7f; }
+#define copysign(a,b) { if (getsign(a)) signbyte(b) |= 0x80; \
+                        else signbyte(b) &= 0x7f; }
+#define changesign(a) { signbyte(a) ^= 0x80; }
+#define setpositive(a) { signbyte(a) &= 0x7f; }
+#define setnegative(a) { signbyte(a) |= 0x80; }
+#define signpositive(a) ( (signbyte(a) & 0x80) == 0 )
+#define signnegative(a) (signbyte(a) & 0x80)
+
+static inline void reg_copy(FPU_REG const *x, FPU_REG *y)
+{
+  *(short *)&(y->exp) = *(const short *)&(x->exp); 
+  *(long long *)&(y->sigl) = *(const long long *)&(x->sigl);
+}
+
+#define exponent(x)  (((*(short *)&((x)->exp)) & 0x7fff) - EXTENDED_Ebias)
+#define setexponentpos(x,y) { (*(short *)&((x)->exp)) = \
+  ((y) + EXTENDED_Ebias) & 0x7fff; }
+#define exponent16(x)         (*(short *)&((x)->exp))
+#define setexponent16(x,y)  { (*(short *)&((x)->exp)) = (y); }
+#define addexponent(x,y)    { (*(short *)&((x)->exp)) += (y); }
+#define stdexp(x)           { (*(short *)&((x)->exp)) += EXTENDED_Ebias; }
+
+#define isdenormal(ptr)   (exponent(ptr) == EXP_BIAS+EXP_UNDER)
 
 #define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] )
 
@@ -145,24 +187,26 @@ extern unsigned char const data_sizes_16[32];
 /*----- Prototypes for functions written in assembler -----*/
 /* extern void reg_move(FPU_REG *a, FPU_REG *b); */
 
-asmlinkage void normalize(FPU_REG *x);
-asmlinkage void normalize_nuo(FPU_REG *x);
-asmlinkage int reg_div(FPU_REG const *arg1, FPU_REG const *arg2,
-                      FPU_REG *answ, unsigned int control_w);
-asmlinkage int reg_u_sub(FPU_REG const *arg1, FPU_REG const *arg2,
-                        FPU_REG *answ, unsigned int control_w);
-asmlinkage int reg_u_mul(FPU_REG const *arg1, FPU_REG const *arg2,
-                        FPU_REG *answ, unsigned int control_w);
-asmlinkage int reg_u_div(FPU_REG const *arg1, FPU_REG const *arg2,
-                        FPU_REG *answ, unsigned int control_w);
-asmlinkage int reg_u_add(FPU_REG const *arg1, FPU_REG const *arg2,
-                        FPU_REG *answ, unsigned int control_w);
-asmlinkage int wm_sqrt(FPU_REG *n, unsigned int control_w);
-asmlinkage unsigned    shrx(void *l, unsigned x);
-asmlinkage unsigned    shrxs(void *v, unsigned x);
-asmlinkage unsigned long div_small(unsigned long long *x, unsigned long y);
-asmlinkage void round_reg(FPU_REG *arg, unsigned int extent,
-                     unsigned int control_w);
+asmlinkage int FPU_normalize(FPU_REG *x, int sign);
+asmlinkage int FPU_normalize_nuo(FPU_REG *x);
+asmlinkage int FPU_u_sub(FPU_REG const *arg1, FPU_REG const *arg2,
+                        FPU_REG *answ, unsigned int control_w, u_char sign,
+                        int expa, int expb);
+asmlinkage int FPU_u_mul(FPU_REG const *arg1, FPU_REG const *arg2,
+                        FPU_REG *answ, unsigned int control_w, u_char sign,
+                        int expon);
+asmlinkage int FPU_u_div(FPU_REG const *arg1, FPU_REG const *arg2,
+                        FPU_REG *answ, unsigned int control_w, u_char sign);
+asmlinkage int FPU_u_add(FPU_REG const *arg1, FPU_REG const *arg2,
+                        FPU_REG *answ, unsigned int control_w, u_char sign,
+                        int expa, int expb);
+asmlinkage int wm_sqrt(FPU_REG *n, int dummy1, int dummy2,
+                      unsigned int control_w, u_char sign);
+asmlinkage unsigned    FPU_shrx(void *l, unsigned x);
+asmlinkage unsigned    FPU_shrxs(void *v, unsigned x);
+asmlinkage unsigned long FPU_div_small(unsigned long long *x, unsigned long y);
+asmlinkage int FPU_round(FPU_REG *arg, unsigned int extent, int dummy,
+                        unsigned int control_w, u_char sign);
 
 #ifndef MAKING_PROTO
 #include "fpu_proto.h"
index f21eb0d0e202c8e8c897ea134573bfefa06ef8d8..ef88e284a8c0419da7532fa8c35b4e3e03b6f5d5 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | The entry functions for wm-FPU-emu                                        |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1996                                         |
+ | Copyright (C) 1992,1993,1994,1996,1997,2001                               |
  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
- |                  E-mail   billm@jacobi.maths.monash.edu.au                |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
  | See the files "README" and "COPYING" for further copyright and warranty   |
  | information.                                                              |
@@ -26,8 +26,6 @@
 
 #include <linux/signal.h>
 
-#include <asm/segment.h>
-
 #include "fpu_system.h"
 #include "fpu_emu.h"
 #include "exception.h"
 
 #define __BAD__ FPU_illegal   /* Illegal on an 80486, causes SIGILL */
 
+#ifdef ONLY_486_FPU
+#define fcmovnb  __BAD__   /* Not present on 80486 */
+#define fcmovne  __BAD__   /* Not present on 80486 */
+#define fcmovnbe __BAD__   /* Not present on 80486 */
+#define fcmovnu  __BAD__   /* Not present on 80486 */
+#define fcmovb   __BAD__   /* Not present on 80486 */
+#define fcmove   __BAD__   /* Not present on 80486 */
+#define fcmovbe  __BAD__   /* Not present on 80486 */
+#define fcmovu   __BAD__   /* Not present on 80486 */
+#define fucomi   __BAD__   /* Not present on 80486 */
+#define fcomi    __BAD__   /* Not present on 80486 */
+#endif
+
 #ifndef NO_UNDOC_CODE    /* Un-documented FPU op-codes supported by default. */
 
 /* WARNING: These codes are not documented by Intel in their 80486 manual
 #define _df_d8_ fstp_i    /* unofficial code (1f) */
 
 static FUNC const st_instr_table[64] = {
-  fadd__,   fld_i_,  __BAD__, __BAD__, fadd_i,  ffree_,  faddp_,  _df_c0_,
-  fmul__,   fxch_i,  __BAD__, __BAD__, fmul_i,  _dd_c8_, fmulp_,  _df_c8_,
-  fcom_st,  fp_nop,  __BAD__, __BAD__, _dc_d0_, fst_i_,  _de_d0_, _df_d0_,
-  fcompst,  _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i,  fcompp,  _df_d8_,
-  fsub__,   fp_etc,  __BAD__, finit_,  fsubri,  fucom_,  fsubrp,  fstsw_,
-  fsubr_,   fconst,  fucompp, __BAD__, fsub_i,  fucomp,  fsubp_,  __BAD__,
-  fdiv__,   trig_a,  __BAD__, __BAD__, fdivri,  __BAD__, fdivrp,  __BAD__,
-  fdivr_,   trig_b,  __BAD__, __BAD__, fdiv_i,  __BAD__, fdivp_,  __BAD__,
+  fadd__,   fld_i_,     fcmovb,  fcmovnb,  fadd_i,  ffree_,  faddp_,  _df_c0_,
+  fmul__,   fxch_i,     fcmove,  fcmovne,  fmul_i,  _dd_c8_, fmulp_,  _df_c8_,
+  fcom_st,  fp_nop,     fcmovbe, fcmovnbe, _dc_d0_, fst_i_,  _de_d0_, _df_d0_,
+  fcompst,  _d9_d8_,    fcmovu,  fcmovnu,  _dc_d8_, fstp_i,  fcompp,  _df_d8_,
+  fsub__,   FPU_etc,    __BAD__, finit_,   fsubri,  fucom_,  fsubrp,  fstsw_,
+  fsubr_,   fconst,     fucompp, fucomi,   fsub_i,  fucomp,  fsubp_,  __BAD__,
+  fdiv__,   FPU_triga,  __BAD__, fcomi,    fdivri,  __BAD__, fdivrp,  __BAD__,
+  fdivr_,   FPU_trigb,  __BAD__, __BAD__,  fdiv_i,  __BAD__, fdivp_,  __BAD__,
 };
 
 #else     /* Support only documented FPU op-codes */
 
 static FUNC const st_instr_table[64] = {
-  fadd__,   fld_i_,  __BAD__, __BAD__, fadd_i,  ffree_,  faddp_,  __BAD__,
-  fmul__,   fxch_i,  __BAD__, __BAD__, fmul_i,  __BAD__, fmulp_,  __BAD__,
-  fcom_st,  fp_nop,  __BAD__, __BAD__, __BAD__, fst_i_,  __BAD__, __BAD__,
-  fcompst,  __BAD__, __BAD__, __BAD__, __BAD__, fstp_i,  fcompp,  __BAD__,
-  fsub__,   fp_etc,  __BAD__, finit_,  fsubri,  fucom_,  fsubrp,  fstsw_,
-  fsubr_,   fconst,  fucompp, __BAD__, fsub_i,  fucomp,  fsubp_,  __BAD__,
-  fdiv__,   trig_a,  __BAD__, __BAD__, fdivri,  __BAD__, fdivrp,  __BAD__,
-  fdivr_,   trig_b,  __BAD__, __BAD__, fdiv_i,  __BAD__, fdivp_,  __BAD__,
+  fadd__,   fld_i_,     fcmovb,  fcmovnb,  fadd_i,  ffree_,  faddp_,  __BAD__,
+  fmul__,   fxch_i,     fcmove,  fcmovne,  fmul_i,  __BAD__, fmulp_,  __BAD__,
+  fcom_st,  fp_nop,     fcmovbe, fcmovnbe, __BAD__, fst_i_,  __BAD__, __BAD__,
+  fcompst,  __BAD__,    fcmovu,  fcmovnu,  __BAD__, fstp_i,  fcompp,  __BAD__,
+  fsub__,   FPU_etc,    __BAD__, finit_,   fsubri,  fucom_,  fsubrp,  fstsw_,
+  fsubr_,   fconst,     fucompp, fucomi,   fsub_i,  fucomp,  fsubp_,  __BAD__,
+  fdiv__,   FPU_triga,  __BAD__, fcomi,    fdivri,  __BAD__, fdivrp,  __BAD__,
+  fdivr_,   FPU_trigb,  __BAD__, __BAD__,  fdiv_i,  __BAD__, fdivp_,  __BAD__,
 };
 
 #endif /* NO_UNDOC_CODE */
@@ -95,27 +106,27 @@ static FUNC const st_instr_table[64] = {
 
 /* Un-documented FPU op-codes supported by default. (see above) */
 
-static unsigned char const type_table[64] = {
-  _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
-  _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
-  _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
-  _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
+static u_char const type_table[64] = {
+  _REGI_, _NONE_, _REGi_, _REGi_, _REGIi, _REGi_, _REGIp, _REGi_,
+  _REGI_, _REGIn, _REGi_, _REGi_, _REGIi, _REGI_, _REGIp, _REGI_,
+  _REGIc, _NONE_, _REGi_, _REGi_, _REGIc, _REG0_, _REGIc, _REG0_,
+  _REGIc, _REG0_, _REGi_, _REGi_, _REGIc, _REG0_, _REGIc, _REG0_,
   _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
-  _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
-  _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+  _REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _null_,
+  _REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _null_,
   _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
 };
 
 #else     /* Support only documented FPU op-codes */
 
-static unsigned char const type_table[64] = {
-  _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
-  _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
-  _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
-  _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
+static u_char const type_table[64] = {
+  _REGI_, _NONE_, _REGi_, _REGi_, _REGIi, _REGi_, _REGIp, _null_,
+  _REGI_, _REGIn, _REGi_, _REGi_, _REGIi, _null_, _REGIp, _null_,
+  _REGIc, _NONE_, _REGi_, _REGi_, _null_, _REG0_, _null_, _null_,
+  _REGIc, _null_, _REGi_, _REGi_, _null_, _REG0_, _REGIc, _null_,
   _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
-  _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
-  _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+  _REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _null_,
+  _REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _null_,
   _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
 };
 
@@ -123,26 +134,26 @@ static unsigned char const type_table[64] = {
 
 
 #ifdef RE_ENTRANT_CHECKING
-char emulating=0;
+u_char emulating=0;
 #endif /* RE_ENTRANT_CHECKING */
 
-static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip,
+static int valid_prefix(u_char *Byte, u_char **fpu_eip,
                        overrides *override);
 
 asmlinkage void math_emulate(long arg)
 {
-  unsigned char  FPU_modrm, byte1;
+  u_char  FPU_modrm, byte1;
   unsigned short code;
   fpu_addr_modes addr_modes;
   int unmasked;
   FPU_REG loaded_data;
+  FPU_REG *st0_ptr;
+  u_char         loaded_tag, st0_tag;
   void *data_address;
   struct address data_sel_off;
   struct address entry_sel_off;
   unsigned long code_base = 0;
   unsigned long code_limit = 0;  /* Initialized to stop compiler warnings */
-  char        st0_tag;
-  FPU_REG      *st0_ptr;
   struct desc_struct code_descriptor;
 
 #ifdef RE_ENTRANT_CHECKING
@@ -153,19 +164,10 @@ asmlinkage void math_emulate(long arg)
   RE_ENTRANT_CHECK_ON;
 #endif /* RE_ENTRANT_CHECKING */
 
-  if (!current->used_math)
+  if (!FPU_current->used_math)
     {
-      int i;
-      for ( i = 0; i < 8; i++ )
-       {
-         /* Make sure that the registers are compatible
-            with the assumptions of the emulator. */
-         if ( !((regs[i].exp == EXP_UNDER) && (regs[i].sigh == 0)
-                && (regs[i].sigl == 0)) )
-           regs[i].sigh |= 0x80000000;
-       }
       finit();
-      current->used_math = 1;
+      FPU_current->used_math = 1;
     }
 
   SETUP_DATA_AREA(arg);
@@ -179,11 +181,11 @@ asmlinkage void math_emulate(long arg)
       FPU_EIP += code_base = FPU_CS << 4;
       code_limit = code_base + 0xffff;  /* Assumes code_base <= 0xffff0000 */
     }
-  else if ( FPU_CS == USER_CS && FPU_DS == USER_DS )
+  else if ( FPU_CS == FPU_USER_CS && FPU_DS == FPU_USER_DS )
     {
       addr_modes.default_mode = 0;
     }
-  else if ( FPU_CS == KERNEL_CS )
+  else if ( FPU_CS == FPU_KERNEL_CS )
     {
       printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP);
       panic("Math emulation needed in kernel");
@@ -196,7 +198,7 @@ asmlinkage void math_emulate(long arg)
          /* Can only handle segmented addressing via the LDT
             for now, and it must be 16 bit */
          printk("FPU emulator: Unsupported addressing mode\n");
-         math_abort(FPU_info, SIGILL);
+         math_abort(SIGILL);
        }
 
       if ( SEG_D_SIZE(code_descriptor = LDT_DESCRIPTOR(FPU_CS)) )
@@ -218,10 +220,10 @@ asmlinkage void math_emulate(long arg)
     }
 
   FPU_lookahead = 1;
-  if (current->flags & PF_PTRACED)
+  if ( FPU_TRACING )
     FPU_lookahead = 0;
 
-  if ( !valid_prefix(&byte1, (unsigned char **)&FPU_EIP,
+  if ( !valid_prefix(&byte1, (u_char **)&FPU_EIP,
                     &addr_modes.override) )
     {
       RE_ENTRANT_CHECK_OFF;
@@ -230,7 +232,7 @@ asmlinkage void math_emulate(long arg)
             byte1);
       RE_ENTRANT_CHECK_ON;
       EXCEPTION(EX_INTERNAL|0x126);
-      math_abort(FPU_info,SIGILL);
+      math_abort(SIGILL);
     }
 
 do_another_FPU_instruction:
@@ -244,7 +246,7 @@ do_another_FPU_instruction:
       /* This checks for the minimum instruction bytes.
         We also need to check any extra (address mode) code access. */
       if ( FPU_EIP > code_limit )
-       math_abort(FPU_info,SIGSEGV);
+       math_abort(SIGSEGV);
     }
 
   if ( (byte1 & 0xf8) != 0xd8 )
@@ -258,13 +260,13 @@ do_another_FPU_instruction:
        }
 #ifdef PARANOID
       EXCEPTION(EX_INTERNAL|0x128);
-      math_abort(FPU_info,SIGILL);
+      math_abort(SIGILL);
 #endif /* PARANOID */
     }
 
   RE_ENTRANT_CHECK_OFF;
   FPU_code_verify_area(1);
-  FPU_modrm = get_fs_byte((unsigned char *) FPU_EIP);
+  FPU_get_user(FPU_modrm, (u_char *) FPU_EIP);
   RE_ENTRANT_CHECK_ON;
   FPU_EIP++;
 
@@ -287,12 +289,11 @@ do_another_FPU_instruction:
           *  interrupts here.
           */
        do_the_FPU_interrupt:
+
          FPU_EIP = FPU_ORIG_EIP;       /* Point to current FPU instruction. */
 
          RE_ENTRANT_CHECK_OFF;
-         current->tss.trap_no = 16;
-         current->tss.error_code = 0;
-         send_sig(SIGFPE, current, 1);
+         FPU_SEND_SIGNAL(SIGFPE);
          return;
        }
     }
@@ -309,16 +310,16 @@ do_another_FPU_instruction:
 
       if ( (addr_modes.default_mode & SIXTEEN)
          ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX) )
-       data_address = get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off,
-                                     addr_modes);
+       data_address = FPU_get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off,
+                                         addr_modes);
       else
-       data_address = get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
-                                  addr_modes);
+       data_address = FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
+                                      addr_modes);
 
       if ( addr_modes.default_mode )
        {
          if ( FPU_EIP-1 > code_limit )
-           math_abort(FPU_info,SIGSEGV);
+           math_abort(SIGSEGV);
        }
 
       if ( !(byte1 & 1) )
@@ -326,7 +327,7 @@ do_another_FPU_instruction:
          unsigned short status1 = partial_status;
 
          st0_ptr = &st(0);
-         st0_tag = st0_ptr->tag;
+         st0_tag = FPU_gettag0();
 
          /* Stack underflow has priority */
          if ( NOT_EMPTY_ST0 )
@@ -335,36 +336,41 @@ do_another_FPU_instruction:
                {
                  /* This table works for 16 and 32 bit protected mode */
                  if ( access_limit < data_sizes_16[(byte1 >> 1) & 3] )
-                   math_abort(FPU_info,SIGSEGV);
+                   math_abort(SIGSEGV);
                }
 
              unmasked = 0;  /* Do this here to stop compiler warnings. */
              switch ( (byte1 >> 1) & 3 )
                {
                case 0:
-                 unmasked = reg_load_single((float *)data_address,
+                 unmasked = FPU_load_single((float *)data_address,
                                             &loaded_data);
+                 loaded_tag = unmasked & 0xff;
+                 unmasked &= ~0xff;
                  break;
                case 1:
-                 reg_load_int32((long *)data_address, &loaded_data);
+                 loaded_tag = FPU_load_int32((long *)data_address, &loaded_data);
                  break;
                case 2:
-                 unmasked = reg_load_double((double *)data_address,
+                 unmasked = FPU_load_double((double *)data_address,
                                             &loaded_data);
+                 loaded_tag = unmasked & 0xff;
+                 unmasked &= ~0xff;
                  break;
                case 3:
-                 reg_load_int16((short *)data_address, &loaded_data);
+               default:  /* Used here to suppress gcc warnings. */
+                 loaded_tag = FPU_load_int16((short *)data_address, &loaded_data);
                  break;
                }
-             
+
              /* No more access to user memory, it is safe
                 to use static data now */
 
              /* NaN operands have the next priority. */
              /* We have to delay looking at st(0) until after
                 loading the data, because that data might contain an SNaN */
-             if ( (st0_tag == TW_NaN) ||
-                 (loaded_data.tag == TW_NaN) )
+             if ( ((st0_tag == TAG_Special) && isNaN(st0_ptr)) ||
+                 ((loaded_tag == TAG_Special) && isNaN(&loaded_data)) )
                {
                  /* Restore the status word; we might have loaded a
                     denormal. */
@@ -375,22 +381,22 @@ do_another_FPU_instruction:
                      EXCEPTION(EX_Invalid);
                      setcc(SW_C3 | SW_C2 | SW_C0);
                      if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
-                       pop();             /* fcomp, masked, so we pop. */
+                       FPU_pop();             /* fcomp, masked, so we pop. */
                    }
                  else
                    {
+                     if ( loaded_tag == TAG_Special )
+                       loaded_tag = FPU_Special(&loaded_data);
 #ifdef PECULIAR_486
                      /* This is not really needed, but gives behaviour
                         identical to an 80486 */
                      if ( (FPU_modrm & 0x28) == 0x20 )
                        /* fdiv or fsub */
-                       real_2op_NaN(&loaded_data, st0_ptr,
-                                    st0_ptr);
+                       real_2op_NaN(&loaded_data, loaded_tag, 0, &loaded_data);
                      else
-#endif /* PECULIAR_486 */
+#endif /* PECULIAR_486 */ 
                        /* fadd, fdivr, fmul, or fsubr */
-                       real_2op_NaN(st0_ptr, &loaded_data,
-                                    st0_ptr);
+                       real_2op_NaN(&loaded_data, loaded_tag, 0, st0_ptr);
                    }
                  goto reg_mem_instr_done;
                }
@@ -401,11 +407,13 @@ do_another_FPU_instruction:
                  if ( (FPU_modrm & 0x38) == 0x38 )
                    {
                      /* fdivr */
-                     if ( (st0_tag == TW_Zero) &&
-                         (loaded_data.tag == TW_Valid) )
+                     if ( (st0_tag == TAG_Zero) &&
+                          ((loaded_tag == TAG_Valid)
+                           || (loaded_tag == TAG_Special
+                               && isdenormal(&loaded_data))) )
                        {
-                         if ( divide_by_zero(loaded_data.sign,
-                                             st0_ptr) )
+                         if ( FPU_divide_by_zero(0, getsign(&loaded_data))
+                              < 0 )
                            {
                              /* We use the fact here that the unmasked
                                 exception in the loaded data was for a
@@ -414,6 +422,8 @@ do_another_FPU_instruction:
                              partial_status &= ~SW_Denorm_Op;
                              partial_status |= status1 & SW_Denorm_Op;
                            }
+                         else
+                           setsign(st0_ptr, getsign(&loaded_data));
                        }
                    }
                  goto reg_mem_instr_done;
@@ -423,43 +433,38 @@ do_another_FPU_instruction:
                {
                case 0:         /* fadd */
                  clear_C1();
-                 reg_add(st0_ptr, &loaded_data, st0_ptr,
-                         control_word);
+                 FPU_add(&loaded_data, loaded_tag, 0, control_word);
                  break;
                case 1:         /* fmul */
                  clear_C1();
-                 reg_mul(st0_ptr, &loaded_data, st0_ptr,
-                         control_word);
+                 FPU_mul(&loaded_data, loaded_tag, 0, control_word);
                  break;
                case 2:         /* fcom */
-                 compare_st_data(&loaded_data);
+                 FPU_compare_st_data(&loaded_data, loaded_tag);
                  break;
                case 3:         /* fcomp */
-                 if ( !compare_st_data(&loaded_data) && !unmasked )
-                   pop();
+                 if ( !FPU_compare_st_data(&loaded_data, loaded_tag)
+                      && !unmasked )
+                   FPU_pop();
                  break;
                case 4:         /* fsub */
                  clear_C1();
-                 reg_sub(st0_ptr, &loaded_data, st0_ptr,
-                         control_word);
+                 FPU_sub(LOADED|loaded_tag, (int)&loaded_data, control_word);
                  break;
                case 5:         /* fsubr */
                  clear_C1();
-                 reg_sub(&loaded_data, st0_ptr, st0_ptr,
-                         control_word);
+                 FPU_sub(REV|LOADED|loaded_tag, (int)&loaded_data, control_word);
                  break;
                case 6:         /* fdiv */
                  clear_C1();
-                 reg_div(st0_ptr, &loaded_data, st0_ptr,
-                         control_word);
+                 FPU_div(LOADED|loaded_tag, (int)&loaded_data, control_word);
                  break;
                case 7:         /* fdivr */
                  clear_C1();
-                 if ( st0_tag == TW_Zero )
+                 if ( st0_tag == TAG_Zero )
                    partial_status = status1;  /* Undo any denorm tag,
-                                              zero-divide has priority. */
-                 reg_div(&loaded_data, st0_ptr, st0_ptr,
-                         control_word);
+                                                 zero-divide has priority. */
+                 FPU_div(REV|LOADED|loaded_tag, (int)&loaded_data, control_word);
                  break;
                }
            }
@@ -471,10 +476,10 @@ do_another_FPU_instruction:
                  EXCEPTION(EX_StackUnder);
                  setcc(SW_C3 | SW_C2 | SW_C0);
                  if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
-                   pop();             /* fcomp */
+                   FPU_pop();             /* fcomp */
                }
              else
-               stack_underflow();
+               FPU_stack_underflow();
            }
        reg_mem_instr_done:
          operand_address = data_sel_off;
@@ -482,8 +487,8 @@ do_another_FPU_instruction:
       else
        {
          if ( !(no_ip_update =
-                load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1,
-                                 addr_modes, data_address)) )
+                FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1,
+                               addr_modes, data_address)) )
            {
              operand_address = data_sel_off;
            }
@@ -493,7 +498,7 @@ do_another_FPU_instruction:
   else
     {
       /* None of these instructions access user memory */
-      unsigned char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
+      u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
 
 #ifdef PECULIAR_486
       /* This is supposed to be undefined, but a real 80486 seems
@@ -503,7 +508,7 @@ do_another_FPU_instruction:
 #endif /* PECULIAR_486 */
 
       st0_ptr = &st(0);
-      st0_tag = st0_ptr->tag;
+      st0_tag = FPU_gettag0();
       switch ( type_table[(int) instr_index] )
        {
        case _NONE_:   /* also _REGIc: _REGIn */
@@ -511,28 +516,28 @@ do_another_FPU_instruction:
        case _REG0_:
          if ( !NOT_EMPTY_ST0 )
            {
-             stack_underflow();
+             FPU_stack_underflow();
              goto FPU_instruction_done;
            }
          break;
        case _REGIi:
          if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
            {
-             stack_underflow_i(FPU_rm);
+             FPU_stack_underflow_i(FPU_rm);
              goto FPU_instruction_done;
            }
          break;
        case _REGIp:
          if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
            {
-             stack_underflow_pop(FPU_rm);
+             FPU_stack_underflow_pop(FPU_rm);
              goto FPU_instruction_done;
            }
          break;
        case _REGI_:
          if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
            {
-             stack_underflow();
+             FPU_stack_underflow();
              goto FPU_instruction_done;
            }
          break;
@@ -558,14 +563,14 @@ FPU_fwait_done:
 
 #ifdef DEBUG
   RE_ENTRANT_CHECK_OFF;
-  emu_printall();
+  FPU_printall();
   RE_ENTRANT_CHECK_ON;
 #endif /* DEBUG */
 
-  if (FPU_lookahead && !need_resched)
+  if (FPU_lookahead && !FPU_need_resched)
     {
       FPU_ORIG_EIP = FPU_EIP - code_base;
-      if ( valid_prefix(&byte1, (unsigned char **)&FPU_EIP,
+      if ( valid_prefix(&byte1, (u_char **)&FPU_EIP,
                        &addr_modes.override) )
        goto do_another_FPU_instruction;
     }
@@ -581,17 +586,17 @@ FPU_fwait_done:
    all prefix bytes, further changes are needed in the emulator code
    which accesses user address space. Access to separate segments is
    important for msdos emulation. */
-static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip,
+static int valid_prefix(u_char *Byte, u_char **fpu_eip,
                        overrides *override)
 {
-  unsigned char byte;
-  unsigned char *ip = *fpu_eip;
+  u_char byte;
+  u_char *ip = *fpu_eip;
 
   *override = (overrides) { 0, 0, PREFIX_DEFAULT };       /* defaults */
 
   RE_ENTRANT_CHECK_OFF;
-  FPU_code_verify_area(1);
-  byte = get_fs_byte(ip);
+  FPU_verify_area(VERIFY_READ,ip,1);
+  FPU_get_user(byte, ip);
   RE_ENTRANT_CHECK_ON;
 
   while ( 1 )
@@ -636,8 +641,8 @@ static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip,
        do_next_byte:
          ip++;
          RE_ENTRANT_CHECK_OFF;
-         FPU_code_verify_area(1);
-         byte = get_fs_byte(ip);
+         FPU_verify_area(VERIFY_READ,ip,1);
+         FPU_get_user(byte, ip);
          RE_ENTRANT_CHECK_ON;
          break;
        case FWAIT_OPCODE:
@@ -662,34 +667,100 @@ static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip,
 }
 
 
-void math_abort(struct info * info, unsigned int signal)
+void math_abort(unsigned int signal)
 {
-       FPU_EIP = FPU_ORIG_EIP;
-       current->tss.trap_no = 16;
-       current->tss.error_code = 0;
-       send_sig(signal,current,1);
-       RE_ENTRANT_CHECK_OFF;
-       __asm__("movl %0,%%esp ; ret" : : "g" (((long) info)-4));
+  FPU_EIP = FPU_ORIG_EIP;
+  FPU_SEND_SIGNAL(signal);
+  RE_ENTRANT_CHECK_OFF;
+  FPU_EXIT;
 #ifdef PARANOID
-      printk("ERROR: wm-FPU-emu math_abort failed!\n");
+  printk("ERROR: wm-FPU-emu math_abort failed!\n");
 #endif /* PARANOID */
 }
 
 
 
-void restore_i387_soft(struct _fpstate *buf)
+#define S387 ((struct i387_soft_struct *)s387)
+#define FPU_s_top      S387->ftop
+#define FPU_s_regs     S387->st_space
+#define sstatus_word() \
+  ((S387->swd & ~SW_Top & 0xffff) | ((FPU_s_top << SW_Top_Shift) & SW_Top))
+
+int restore_i387_soft(void *s387, struct _fpstate *buf)
 {
-  fpu_addr_modes addr_modes = {{ 0, 0, PREFIX_DEFAULT }, 0};
+  u_char *d = (u_char *)buf;
+  int offset, other, i, tags, regnr, tag, newtop;
+
+  RE_ENTRANT_CHECK_OFF;
+  FPU_verify_area(VERIFY_READ, d, 7*4 + 8*10);
+  if (FPU_copy_from_user(&S387->cwd, d, 7*4))
+    return -1;
+  RE_ENTRANT_CHECK_ON;
+
+  d += 7*4;
 
-  frstor(addr_modes, (char *)buf);
+  FPU_s_top = (S387->swd >> SW_Top_Shift) & 7;
+  offset = (FPU_s_top & 7) * 10;
+  other = 80 - offset;
+
+  RE_ENTRANT_CHECK_OFF;
+  /* Copy all registers in stack order. */
+  if (FPU_copy_from_user(((u_char *)&FPU_s_regs)+offset, d, other))
+    return -1;
+  if ( offset )
+    if (FPU_copy_from_user((u_char *)&FPU_s_regs, d+other, offset))
+      return -1;
+  RE_ENTRANT_CHECK_ON;
+
+  /* The tags may need to be corrected now. */
+  tags = S387->twd;
+  newtop = FPU_s_top;
+  for ( i = 0; i < 8; i++ )
+    {
+      regnr = (i+newtop) & 7;
+      if ( ((tags >> ((regnr & 7)*2)) & 3) != TAG_Empty )
+       {
+         /* The loaded data over-rides all other cases. */
+         tag = FPU_tagof((FPU_REG *)((u_char *)FPU_s_regs + 10*regnr));
+         tags &= ~(3 << (regnr*2));
+         tags |= (tag & 3) << (regnr*2);
+       }
+    }
+  S387->twd = tags;
+
+  return 0;
 }
 
 
-struct _fpstate * save_i387_soft(struct _fpstate * buf)
+int save_i387_soft(void *s387, struct _fpstate * buf)
 {
-  fpu_addr_modes addr_modes = {{ 0, 0, PREFIX_DEFAULT }, 0};
+  u_char *d = (u_char *)buf;
+  int offset = (FPU_s_top & 7) * 10, other = 80 - offset;
 
-  fsave(addr_modes, (char *)buf);
+  RE_ENTRANT_CHECK_OFF;
+  FPU_verify_area(VERIFY_WRITE, d, 7*4 + 8*10);
+#ifdef PECULIAR_486
+  S387->cwd &= ~0xe080;
+  /* An 80486 sets nearly all of the reserved bits to 1. */
+  S387->cwd |= 0xffff0040;
+  S387->swd = sstatus_word() | 0xffff0000;
+  S387->twd |= 0xffff0000;
+  S387->fcs &= ~0xf8000000;
+  S387->fos |= 0xffff0000;
+#endif /* PECULIAR_486 */
+  FPU_copy_to_user(d, &S387->cwd, 7*4);
+  RE_ENTRANT_CHECK_ON;
+
+  d += 7*4;
+
+  RE_ENTRANT_CHECK_OFF;
+  /* Copy all registers in stack order. */
+  if (FPU_copy_to_user(d, ((u_char *)&FPU_s_regs)+offset, other))
+    return -1;
+  if ( offset )
+    if (FPU_copy_to_user(d+other, (u_char *)&FPU_s_regs, offset))
+      return -1
+  RE_ENTRANT_CHECK_ON;
 
-  return buf;
+  return 1;
 }
index f3a8377d15e865849c1c85420b83658aaf340c73..ba562d12979b57b6b71942b98fb06dc487b46a03 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Implement a few FPU instructions.                                         |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
+ | Copyright (C) 1992,1993,1994,1997                                         |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@suburbia.net             |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 #include "reg_constant.h"
 
 
-static void fchs(FPU_REG *st0_ptr)
+static void fchs(FPU_REG *st0_ptr, u_char st0tag)
 {
-  if ( st0_ptr->tag ^ TW_Empty )
+  if ( st0tag ^ TAG_Empty )
     {
-      st0_ptr->sign ^= SIGN_POS^SIGN_NEG;
+      signbyte(st0_ptr) ^= SIGN_NEG;
       clear_C1();
     }
   else
-    stack_underflow();
+    FPU_stack_underflow();
 }
 
-static void fabs(FPU_REG *st0_ptr)
+
+static void fabs(FPU_REG *st0_ptr, u_char st0tag)
 {
-  if ( st0_ptr->tag ^ TW_Empty )
+  if ( st0tag ^ TAG_Empty )
     {
-      st0_ptr->sign = SIGN_POS;
+      setpositive(st0_ptr);
       clear_C1();
     }
   else
-    stack_underflow();
+    FPU_stack_underflow();
 }
 
 
-static void ftst_(FPU_REG *st0_ptr)
+static void ftst_(FPU_REG *st0_ptr, u_char st0tag)
 {
-  switch (st0_ptr->tag)
+  switch (st0tag)
     {
-    case TW_Zero:
+    case TAG_Zero:
       setcc(SW_C3);
       break;
-    case TW_Valid:
-      if (st0_ptr->sign == SIGN_POS)
+    case TAG_Valid:
+      if (getsign(st0_ptr) == SIGN_POS)
         setcc(0);
       else
         setcc(SW_C0);
-
-#ifdef DENORM_OPERAND
-      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+      break;
+    case TAG_Special:
+      switch ( FPU_Special(st0_ptr) )
        {
+       case TW_Denormal:
+         if (getsign(st0_ptr) == SIGN_POS)
+           setcc(0);
+         else
+           setcc(SW_C0);
+         if ( denormal_operand() < 0 )
+           {
 #ifdef PECULIAR_486
-         /* This is weird! */
-         if (st0_ptr->sign == SIGN_POS)
-           setcc(SW_C3);
+             /* This is weird! */
+             if (getsign(st0_ptr) == SIGN_POS)
+               setcc(SW_C3);
 #endif /* PECULIAR_486 */
-         return;
+             return;
+           }
+         break;
+       case TW_NaN:
+         setcc(SW_C0|SW_C2|SW_C3);   /* Operand is not comparable */ 
+         EXCEPTION(EX_Invalid);
+         break;
+       case TW_Infinity:
+         if (getsign(st0_ptr) == SIGN_POS)
+           setcc(0);
+         else
+           setcc(SW_C0);
+         break;
+       default:
+         setcc(SW_C0|SW_C2|SW_C3);   /* Operand is not comparable */ 
+         EXCEPTION(EX_INTERNAL|0x14);
+         break;
        }
-#endif /* DENORM_OPERAND */
-
-      break;
-    case TW_NaN:
-      setcc(SW_C0|SW_C2|SW_C3);   /* Operand is not comparable */ 
-      EXCEPTION(EX_Invalid);
-      break;
-    case TW_Infinity:
-      if (st0_ptr->sign == SIGN_POS)
-        setcc(0);
-      else
-        setcc(SW_C0);
       break;
-    case TW_Empty:
+    case TAG_Empty:
       setcc(SW_C0|SW_C2|SW_C3);
       EXCEPTION(EX_StackUnder);
       break;
-    default:
-      setcc(SW_C0|SW_C2|SW_C3);   /* Operand is not comparable */ 
-      EXCEPTION(EX_INTERNAL|0x14);
-      break;
     }
 }
 
-static void fxam(FPU_REG *st0_ptr)
+
+static void fxam(FPU_REG *st0_ptr, u_char st0tag)
 {
-  int c=0;
-  switch (st0_ptr->tag)
+  int c = 0;
+  switch (st0tag)
     {
-    case TW_Empty:
+    case TAG_Empty:
       c = SW_C3|SW_C0;
       break;
-    case TW_Zero:
+    case TAG_Zero:
       c = SW_C3;
       break;
-    case TW_Valid:
-      /* This will need to be changed if TW_Denormal is ever used. */
-      if ( st0_ptr->exp <= EXP_UNDER )
-        c = SW_C2|SW_C3;  /* Denormal */
-      else
-        c = SW_C2;
-      break;
-    case TW_NaN:
-      c = SW_C0;
-      break;
-    case TW_Infinity:
-      c = SW_C2|SW_C0;
+    case TAG_Valid:
+      c = SW_C2;
       break;
+    case TAG_Special:
+      switch ( FPU_Special(st0_ptr) )
+       {
+       case TW_Denormal:
+         c = SW_C2|SW_C3;  /* Denormal */
+         break;
+       case TW_NaN:
+         /* We also use NaN for unsupported types. */
+         if ( (st0_ptr->sigh & 0x80000000) && (exponent(st0_ptr) == EXP_OVER) )
+           c = SW_C0;
+         break;
+       case TW_Infinity:
+         c = SW_C2|SW_C0;
+         break;
+       }
     }
-  if (st0_ptr->sign == SIGN_NEG)
+  if ( getsign(st0_ptr) == SIGN_NEG )
     c |= SW_C1;
   setcc(c);
 }
@@ -123,7 +137,7 @@ static FUNC_ST0 const fp_etc_table[] = {
   ftst_, fxam, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal
 };
 
-void fp_etc()
+void FPU_etc()
 {
-  (fp_etc_table[FPU_rm])(&st(0));
+  (fp_etc_table[FPU_rm])(&st(0), FPU_gettag0());
 }
index b4392fe57de70085683089739547a52c2fe86bea..c05686a0e10d373464046551d6d24359cb9aff86 100644 (file)
@@ -1,22 +1,26 @@
+#ifndef _FPU_PROTO_H
+#define _FPU_PROTO_H
+
 /* errors.c */
 extern void Un_impl(void);
 extern void FPU_illegal(void);
-extern void emu_printall(void);
-extern void stack_overflow(void);
-extern void stack_underflow(void);
-extern void stack_underflow_i(int i);
-extern void stack_underflow_pop(int i);
+extern void FPU_printall(void);
+asmlinkage void FPU_exception(int n);
+extern int real_1op_NaN(FPU_REG *a);
+extern int real_2op_NaN(FPU_REG const *b, u_char tagb, int deststnr,
+                       FPU_REG const *defaultNaN);
+extern int arith_invalid(int deststnr);
+extern int FPU_divide_by_zero(int deststnr, u_char sign);
 extern int set_precision_flag(int flags);
-asmlinkage void exception(int n);
-asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest);
-asmlinkage int arith_invalid(FPU_REG *dest);
-asmlinkage int divide_by_zero(int sign, FPU_REG *dest);
-asmlinkage void set_precision_flag_up(void);
-asmlinkage void set_precision_flag_down(void);
-asmlinkage int denormal_operand(void);
-asmlinkage int arith_overflow(FPU_REG *dest);
-asmlinkage int arith_underflow(FPU_REG *dest);
-
+extern void set_precision_flag_up(void);
+extern void set_precision_flag_down(void);
+extern int denormal_operand(void);
+extern int arith_overflow(FPU_REG *dest, int sign);
+extern int arith_underflow(FPU_REG *dest);
+extern void FPU_stack_overflow(void);
+extern void FPU_stack_underflow(void);
+extern void FPU_stack_underflow_i(int i);
+extern void FPU_stack_underflow_pop(int i);
 /* fpu_arith.c */
 extern void fadd__(void);
 extern void fmul__(void);
@@ -36,7 +40,6 @@ extern void fsubrp(void);
 extern void fsubp_(void);
 extern void fdivrp(void);
 extern void fdivp_(void);
-
 /* fpu_aux.c */
 extern void fclex(void);
 extern void finit(void);
@@ -50,56 +53,66 @@ extern void ffreep(void);
 extern void fst_i_(void);
 extern void fstp_i(void);
 
-/* fpu_entry.c */
-asmlinkage void math_emulate(long arg);
-extern void math_abort(struct info *info, unsigned int signal);
+extern void fcmovnb(void);
+extern void fcmovne(void);
+extern void fcmovnbe(void);
+extern void fcmovnu(void);
+extern void fcmovb(void);
+extern void fcmove(void);
+extern void fcmovbe(void);
+extern void fcmovu(void);
 
+/* fpu_entry.c */
+extern void math_emulate(long arg);
+extern void math_abort(unsigned int signal);
 /* fpu_etc.c */
-extern void fp_etc(void);
-
+extern void FPU_etc(void);
+/* fpu_tags.c */
+extern int FPU_gettag0(void);
+extern int FPU_gettagi(int stnr);
+extern int FPU_gettag(int regnr);
+extern void FPU_settag0(int tag);
+extern void FPU_settagi(int stnr, int tag);
+extern void FPU_settag(int regnr, int tag);
+extern int FPU_Special(FPU_REG const *ptr);
+extern int isNaN(FPU_REG const *ptr);
+extern void FPU_pop(void);
+extern int FPU_empty_i(int stnr);
+extern int FPU_stackoverflow(FPU_REG **st_new_ptr);
+extern void FPU_sync_tags(void);
+extern void FPU_copy_to_regi(FPU_REG const *r, u_char tag, int stnr);
+extern void FPU_copy_to_reg1(FPU_REG const *r, u_char tag);
+extern void FPU_copy_to_reg0(FPU_REG const *r, u_char tag);
 /* fpu_trig.c */
-extern void convert_l2reg(long const *arg, FPU_REG *dest);
-extern void trig_a(void);
-extern void trig_b(void);
-
+extern void FPU_triga(void);
+extern void FPU_trigb(void);
 /* get_address.c */
-extern void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
-                        struct address *addr,
-                        fpu_addr_modes);
-extern void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
-                           struct address *addr,
-                           fpu_addr_modes);
-
+extern void *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
+                        struct address *addr, fpu_addr_modes addr_modes);
+extern void *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
+                           struct address *addr, fpu_addr_modes addr_modes);
 /* load_store.c */
-extern int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
-                            void *address);
-
+extern int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
+                           void *data_address);
 /* poly_2xm1.c */
-extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result);
-
+extern int poly_2xm1(u_char sign, FPU_REG *arg, FPU_REG *result);
 /* poly_atan.c */
-extern void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result);
-
+extern void poly_atan(FPU_REG *st0_ptr, u_char st0_tag, FPU_REG *st1_ptr,
+                     u_char st1_tag);
 /* poly_l2.c */
-extern void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result);
-extern int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result);
-
+extern void poly_l2(FPU_REG *st0_ptr, FPU_REG *st1_ptr, u_char st1_sign);
+extern int poly_l2p1(u_char s0, u_char s1, FPU_REG *r0, FPU_REG *r1,
+                    FPU_REG *d);
 /* poly_sin.c */
-extern void poly_sine(FPU_REG const *arg, FPU_REG *result);
-extern void poly_cos(FPU_REG const *arg, FPU_REG *result);
-
+extern void poly_sine(FPU_REG *st0_ptr);
+extern void poly_cos(FPU_REG *st0_ptr);
 /* poly_tan.c */
-extern void poly_tan(FPU_REG const *arg, FPU_REG *result);
-
+extern void poly_tan(FPU_REG *st0_ptr);
 /* reg_add_sub.c */
-extern int reg_add(FPU_REG const *a, FPU_REG const *b,
-                  FPU_REG *dest, int control_w);
-extern int reg_sub(FPU_REG const *a, FPU_REG const *b,
-                  FPU_REG *dest, int control_w);
-
+extern int FPU_add(FPU_REG const *b, u_char tagb, int destrnr, int control_w);
+extern int FPU_sub(int flags, int rm, int control_w);
 /* reg_compare.c */
-extern int compare(FPU_REG const *b);
-extern int compare_st_data(FPU_REG const *b);
+extern int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag);
 extern void fcom_st(void);
 extern void fcompst(void);
 extern void fcompp(void);
@@ -107,31 +120,37 @@ extern void fucom_(void);
 extern void fucomp(void);
 extern void fucompp(void);
 
+extern void fucomi(void);
+extern void fcomi(void);
 /* reg_constant.c */
 extern void fconst(void);
-
 /* reg_ld_str.c */
-extern int reg_load_extended(long double *addr, FPU_REG *loaded_data);
-extern int reg_load_double(double *dfloat, FPU_REG *loaded_data);
-extern int reg_load_single(float *single, FPU_REG *loaded_data);
-extern void reg_load_int64(long long *_s, FPU_REG *loaded_data);
-extern void reg_load_int32(long *_s, FPU_REG *loaded_data);
-extern void reg_load_int16(short *_s, FPU_REG *loaded_data);
-extern void reg_load_bcd(char *s, FPU_REG *loaded_data);
-extern int reg_store_extended(long double *d, FPU_REG *st0_ptr);
-extern int reg_store_double(double *dfloat, FPU_REG *st0_ptr);
-extern int reg_store_single(float *single, FPU_REG *st0_ptr);
-extern int reg_store_int64(long long *d, FPU_REG *st0_ptr);
-extern int reg_store_int32(long *d, FPU_REG *st0_ptr);
-extern int reg_store_int16(short *d, FPU_REG *st0_ptr);
-extern int reg_store_bcd(char *d, FPU_REG *st0_ptr);
-extern int round_to_int(FPU_REG *r);
-extern char *fldenv(fpu_addr_modes addr_modes, char *address);
-extern void frstor(fpu_addr_modes addr_modes, char *address);
-extern unsigned short tag_word(void);
-extern char *fstenv(fpu_addr_modes addr_modes, char *address);
-extern void fsave(fpu_addr_modes addr_modes, char *address);
-
+extern int FPU_load_extended(long double *s, int stnr);
+extern int FPU_load_double(double *dfloat, FPU_REG *loaded_data);
+extern int FPU_load_single(float *single, FPU_REG *loaded_data);
+extern int FPU_load_int64(long long *_s);
+extern int FPU_load_int32(long *_s, FPU_REG *loaded_data);
+extern int FPU_load_int16(short *_s, FPU_REG *loaded_data);
+extern int FPU_load_bcd(u_char *s);
+extern int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag,
+                             long double *d);
+extern int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double *dfloat);
+extern int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float *single);
+extern int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag, long long *d);
+extern int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long *d);
+extern int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short *d);
+extern int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char *d);
+extern int FPU_round_to_int(FPU_REG *r, u_char tag);
+extern u_char *fldenv(fpu_addr_modes addr_modes, u_char *s);
+extern void frstor(fpu_addr_modes addr_modes, u_char *data_address);
+extern u_char *fstenv(fpu_addr_modes addr_modes, u_char *d);
+extern void fsave(fpu_addr_modes addr_modes, u_char *data_address);
+extern int FPU_tagof(FPU_REG *ptr);
 /* reg_mul.c */
-extern int reg_mul(FPU_REG const *a, FPU_REG const *b,
-                  FPU_REG *dest, unsigned int control_w);
+extern int FPU_mul(FPU_REG const *b, u_char tagb, int deststnr, int control_w);
+
+extern int FPU_div(int flags, int regrm, int control_w);
+/* reg_convert.c */
+extern int FPU_to_exp16(FPU_REG const *a, FPU_REG *x);
+#endif /* _FPU_PROTO_H */
+
index d2c3fa7160b9486c1523e92ad75134bf8b095936..62ab30a762faa631015142e1e0b5e4b19006fec5 100644 (file)
@@ -1,9 +1,9 @@
 /*---------------------------------------------------------------------------+
  |  fpu_system.h                                                             |
  |                                                                           |
- | Copyright (C) 1992,1994                                                   |
+ | Copyright (C) 1992,1994,1997,2001                                         |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
 
 /* system dependent definitions */
 
+#include <asm/segment.h>
+
+#include <linux/isdnif.h>  /* for copy to and from user */
+
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 
+#ifdef FPU_current
+extern struct task_struct * FPU_current;
+#else
+#define FPU_current current
+#endif
+
 /* This sets the pointer FPU_info to point to the argument part
    of the stack frame of math_emulate() */
-#define SETUP_DATA_AREA(arg)    FPU_info = (struct info *) &arg
-
-#define LDT_DESCRIPTOR(s)       (current->ldt[(s) >> 3])
-#define SEG_D_SIZE(x)           ((x).b & (3 << 21))
-#define SEG_G_BIT(x)            ((x).b & (1 << 23))
-#define SEG_GRANULARITY(x)      (((x).b & (1 << 23)) ? 4096 : 1)
-#define SEG_286_MODE(x)         ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23)))
-#define SEG_BASE_ADDR(s)        (((s).b & 0xff000000) \
+#define SETUP_DATA_AREA(arg)   FPU_info = (struct info *) &arg
+
+#define LDT_DESCRIPTOR(s)      (FPU_current->ldt[(s) >> 3])
+#define SEG_D_SIZE(x)          ((x).b & (3 << 21))
+#define SEG_G_BIT(x)           ((x).b & (1 << 23))
+#define SEG_GRANULARITY(x)     (((x).b & (1 << 23)) ? 4096 : 1)
+#define SEG_286_MODE(x)                ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23)))
+#define SEG_BASE_ADDR(s)       (((s).b & 0xff000000) \
                                 | (((s).b & 0xff) << 16) | ((s).a >> 16))
-#define SEG_LIMIT(s)            (((s).b & 0xff0000) | ((s).a & 0xffff))
-#define SEG_EXECUTE_ONLY(s)     (((s).b & ((1 << 11) | (1 << 9))) == (1 << 11))
-#define SEG_WRITE_PERM(s)       (((s).b & ((1 << 11) | (1 << 9))) == (1 << 9))
-#define SEG_EXPAND_DOWN(s)      (((s).b & ((1 << 11) | (1 << 10))) \
+#define SEG_LIMIT(s)           (((s).b & 0xff0000) | ((s).a & 0xffff))
+#define SEG_EXECUTE_ONLY(s)    (((s).b & ((1 << 11) | (1 << 9))) == (1 << 11))
+#define SEG_WRITE_PERM(s)      (((s).b & ((1 << 11) | (1 << 9))) == (1 << 9))
+#define SEG_EXPAND_DOWN(s)     (((s).b & ((1 << 11) | (1 << 10))) \
                                 == (1 << 10))
 
-#define I387                   (current->tss.i387)
+#define I387                   (FPU_current->tss.i387)
 #define FPU_info               (I387.soft.info)
 
 #define FPU_CS                 (*(unsigned short *) &(FPU_info->___cs))
 #define FPU_EIP                        (FPU_info->___eip)
 #define FPU_ORIG_EIP           (FPU_info->___orig_eip)
 
+#define FPU_USER_CS            USER_CS
+#define FPU_USER_DS            USER_DS
+#define FPU_KERNEL_CS          KERNEL_CS
+
+#define FPU_TRACING            (FPU_current->flags & PF_PTRACED)
+
+#define FPU_SEND_SIGNAL(signal)        FPU_current->tss.trap_no = 16; \
+                               FPU_current->tss.error_code = 0; \
+                               send_sig(signal,FPU_current,1);
+
 #define FPU_lookahead           (I387.soft.lookahead)
 
+#define FPU_need_resched       need_resched
+
 /* nz if ip_offset and cs_selector are not to be set for the current
    instruction. */
-#define no_ip_update            (((char *)&(I387.soft.twd))[0])
-#define FPU_rm                  (((unsigned char *)&(I387.soft.twd))[1])
+#define no_ip_update           (*(u_char *)&(I387.soft.no_update))
+#define FPU_rm                 (*(u_char *)&(I387.soft.rm))
 
 /* Number of bytes of data which can be legally accessed by the current
    instruction. This only needs to hold a number <= 108, so a byte will do. */
-#define access_limit            (((unsigned char *)&(I387.soft.twd))[2])
+#define access_limit           (*(u_char *)&(I387.soft.alimit))
 
-#define partial_status         (I387.soft.swd)
+#define partial_status         (I387.soft.swd)
 #define control_word           (I387.soft.cwd)
-#define regs                   (I387.soft.regs)
-#define top                    (I387.soft.top)
+#define fpu_tag_word           (I387.soft.twd)
+#define registers              (I387.soft.st_space)
+#define FPU_top                        (I387.soft.ftop)
 
-#define instruction_address     (*(struct address *)&I387.soft.fip)
-#define operand_address         (*(struct address *)&I387.soft.foo)
+#define instruction_address    (*(struct address *)&I387.soft.fip)
+#define operand_address                (*(struct address *)&I387.soft.foo)
 
-#define FPU_verify_area(x,y,z)  if ( verify_area(x,y,z) ) \
-                                math_abort(FPU_info,SIGSEGV)
+#define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) math_abort(SIGSEGV)
 
 #undef FPU_IGNORE_CODE_SEGV
 #ifdef FPU_IGNORE_CODE_SEGV
 #define        FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z)
 #endif
 
+#ifndef FPU_get_user
+#define FPU_get_user(x,y)       (x) = get_user(y)
+#define FPU_put_user(x,y)       put_user((x),(y))
+#define FPU_copy_from_user(x,y,z)      copy_from_user((x),(y),(z))
+#define FPU_copy_to_user(x,y,z)        copy_to_user((x),(y),(z))
+#else
+int get_user_n(void *dst, void *ptr, int n);
+#define FPU_get_user(x,y)       { unsigned long int v; \
+                       get_user_n(&v, (y),sizeof(*(y))); (x) = v; }
+int put_user_n(void *src, void *ptr, int n);
+#define FPU_put_user(x,y)       { unsigned long int v = (x); \
+                       put_user_n(&v,(y),sizeof(*(y))); }
+#define FPU_copy_from_user(x,y,n)      get_user_n((x),(y),n)
+#define FPU_copy_to_user(x,y,n)        put_user_n((x),(y),n)
+#endif
+
+#define FPU_EXIT  __asm__("movl %0,%%esp ; ret": :"g" (((long) FPU_info)-4))
+
 #endif
diff --git a/arch/i386/math-emu/fpu_tags.c b/arch/i386/math-emu/fpu_tags.c
new file mode 100644 (file)
index 0000000..56e7a21
--- /dev/null
@@ -0,0 +1,127 @@
+/*---------------------------------------------------------------------------+
+ |  fpu_tags.c                                                               |
+ |                                                                           |
+ |  Set FPU register tags.                                                   |
+ |                                                                           |
+ | Copyright (C) 1997,2001                                                   |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@melbpc.org.au                             |
+ |                                                                           |
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_emu.h"
+#include "fpu_system.h"
+#include "exception.h"
+
+
+void FPU_pop(void)
+{
+  fpu_tag_word |= 3 << ((FPU_top & 7)*2);
+  FPU_top++;
+}
+
+
+int FPU_gettag0(void)
+{
+  return (fpu_tag_word >> ((FPU_top & 7)*2)) & 3;
+}
+
+
+int FPU_gettagi(int stnr)
+{
+  return (fpu_tag_word >> (((FPU_top+stnr) & 7)*2)) & 3;
+}
+
+
+int FPU_gettag(int regnr)
+{
+  return (fpu_tag_word >> ((regnr & 7)*2)) & 3;
+}
+
+
+void FPU_settag0(int tag)
+{
+  int regnr = FPU_top;
+  regnr &= 7;
+  fpu_tag_word &= ~(3 << (regnr*2));
+  fpu_tag_word |= (tag & 3) << (regnr*2);
+}
+
+
+void FPU_settagi(int stnr, int tag)
+{
+  int regnr = stnr+FPU_top;
+  regnr &= 7;
+  fpu_tag_word &= ~(3 << (regnr*2));
+  fpu_tag_word |= (tag & 3) << (regnr*2);
+}
+
+
+void FPU_settag(int regnr, int tag)
+{
+  regnr &= 7;
+  fpu_tag_word &= ~(3 << (regnr*2));
+  fpu_tag_word |= (tag & 3) << (regnr*2);
+}
+
+
+int FPU_Special(FPU_REG const *ptr)
+{
+  int exp = exponent(ptr);
+
+  if ( exp == EXP_BIAS+EXP_UNDER )
+    return TW_Denormal;
+  else if ( exp != EXP_BIAS+EXP_OVER )
+    return TW_NaN;
+  else if ( (ptr->sigh == 0x80000000) && (ptr->sigl == 0) )
+    return TW_Infinity;
+  return TW_NaN;
+}
+
+
+int isNaN(FPU_REG const *ptr)
+{
+  return ( (exponent(ptr) == EXP_BIAS+EXP_OVER)
+          && !((ptr->sigh == 0x80000000) && (ptr->sigl == 0)) );
+}
+
+
+int FPU_empty_i(int stnr)
+{
+  int regnr = (FPU_top+stnr) & 7;
+
+  return ((fpu_tag_word >> (regnr*2)) & 3) == TAG_Empty;
+}
+
+
+int FPU_stackoverflow(FPU_REG **st_new_ptr)
+{
+  *st_new_ptr = &st(-1);
+
+  return ((fpu_tag_word >> (((FPU_top - 1) & 7)*2)) & 3) != TAG_Empty;
+}
+
+
+void FPU_copy_to_regi(FPU_REG const *r, u_char tag, int stnr)
+{
+  reg_copy(r, &st(stnr));
+  FPU_settagi(stnr, tag);
+}
+
+void FPU_copy_to_reg1(FPU_REG const *r, u_char tag)
+{
+  reg_copy(r, &st(1));
+  FPU_settagi(1, tag);
+}
+
+void FPU_copy_to_reg0(FPU_REG const *r, u_char tag)
+{
+  int regnr = FPU_top;
+  regnr &= 7;
+
+  reg_copy(r, &st(0));
+
+  fpu_tag_word &= ~(3 << (regnr*2));
+  fpu_tag_word |= (tag & 3) << (regnr*2);
+}
index 51dca72747668a4ed9328c3fac3798029517b644..2cf1faa9312cad85f9192ca300c3effa826ed275 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Implementation of the FPU "transcendental" functions.                     |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
+ | Copyright (C) 1992,1993,1994,1997,1999,2001                               |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -17,7 +17,6 @@
 #include "control_w.h"
 #include "reg_constant.h"      
 
-
 static void rem_kernel(unsigned long long st0, unsigned long long *y,
                       unsigned long long st1,
                       unsigned long long q, int n);
@@ -25,9 +24,6 @@ static void rem_kernel(unsigned long long st0, unsigned long long *y,
 #define BETTER_THAN_486
 
 #define FCOS  4
-/* Not needed now with new code
-#define FPTAN 1
- */
 
 /* Used only by fptan, fsin, fcos, and fsincos. */
 /* This routine produces very accurate results, similar to
@@ -35,13 +31,15 @@ static void rem_kernel(unsigned long long st0, unsigned long long *y,
 /* Limited measurements show no results worse than 64 bit precision
    except for the results for arguments close to 2^63, where the
    precision of the result sometimes degrades to about 63.9 bits */
-static int trig_arg(FPU_REG *X, int even)
+static int trig_arg(FPU_REG *st0_ptr, int even)
 {
   FPU_REG tmp;
+  u_char tmptag;
   unsigned long long q;
   int old_cw = control_word, saved_status = partial_status;
+  int tag, st0_tag = TAG_Valid;
 
-  if ( X->exp >= EXP_BIAS + 63 )
+  if ( exponent(st0_ptr) >= 63 )
     {
       partial_status |= SW_C2;     /* Reduction incomplete. */
       return -1;
@@ -50,58 +48,53 @@ static int trig_arg(FPU_REG *X, int even)
   control_word &= ~CW_RC;
   control_word |= RC_CHOP;
 
-  reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
-  round_to_int(&tmp);  /* Fortunately, this can't overflow
-                         to 2^64 */
+  setpositive(st0_ptr);
+  tag = FPU_u_div(st0_ptr, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f,
+                 SIGN_POS);
+
+  FPU_round_to_int(&tmp, tag);  /* Fortunately, this can't overflow
+                                  to 2^64 */
   q = significand(&tmp);
   if ( q )
     {
-      rem_kernel(significand(X),
+      rem_kernel(significand(st0_ptr),
                 &significand(&tmp),
                 significand(&CONST_PI2),
-                q, X->exp - CONST_PI2.exp);
-      tmp.exp = CONST_PI2.exp;
-      normalize(&tmp);
-      reg_move(&tmp, X);
-    }
-
-#ifdef FPTAN
-  if ( even == FPTAN )
-    {
-      if ( ((X->exp >= EXP_BIAS) ||
-           ((X->exp == EXP_BIAS-1)
-            && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) )
-       even = FCOS;
-      else
-       even = 0;
+                q, exponent(st0_ptr) - exponent(&CONST_PI2));
+      setexponent16(&tmp, exponent(&CONST_PI2));
+      st0_tag = FPU_normalize(&tmp, 0);  /* Can't overflow */
+      FPU_copy_to_reg0(&tmp, st0_tag);
     }
-#endif /* FPTAN */
 
   if ( (even && !(q & 1)) || (!even && (q & 1)) )
     {
-      reg_sub(&CONST_PI2, X, X, FULL_PRECISION);
+      st0_tag = FPU_sub(REV|LOADED|TAG_Valid, (int)&CONST_PI2, FULL_PRECISION);
+
 #ifdef BETTER_THAN_486
       /* So far, the results are exact but based upon a 64 bit
         precision approximation to pi/2. The technique used
         now is equivalent to using an approximation to pi/2 which
         is accurate to about 128 bits. */
-      if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) )
+      if ( (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64) || (q > 1) )
        {
-         /* This code gives the effect of having p/2 to better than
+         /* This code gives the effect of having pi/2 to better than
             128 bits precision. */
+
          significand(&tmp) = q + 1;
-         tmp.exp = EXP_BIAS + 63;
-         tmp.tag = TW_Valid;
-         normalize(&tmp);
-         reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
-         reg_add(X, &tmp,  X, FULL_PRECISION);
-         if ( X->sign == SIGN_NEG )
+         setexponent16(&tmp, 63);
+         FPU_normalize(&tmp, 0);   /* Can't overflow */
+         tmptag =
+           FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION, SIGN_POS,
+                     exponent(&CONST_PI2extra) + exponent(&tmp));
+         setsign(&tmp, getsign(&CONST_PI2extra));
+         st0_tag = FPU_add(&tmp, tmptag, 0, FULL_PRECISION);
+         if ( signnegative(st0_ptr) )
            {
              /* CONST_PI2extra is negative, so the result of the addition
                 can be negative. This means that the argument is actually
                 in a different quadrant. The correction is always < pi/2,
                 so it can't overflow into yet another quadrant. */
-             X->sign = SIGN_POS;
+             setpositive(st0_ptr);
              q++;
            }
        }
@@ -114,33 +107,40 @@ static int trig_arg(FPU_REG *X, int even)
         precision approximation to pi/2. The technique used
         now is equivalent to using an approximation to pi/2 which
         is accurate to about 128 bits. */
-      if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) )
+      if ( ((q > 0) && (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64))
+          || (q > 1) )
        {
          /* This code gives the effect of having p/2 to better than
             128 bits precision. */
+
          significand(&tmp) = q;
-         tmp.exp = EXP_BIAS + 63;
-         tmp.tag = TW_Valid;
-         normalize(&tmp);
-         reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
-         reg_sub(X, &tmp, X, FULL_PRECISION);
-         if ( (X->exp == CONST_PI2.exp) &&
-             ((X->sigh > CONST_PI2.sigh)
-              || ((X->sigh == CONST_PI2.sigh)
-                  && (X->sigl > CONST_PI2.sigl))) )
+         setexponent16(&tmp, 63);
+         FPU_normalize(&tmp, 0);         /* This must return TAG_Valid */
+         tmptag = FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION,
+                            SIGN_POS,
+                            exponent(&CONST_PI2extra) + exponent(&tmp));
+         setsign(&tmp, getsign(&CONST_PI2extra));
+         st0_tag = FPU_sub(LOADED|(tmptag & 0x0f), (int)&tmp,
+                           FULL_PRECISION);
+         if ( (exponent(st0_ptr) == exponent(&CONST_PI2)) &&
+             ((st0_ptr->sigh > CONST_PI2.sigh)
+              || ((st0_ptr->sigh == CONST_PI2.sigh)
+                  && (st0_ptr->sigl > CONST_PI2.sigl))) )
            {
              /* CONST_PI2extra is negative, so the result of the
                 subtraction can be larger than pi/2. This means
                 that the argument is actually in a different quadrant.
                 The correction is always < pi/2, so it can't overflow
                 into yet another quadrant. */
-             reg_sub(&CONST_PI, X, X, FULL_PRECISION);
+             st0_tag = FPU_sub(REV|LOADED|TAG_Valid, (int)&CONST_PI2,
+                               FULL_PRECISION);
              q++;
            }
        }
     }
 #endif /* BETTER_THAN_486 */
 
+  FPU_settag0(st0_tag);
   control_word = old_cw;
   partial_status = saved_status & ~SW_C2;     /* Reduction complete. */
 
@@ -149,57 +149,56 @@ static int trig_arg(FPU_REG *X, int even)
 
 
 /* Convert a long to register */
-void convert_l2reg(long const *arg, FPU_REG *dest)
+static void convert_l2reg(long const *arg, int deststnr)
 {
+  int tag;
   long num = *arg;
+  u_char sign;
+  FPU_REG *dest = &st(deststnr);
 
   if (num == 0)
-    { reg_move(&CONST_Z, dest); return; }
+    {
+      FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
+      return;
+    }
 
   if (num > 0)
-    dest->sign = SIGN_POS;
+    { sign = SIGN_POS; }
   else
-    { num = -num; dest->sign = SIGN_NEG; }
+    { num = -num; sign = SIGN_NEG; }
 
   dest->sigh = num;
   dest->sigl = 0;
-  dest->exp = EXP_BIAS + 31;
-  dest->tag = TW_Valid;
-  normalize(dest);
+  setexponent16(dest, 31);
+  tag = FPU_normalize(dest, sign);
+  FPU_settagi(deststnr, tag);
+  setsign(dest, sign);
+  return;
 }
 
 
-static void single_arg_error(FPU_REG *st0_ptr)
+static void single_arg_error(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  switch ( st0_ptr->tag )
-    {
-    case TW_NaN:
-      if ( !(st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
-       {
-         EXCEPTION(EX_Invalid);
-         if ( control_word & CW_Invalid )
-           st0_ptr->sigh |= 0x40000000;          /* Convert to a QNaN */
-       }
-      break;              /* return with a NaN in st(0) */
-    case TW_Empty:
-      stack_underflow();  /* Puts a QNaN in st(0) */
-      break;
+  if ( st0_tag == TAG_Empty )
+    FPU_stack_underflow();  /* Puts a QNaN in st(0) */
+  else if ( st0_tag == TW_NaN )
+    real_1op_NaN(st0_ptr);       /* return with a NaN in st(0) */
 #ifdef PARANOID
-    default:
-      EXCEPTION(EX_INTERNAL|0x0112);
+  else
+    EXCEPTION(EX_INTERNAL|0x0112);
 #endif /* PARANOID */
-    }
 }
 
 
-static void single_arg_2_error(FPU_REG *st0_ptr)
+static void single_arg_2_error(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  FPU_REG *st_new_ptr;
+  int isNaN;
 
-  switch ( st0_ptr->tag )
+  switch ( st0_tag )
     {
     case TW_NaN:
-      if ( !(st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
+      isNaN = (exponent(st0_ptr) == EXP_OVER) && (st0_ptr->sigh & 0x80000000);
+      if ( isNaN && !(st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
        {
          EXCEPTION(EX_Invalid);
          if ( control_word & CW_Invalid )
@@ -207,17 +206,27 @@ static void single_arg_2_error(FPU_REG *st0_ptr)
              /* The masked response */
              /* Convert to a QNaN */
              st0_ptr->sigh |= 0x40000000;
-             st_new_ptr = &st(-1);
              push();
-             reg_move(&st(1), st_new_ptr);
+             FPU_copy_to_reg0(st0_ptr, TAG_Special);
            }
        }
-      else
+      else if ( isNaN )
        {
          /* A QNaN */
-         st_new_ptr = &st(-1);
          push();
-         reg_move(&st(1), st_new_ptr);
+         FPU_copy_to_reg0(st0_ptr, TAG_Special);
+       }
+      else
+       {
+         /* pseudoNaN or other unsupported */
+         EXCEPTION(EX_Invalid);
+         if ( control_word & CW_Invalid )
+           {
+             /* The masked response */
+             FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
+             push();
+             FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
+           }
        }
       break;              /* return with a NaN in st(0) */
 #ifdef PARANOID
@@ -230,92 +239,88 @@ static void single_arg_2_error(FPU_REG *st0_ptr)
 
 /*---------------------------------------------------------------------------*/
 
-static void f2xm1(FPU_REG *st0_ptr)
+static void f2xm1(FPU_REG *st0_ptr, u_char tag)
 {
+  FPU_REG a;
+
   clear_C1();
-  switch ( st0_ptr->tag )
-    {
-    case TW_Valid:
-      {
-       if ( st0_ptr->exp >= 0 )
-         {
-           /* For an 80486 FPU, the result is undefined. */
-         }
-#ifdef DENORM_OPERAND
-       else if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-         return;
-#endif /* DENORM_OPERAND */
-       else
-         {
-           /* poly_2xm1(x) requires 0 < x < 1. */
-           poly_2xm1(st0_ptr, st0_ptr);
-         }
-       if ( st0_ptr->exp <= EXP_UNDER )
-         {
-           /* A denormal result has been produced.
-              Precision must have been lost, this is always
-              an underflow. */
-           arith_underflow(st0_ptr);
-         }
-       set_precision_flag_up();   /* 80486 appears to always do this */
-       return;
-      }
-    case TW_Zero:
+
+  if ( tag == TAG_Valid )
+    {
+      /* For an 80486 FPU, the result is undefined if the arg is >= 1.0 */
+      if ( exponent(st0_ptr) < 0 )
+       {
+       denormal_arg:
+
+         FPU_to_exp16(st0_ptr, &a);
+
+         /* poly_2xm1(x) requires 0 < st(0) < 1. */
+         poly_2xm1(getsign(st0_ptr), &a, st0_ptr);
+       }
+      set_precision_flag_up();   /* 80486 appears to always do this */
       return;
+    }
+
+  if ( tag == TAG_Zero )
+    return;
+
+  if ( tag == TAG_Special )
+    tag = FPU_Special(st0_ptr);
+
+  switch ( tag )
+    {
+    case TW_Denormal:
+      if ( denormal_operand() < 0 )
+       return;
+      goto denormal_arg;
     case TW_Infinity:
-      if ( st0_ptr->sign == SIGN_NEG )
+      if ( signnegative(st0_ptr) )
        {
          /* -infinity gives -1 (p16-10) */
-         reg_move(&CONST_1, st0_ptr);
-         st0_ptr->sign = SIGN_NEG;
+         FPU_copy_to_reg0(&CONST_1, TAG_Valid);
+         setnegative(st0_ptr);
        }
       return;
     default:
-      single_arg_error(st0_ptr);
+      single_arg_error(st0_ptr, tag);
     }
 }
 
 
-static void fptan(FPU_REG *st0_ptr)
+static void fptan(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st_new_ptr;
   int q;
-  char arg_sign = st0_ptr->sign;
+  u_char arg_sign = getsign(st0_ptr);
 
   /* Stack underflow has higher priority */
-  if ( st0_tag == TW_Empty )
+  if ( st0_tag == TAG_Empty )
     {
-      stack_underflow();  /* Puts a QNaN in st(0) */
+      FPU_stack_underflow();  /* Puts a QNaN in st(0) */
       if ( control_word & CW_Invalid )
        {
          st_new_ptr = &st(-1);
          push();
-         stack_underflow();  /* Puts a QNaN in the new st(0) */
+         FPU_stack_underflow();  /* Puts a QNaN in the new st(0) */
        }
       return;
     }
 
   if ( STACK_OVERFLOW )
-    { stack_overflow(); return; }
+    { FPU_stack_overflow(); return; }
 
-  switch ( st0_tag )
+  if ( st0_tag == TAG_Valid )
     {
-    case TW_Valid:
-      if ( st0_ptr->exp > EXP_BIAS - 40 )
+      if ( exponent(st0_ptr) > -40 )
        {
-         st0_ptr->sign = SIGN_POS;
-         if ( (q = trig_arg(st0_ptr, 0)) != -1 )
-           {
-             poly_tan(st0_ptr, st0_ptr);
-             st0_ptr->sign = (q & 1) ^ arg_sign;
-           }
-         else
+         if ( (q = trig_arg(st0_ptr, 0)) == -1 )
            {
              /* Operand is out of range */
-             st0_ptr->sign = arg_sign;         /* restore st(0) */
              return;
            }
+
+         poly_tan(st0_ptr);
+         setsign(st0_ptr, (q & 1) ^ (arg_sign != 0));
          set_precision_flag_up();  /* We do not really know if up or down */
        }
       else
@@ -323,106 +328,134 @@ static void fptan(FPU_REG *st0_ptr)
          /* For a small arg, the result == the argument */
          /* Underflow may happen */
 
-         if ( st0_ptr->exp <= EXP_UNDER )
-           {
-#ifdef DENORM_OPERAND
-             if ( denormal_operand() )
-               return;
-#endif /* DENORM_OPERAND */
-             /* A denormal result has been produced.
-                Precision must have been lost, this is always
-                an underflow. */
-             if ( arith_underflow(st0_ptr) )
-               return;
-           }
-         set_precision_flag_down();  /* Must be down. */
+       denormal_arg:
+
+         FPU_to_exp16(st0_ptr, st0_ptr);
+      
+         st0_tag = FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign);
+         FPU_settag0(st0_tag);
        }
       push();
-      reg_move(&CONST_1, st_new_ptr);
+      FPU_copy_to_reg0(&CONST_1, TAG_Valid);
       return;
-      break;
-    case TW_Infinity:
+    }
+
+  if ( st0_tag == TAG_Zero )
+    {
+      push();
+      FPU_copy_to_reg0(&CONST_1, TAG_Valid);
+      setcc(0);
+      return;
+    }
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+
+  if ( st0_tag == TW_Denormal )
+    {
+      if ( denormal_operand() < 0 )
+       return;
+
+      goto denormal_arg;
+    }
+
+  if ( st0_tag == TW_Infinity )
+    {
       /* The 80486 treats infinity as an invalid operand */
-      arith_invalid(st0_ptr);
-      if ( control_word & CW_Invalid )
+      if ( arith_invalid(0) >= 0 )
        {
          st_new_ptr = &st(-1);
          push();
-         arith_invalid(st_new_ptr);
+         arith_invalid(0);
        }
       return;
-    case TW_Zero:
-      push();
-      reg_move(&CONST_1, st_new_ptr);
-      setcc(0);
-      break;
-    default:
-      single_arg_2_error(st0_ptr);
-      break;
     }
+
+  single_arg_2_error(st0_ptr, st0_tag);
 }
 
 
-static void fxtract(FPU_REG *st0_ptr)
+static void fxtract(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st_new_ptr;
+  u_char sign;
   register FPU_REG *st1_ptr = st0_ptr;  /* anticipate */
 
   if ( STACK_OVERFLOW )
-    {  stack_overflow(); return; }
+    {  FPU_stack_overflow(); return; }
+
   clear_C1();
-  if ( !(st0_tag ^ TW_Valid) )
+
+  if ( st0_tag == TAG_Valid )
     {
       long e;
 
-#ifdef DENORM_OPERAND
-      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+      push();
+      sign = getsign(st1_ptr);
+      reg_copy(st1_ptr, st_new_ptr);
+      setexponent16(st_new_ptr, exponent(st_new_ptr));
+
+    denormal_arg:
+
+      e = exponent16(st_new_ptr);
+      convert_l2reg(&e, 1);
+      setexponentpos(st_new_ptr, 0);
+      setsign(st_new_ptr, sign);
+      FPU_settag0(TAG_Valid);       /* Needed if arg was a denormal */
+      return;
+    }
+  else if ( st0_tag == TAG_Zero )
+    {
+      sign = getsign(st0_ptr);
+
+      if ( FPU_divide_by_zero(0, SIGN_NEG) < 0 )
        return;
-#endif /* DENORM_OPERAND */
 
       push();
-      reg_move(st1_ptr, st_new_ptr);
-      st_new_ptr->exp = EXP_BIAS;
-      e = st1_ptr->exp - EXP_BIAS;
-      convert_l2reg(&e, st1_ptr);
+      FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
+      setsign(st_new_ptr, sign);
       return;
     }
-  else if ( st0_tag == TW_Zero )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+
+  if ( st0_tag == TW_Denormal )
     {
-      char sign = st0_ptr->sign;
-      if ( divide_by_zero(SIGN_NEG, st0_ptr) )
+      if (denormal_operand() < 0 )
        return;
+
       push();
-      reg_move(&CONST_Z, st_new_ptr);
-      st_new_ptr->sign = sign;
-      return;
+      sign = getsign(st1_ptr);
+      FPU_to_exp16(st1_ptr, st_new_ptr);
+      goto denormal_arg;
     }
   else if ( st0_tag == TW_Infinity )
     {
-      char sign = st0_ptr->sign;
-      st0_ptr->sign = SIGN_POS;
+      sign = getsign(st0_ptr);
+      setpositive(st0_ptr);
       push();
-      reg_move(&CONST_INF, st_new_ptr);
-      st_new_ptr->sign = sign;
+      FPU_copy_to_reg0(&CONST_INF, TAG_Special);
+      setsign(st_new_ptr, sign);
       return;
     }
   else if ( st0_tag == TW_NaN )
     {
-      if ( real_2op_NaN(st0_ptr, st0_ptr, st0_ptr) )
+      if ( real_1op_NaN(st0_ptr) < 0 )
        return;
+
       push();
-      reg_move(st1_ptr, st_new_ptr);
+      FPU_copy_to_reg0(st0_ptr, TAG_Special);
       return;
     }
-  else if ( st0_tag == TW_Empty )
+  else if ( st0_tag == TAG_Empty )
     {
       /* Is this the correct behaviour? */
       if ( control_word & EX_Invalid )
        {
-         stack_underflow();
+         FPU_stack_underflow();
          push();
-         stack_underflow();
+         FPU_stack_underflow();
        }
       else
        EXCEPTION(EX_StackUnder);
@@ -434,193 +467,233 @@ static void fxtract(FPU_REG *st0_ptr)
 }
 
 
-static void fdecstp(FPU_REG *st0_ptr)
+static void fdecstp(void)
 {
   clear_C1();
-  top--;  /* st0_ptr will be fixed in math_emulate() before the next instr */
+  FPU_top--;
 }
 
-static void fincstp(FPU_REG *st0_ptr)
+static void fincstp(void)
 {
   clear_C1();
-  top++;  /* st0_ptr will be fixed in math_emulate() before the next instr */
+  FPU_top++;
 }
 
 
-static void fsqrt_(FPU_REG *st0_ptr)
+static void fsqrt_(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
+  int expon;
 
   clear_C1();
-  if ( !(st0_tag ^ TW_Valid) )
+
+  if ( st0_tag == TAG_Valid )
     {
-      int expon;
+      u_char tag;
       
-      if (st0_ptr->sign == SIGN_NEG)
+      if (signnegative(st0_ptr))
        {
-         arith_invalid(st0_ptr);  /* sqrt(negative) is invalid */
+         arith_invalid(0);  /* sqrt(negative) is invalid */
          return;
        }
 
-#ifdef DENORM_OPERAND
-      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-       return;
-#endif /* DENORM_OPERAND */
+      /* make st(0) in  [1.0 .. 4.0) */
+      expon = exponent(st0_ptr);
 
-      expon = st0_ptr->exp - EXP_BIAS;
-      st0_ptr->exp = EXP_BIAS + (expon & 1);  /* make st(0) in  [1.0 .. 4.0) */
-      
-      wm_sqrt(st0_ptr, control_word);  /* Do the computation */
-      
-      st0_ptr->exp += expon >> 1;
-      st0_ptr->sign = SIGN_POS;
+    denormal_arg:
+
+      setexponent16(st0_ptr, (expon & 1));
+
+      /* Do the computation, the sign of the result will be positive. */
+      tag = wm_sqrt(st0_ptr, 0, 0, control_word, SIGN_POS);
+      addexponent(st0_ptr, expon >> 1);
+      FPU_settag0(tag);
+      return;
     }
-  else if ( st0_tag == TW_Zero )
+
+  if ( st0_tag == TAG_Zero )
     return;
-  else if ( st0_tag == TW_Infinity )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+
+  if ( st0_tag == TW_Infinity )
     {
-      if ( st0_ptr->sign == SIGN_NEG )
-       arith_invalid(st0_ptr);  /* sqrt(-Infinity) is invalid */
+      if ( signnegative(st0_ptr) )
+       arith_invalid(0);  /* sqrt(-Infinity) is invalid */
       return;
     }
-  else
-    { single_arg_error(st0_ptr); return; }
+  else if ( st0_tag == TW_Denormal )
+    {
+      if (signnegative(st0_ptr))
+       {
+         arith_invalid(0);  /* sqrt(negative) is invalid */
+         return;
+       }
+
+      if ( denormal_operand() < 0 )
+       return;
+
+      FPU_to_exp16(st0_ptr, st0_ptr);
+
+      expon = exponent16(st0_ptr);
+
+      goto denormal_arg;
+    }
+
+  single_arg_error(st0_ptr, st0_tag);
 
 }
 
 
-static void frndint_(FPU_REG *st0_ptr)
+static void frndint_(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
-  int flags;
+  int flags, tag;
 
-  if ( !(st0_tag ^ TW_Valid) )
+  if ( st0_tag == TAG_Valid )
     {
-      if (st0_ptr->exp > EXP_BIAS+63)
-       return;
+      u_char sign;
 
-#ifdef DENORM_OPERAND
-      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+    denormal_arg:
+
+      sign = getsign(st0_ptr);
+
+      if (exponent(st0_ptr) > 63)
        return;
-#endif /* DENORM_OPERAND */
+
+      if ( st0_tag == TW_Denormal )
+       {
+         if (denormal_operand() < 0 )
+           return;
+       }
 
       /* Fortunately, this can't overflow to 2^64 */
-      if ( (flags = round_to_int(st0_ptr)) )
+      if ( (flags = FPU_round_to_int(st0_ptr, st0_tag)) )
        set_precision_flag(flags);
 
-      st0_ptr->exp = EXP_BIAS + 63;
-      normalize(st0_ptr);
+      setexponent16(st0_ptr, 63);
+      tag = FPU_normalize(st0_ptr, sign);
+      setsign(st0_ptr, sign);
+      FPU_settag0(tag);
       return;
     }
-  else if ( (st0_tag == TW_Zero) || (st0_tag == TW_Infinity) )
+
+  if ( st0_tag == TAG_Zero )
+    return;
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+
+  if ( st0_tag == TW_Denormal )
+    goto denormal_arg;
+  else if ( st0_tag == TW_Infinity )
     return;
   else
-    single_arg_error(st0_ptr);
+    single_arg_error(st0_ptr, st0_tag);
 }
 
 
-static void fsin(FPU_REG *st0_ptr)
+static int fsin(FPU_REG *st0_ptr, u_char tag)
 {
-  char st0_tag = st0_ptr->tag;
-  char arg_sign = st0_ptr->sign;
+  u_char arg_sign = getsign(st0_ptr);
 
-  if ( st0_tag == TW_Valid )
+  if ( tag == TAG_Valid )
     {
-      FPU_REG rv;
       int q;
 
-      if ( st0_ptr->exp > EXP_BIAS - 40 )
+      if ( exponent(st0_ptr) > -40 )
        {
-         st0_ptr->sign = SIGN_POS;
-         if ( (q = trig_arg(st0_ptr, 0)) != -1 )
+         if ( (q = trig_arg(st0_ptr, 0)) == -1 )
            {
+             /* Operand is out of range */
+             return 1;
+           }
 
-             poly_sine(st0_ptr, &rv);
+         poly_sine(st0_ptr);
+         
+         if (q & 2)
+           changesign(st0_ptr);
 
-             if (q & 2)
-               rv.sign ^= SIGN_POS ^ SIGN_NEG;
-             rv.sign ^= arg_sign;
-             reg_move(&rv, st0_ptr);
+         setsign(st0_ptr, getsign(st0_ptr) ^ arg_sign);
 
-             /* We do not really know if up or down */
-             set_precision_flag_up();
-             return;
-           }
-         else
-           {
-             /* Operand is out of range */
-             st0_ptr->sign = arg_sign;         /* restore st(0) */
-             return;
-           }
+         /* We do not really know if up or down */
+         set_precision_flag_up();
+         return 0;
        }
       else
        {
          /* For a small arg, the result == the argument */
-         /* Underflow may happen */
-
-         if ( st0_ptr->exp <= EXP_UNDER )
-           {
-#ifdef DENORM_OPERAND
-             if ( denormal_operand() )
-               return;
-#endif /* DENORM_OPERAND */
-             /* A denormal result has been produced.
-                Precision must have been lost, this is always
-                an underflow. */
-             arith_underflow(st0_ptr);
-             return;
-           }
-
          set_precision_flag_up();  /* Must be up. */
+         return 0;
        }
     }
-  else if ( st0_tag == TW_Zero )
+
+  if ( tag == TAG_Zero )
     {
       setcc(0);
-      return;
+      return 0;
     }
-  else if ( st0_tag == TW_Infinity )
+
+  if ( tag == TAG_Special )
+    tag = FPU_Special(st0_ptr);
+
+  if ( tag == TW_Denormal )
+    {
+      if ( denormal_operand() < 0 )
+       return 1;
+
+      /* For a small arg, the result == the argument */
+      /* Underflow may happen */
+      FPU_to_exp16(st0_ptr, st0_ptr);
+      
+      tag = FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign);
+
+      FPU_settag0(tag);
+
+      return 0;
+    }
+  else if ( tag == TW_Infinity )
     {
       /* The 80486 treats infinity as an invalid operand */
-      arith_invalid(st0_ptr);
-      return;
+      arith_invalid(0);
+      return 1;
     }
   else
-    single_arg_error(st0_ptr);
+    {
+      single_arg_error(st0_ptr, tag);
+      return 1;
+    }
 }
 
 
-static int f_cos(FPU_REG *arg)
+static int f_cos(FPU_REG *st0_ptr, u_char tag)
 {
-  char arg_sign = arg->sign;
+  u_char st0_sign;
+
+  st0_sign = getsign(st0_ptr);
 
-  if ( arg->tag == TW_Valid )
+  if ( tag == TAG_Valid )
     {
-      FPU_REG rv;
       int q;
 
-      if ( arg->exp > EXP_BIAS - 40 )
+      if ( exponent(st0_ptr) > -40 )
        {
-         arg->sign = SIGN_POS;
-         if ( (arg->exp < EXP_BIAS)
-             || ((arg->exp == EXP_BIAS)
-                 && (significand(arg) <= 0xc90fdaa22168c234LL)) )
+         if ( (exponent(st0_ptr) < 0)
+             || ((exponent(st0_ptr) == 0)
+                 && (significand(st0_ptr) <= 0xc90fdaa22168c234LL)) )
            {
-             poly_cos(arg, &rv);
-             reg_move(&rv, arg);
+             poly_cos(st0_ptr);
 
              /* We do not really know if up or down */
              set_precision_flag_down();
          
              return 0;
            }
-         else if ( (q = trig_arg(arg, FCOS)) != -1 )
+         else if ( (q = trig_arg(st0_ptr, FCOS)) != -1 )
            {
-             poly_sine(arg, &rv);
+             poly_sine(st0_ptr);
 
              if ((q+1) & 2)
-               rv.sign ^= SIGN_POS ^ SIGN_NEG;
-             reg_move(&rv, arg);
+               changesign(st0_ptr);
 
              /* We do not really know if up or down */
              set_precision_flag_down();
@@ -630,19 +703,15 @@ static int f_cos(FPU_REG *arg)
          else
            {
              /* Operand is out of range */
-             arg->sign = arg_sign;         /* restore st(0) */
              return 1;
            }
        }
       else
        {
-#ifdef DENORM_OPERAND
-         if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) )
-           return 1;
-#endif /* DENORM_OPERAND */
+       denormal_arg:
 
          setcc(0);
-         reg_move(&CONST_1, arg);
+         FPU_copy_to_reg0(&CONST_1, TAG_Valid);
 #ifdef PECULIAR_486
          set_precision_flag_down();  /* 80486 appears to do this. */
 #else
@@ -651,79 +720,99 @@ static int f_cos(FPU_REG *arg)
          return 0;
        }
     }
-  else if ( arg->tag == TW_Zero )
+  else if ( tag == TAG_Zero )
     {
-      reg_move(&CONST_1, arg);
+      FPU_copy_to_reg0(&CONST_1, TAG_Valid);
       setcc(0);
       return 0;
     }
-  else if ( arg->tag == TW_Infinity )
+
+  if ( tag == TAG_Special )
+    tag = FPU_Special(st0_ptr);
+
+  if ( tag == TW_Denormal )
+    {
+      if ( denormal_operand() < 0 )
+       return 1;
+
+      goto denormal_arg;
+    }
+  else if ( tag == TW_Infinity )
     {
       /* The 80486 treats infinity as an invalid operand */
-      arith_invalid(arg);
+      arith_invalid(0);
       return 1;
     }
   else
     {
-      single_arg_error(arg);  /* requires arg == &st(0) */
+      single_arg_error(st0_ptr, tag);  /* requires st0_ptr == &st(0) */
       return 1;
     }
 }
 
 
-static void fcos(FPU_REG *st0_ptr)
+static void fcos(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  f_cos(st0_ptr);
+  f_cos(st0_ptr, st0_tag);
 }
 
 
-static void fsincos(FPU_REG *st0_ptr)
+static void fsincos(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st_new_ptr;
   FPU_REG arg;
+  u_char tag;
 
   /* Stack underflow has higher priority */
-  if ( st0_tag == TW_Empty )
+  if ( st0_tag == TAG_Empty )
     {
-      stack_underflow();  /* Puts a QNaN in st(0) */
+      FPU_stack_underflow();  /* Puts a QNaN in st(0) */
       if ( control_word & CW_Invalid )
        {
          st_new_ptr = &st(-1);
          push();
-         stack_underflow();  /* Puts a QNaN in the new st(0) */
+         FPU_stack_underflow();  /* Puts a QNaN in the new st(0) */
        }
       return;
     }
 
   if ( STACK_OVERFLOW )
-    { stack_overflow(); return; }
+    { FPU_stack_overflow(); return; }
+
+  if ( st0_tag == TAG_Special )
+    tag = FPU_Special(st0_ptr);
+  else
+    tag = st0_tag;
 
-  if ( st0_tag == TW_NaN )
+  if ( tag == TW_NaN )
     {
-      single_arg_2_error(st0_ptr);
+      single_arg_2_error(st0_ptr, TW_NaN);
       return;
     }
-  else if ( st0_tag == TW_Infinity )
+  else if ( tag == TW_Infinity )
     {
       /* The 80486 treats infinity as an invalid operand */
-      if ( !arith_invalid(st0_ptr) )
+      if ( arith_invalid(0) >= 0 )
        {
-         /* unmasked response */
+         /* Masked response */
          push();
-         arith_invalid(st_new_ptr);
+         arith_invalid(0);
        }
       return;
     }
 
-  reg_move(st0_ptr,&arg);
-  if ( !f_cos(&arg) )
+  reg_copy(st0_ptr, &arg);
+  if ( !fsin(st0_ptr, st0_tag) )
     {
-      fsin(st0_ptr);
       push();
-      reg_move(&arg,st_new_ptr);
+      FPU_copy_to_reg0(&arg, st0_tag);
+      f_cos(&st(0), st0_tag);
+    }
+  else
+    {
+      /* An error, so restore st(0) */
+      FPU_copy_to_reg0(&arg, st0_tag);
     }
-
 }
 
 
@@ -740,18 +829,29 @@ static void rem_kernel(unsigned long long st0, unsigned long long *y,
                       unsigned long long st1,
                       unsigned long long q, int n)
 {
+  int dummy;
   unsigned long long x;
 
   x = st0 << n;
 
   /* Do the required multiplication and subtraction in the one operation */
-  asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1;
-                 movl %3,%%eax; mull %4; subl %%eax,%1;
-                 movl %2,%%eax; mull %5; subl %%eax,%1;"
-               :"=m" (x), "=m" (((unsigned *)&x)[1])
-               :"m" (st1),"m" (((unsigned *)&st1)[1]),
-                "m" (q),"m" (((unsigned *)&q)[1])
-               :"%ax","%dx");
+
+  /* lsw x -= lsw st1 * lsw q */
+  asm volatile ("mull %4; subl %%eax,%0; sbbl %%edx,%1"
+               :"=m" (((unsigned *)&x)[0]), "=m" (((unsigned *)&x)[1]),
+               "=a" (dummy)
+               :"2" (((unsigned *)&st1)[0]), "m" (((unsigned *)&q)[0])
+               :"%dx");
+  /* msw x -= msw st1 * lsw q */
+  asm volatile ("mull %3; subl %%eax,%0"
+               :"=m" (((unsigned *)&x)[1]), "=a" (dummy)
+               :"1" (((unsigned *)&st1)[1]), "m" (((unsigned *)&q)[0])
+               :"%dx");
+  /* msw x -= lsw st1 * msw q */
+  asm volatile ("mull %3; subl %%eax,%0"
+               :"=m" (((unsigned *)&x)[1]), "=a" (dummy)
+               :"1" (((unsigned *)&st1)[0]), "m" (((unsigned *)&q)[1])
+               :"%dx");
 
   *y = x;
 }
@@ -760,27 +860,31 @@ static void rem_kernel(unsigned long long st0, unsigned long long *y,
 /* Remainder of st(0) / st(1) */
 /* This routine produces exact results, i.e. there is never any
    rounding or truncation, etc of the result. */
-static void do_fprem(FPU_REG *st0_ptr, int round)
+static void do_fprem(FPU_REG *st0_ptr, u_char st0_tag, int round)
 {
   FPU_REG *st1_ptr = &st(1);
-  char st1_tag = st1_ptr->tag;
-  char st0_tag = st0_ptr->tag;
-  char sign = st0_ptr->sign;
+  u_char st1_tag = FPU_gettagi(1);
 
-  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+  if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) )
     {
-      FPU_REG tmp;
-      int old_cw = control_word;
-      int expdif = st0_ptr->exp - st1_ptr->exp;
+      FPU_REG tmp, st0, st1;
+      u_char st0_sign, st1_sign;
+      u_char tmptag;
+      int tag;
+      int old_cw;
+      int expdif;
       long long q;
       unsigned short saved_status;
-      int cc = 0;
+      int cc;
 
-#ifdef DENORM_OPERAND
-      if ( ((st0_ptr->exp <= EXP_UNDER) ||
-           (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
-       return;
-#endif /* DENORM_OPERAND */
+    fprem_valid:
+      /* Convert registers for internal use. */
+      st0_sign = FPU_to_exp16(st0_ptr, &st0);
+      st1_sign = FPU_to_exp16(st1_ptr, &st1);
+      expdif = exponent16(&st0) - exponent16(&st1);
+
+      old_cw = control_word;
+      cc = 0;
 
       /* We want the status following the denorm tests, but don't want
         the status changed by the arithmetic operations. */
@@ -788,51 +892,54 @@ static void do_fprem(FPU_REG *st0_ptr, int round)
       control_word &= ~CW_RC;
       control_word |= RC_CHOP;
 
-      if (expdif < 64)
+      if ( expdif < 64 )
        {
          /* This should be the most common case */
 
          if ( expdif > -2 )
            {
-             reg_div(st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
+             u_char sign = st0_sign ^ st1_sign;
+             tag = FPU_u_div(&st0, &st1, &tmp,
+                             PR_64_BITS | RC_CHOP | 0x3f,
+                             sign);
+             setsign(&tmp, sign);
 
-             if ( tmp.exp >= EXP_BIAS )
+             if ( exponent(&tmp) >= 0 )
                {
-                 round_to_int(&tmp);  /* Fortunately, this can't overflow
-                                         to 2^64 */
+                 FPU_round_to_int(&tmp, tag);  /* Fortunately, this can't
+                                                  overflow to 2^64 */
                  q = significand(&tmp);
 
-                 rem_kernel(significand(st0_ptr),
+                 rem_kernel(significand(&st0),
                             &significand(&tmp),
-                            significand(st1_ptr),
+                            significand(&st1),
                             q, expdif);
 
-                 tmp.exp = st1_ptr->exp;
+                 setexponent16(&tmp, exponent16(&st1));
                }
              else
                {
-                 reg_move(st0_ptr, &tmp);
+                 reg_copy(&st0, &tmp);
                  q = 0;
                }
-             tmp.sign = sign;
 
              if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) )
                {
                  /* We may need to subtract st(1) once more,
                     to get a result <= 1/2 of st(1). */
                  unsigned long long x;
-                 expdif = st1_ptr->exp - tmp.exp;
+                 expdif = exponent16(&st1) - exponent16(&tmp);
                  if ( expdif <= 1 )
                    {
                      if ( expdif == 0 )
-                       x = significand(st1_ptr) - significand(&tmp);
+                       x = significand(&st1) - significand(&tmp);
                      else /* expdif is 1 */
-                       x = (significand(st1_ptr) << 1) - significand(&tmp);
+                       x = (significand(&st1) << 1) - significand(&tmp);
                      if ( (x < significand(&tmp)) ||
                          /* or equi-distant (from 0 & st(1)) and q is odd */
                          ((x == significand(&tmp)) && (q & 1) ) )
                        {
-                         tmp.sign ^= (SIGN_POS^SIGN_NEG);
+                         st0_sign = ! st0_sign;
                          significand(&tmp) = x;
                          q++;
                        }
@@ -855,28 +962,35 @@ static void do_fprem(FPU_REG *st0_ptr, int round)
          /* There is a large exponent difference ( >= 64 ) */
          /* To make much sense, the code in this section should
             be done at high precision. */
-         int exp_1;
+         int exp_1, N;
+         u_char sign;
 
          /* prevent overflow here */
          /* N is 'a number between 32 and 63' (p26-113) */
-         reg_move(st0_ptr, &tmp);
-         tmp.exp = EXP_BIAS + 56;
-         exp_1 = st1_ptr->exp;      st1_ptr->exp = EXP_BIAS;
-         expdif -= 56;
-
-         reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
-         st1_ptr->exp = exp_1;
-
-         round_to_int(&tmp);  /* Fortunately, this can't overflow to 2^64 */
-
-         rem_kernel(significand(st0_ptr),
+         reg_copy(&st0, &tmp);
+         tmptag = st0_tag;
+         N = (expdif & 0x0000001f) + 32;  /* This choice gives results
+                                             identical to an AMD 486 */
+         setexponent16(&tmp, N);
+         exp_1 = exponent16(&st1);
+         setexponent16(&st1, 0);
+         expdif -= N;
+
+         sign = getsign(&tmp) ^ st1_sign;
+         tag = FPU_u_div(&tmp, &st1, &tmp, PR_64_BITS | RC_CHOP | 0x3f,
+                         sign);
+         setsign(&tmp, sign);
+
+         FPU_round_to_int(&tmp, tag);  /* Fortunately, this can't
+                                          overflow to 2^64 */
+
+         rem_kernel(significand(&st0),
                     &significand(&tmp),
-                    significand(st1_ptr),
+                    significand(&st1),
                     significand(&tmp),
-                    tmp.exp - EXP_BIAS
+                    exponent(&tmp)
                     ); 
-         tmp.exp = exp_1 + expdif;
-         tmp.sign = sign;
+         setexponent16(&tmp, exp_1 + expdif);
 
          /* It is possible for the operation to be complete here.
             What does the IEEE standard say? The Intel 80486 manual
@@ -888,8 +1002,8 @@ static void do_fprem(FPU_REG *st0_ptr, int round)
              /* The result is zero */
              control_word = old_cw;
              partial_status = saved_status;
-             reg_move(&CONST_Z, st0_ptr);
-             st0_ptr->sign = sign;
+             FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
+             setsign(&st0, st0_sign);
 #ifdef PECULIAR_486
              setcc(SW_C2);
 #else
@@ -902,52 +1016,82 @@ static void do_fprem(FPU_REG *st0_ptr, int round)
 
       control_word = old_cw;
       partial_status = saved_status;
-      normalize_nuo(&tmp);
-      reg_move(&tmp, st0_ptr);
-      setcc(cc);
+      tag = FPU_normalize_nuo(&tmp);
+      reg_copy(&tmp, st0_ptr);
 
       /* The only condition to be looked for is underflow,
         and it can occur here only if underflow is unmasked. */
-      if ( (st0_ptr->exp <= EXP_UNDER) && (st0_ptr->tag != TW_Zero)
+      if ( (exponent16(&tmp) <= EXP_UNDER) && (tag != TAG_Zero)
          && !(control_word & CW_Underflow) )
-       arith_underflow(st0_ptr);
+       {
+         setcc(cc);
+         tag = arith_underflow(st0_ptr);
+         setsign(st0_ptr, st0_sign);
+         FPU_settag0(tag);
+         return;
+       }
+      else if ( (exponent16(&tmp) > EXP_UNDER) || (tag == TAG_Zero) )
+       {
+         stdexp(st0_ptr);
+         setsign(st0_ptr, st0_sign);
+       }
+      else
+       {
+         tag = FPU_round(st0_ptr, 0, 0, FULL_PRECISION, st0_sign);
+       }
+      FPU_settag0(tag);
+      setcc(cc);
 
       return;
     }
-  else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+  if ( st1_tag == TAG_Special )
+    st1_tag = FPU_Special(st1_ptr);
+
+  if ( ((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
+           || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
+           || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal)) )
     {
-      stack_underflow();
+      if ( denormal_operand() < 0 )
+       return;
+      goto fprem_valid;
+    }
+  else if ( (st0_tag == TAG_Empty) | (st1_tag == TAG_Empty) )
+    {
+      FPU_stack_underflow();
       return;
     }
-  else if ( st0_tag == TW_Zero )
+  else if ( st0_tag == TAG_Zero )
     {
-      if ( st1_tag == TW_Valid )
+      if ( st1_tag == TAG_Valid )
        {
-#ifdef DENORM_OPERAND
-         if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+         setcc(0); return;
+       }
+      else if ( st1_tag == TW_Denormal )
+       {
+         if ( denormal_operand() < 0 )
            return;
-#endif /* DENORM_OPERAND */
-
          setcc(0); return;
        }
-      else if ( st1_tag == TW_Zero )
-       { arith_invalid(st0_ptr); return; } /* fprem(?,0) always invalid */
+      else if ( st1_tag == TAG_Zero )
+       { arith_invalid(0); return; } /* fprem(?,0) always invalid */
       else if ( st1_tag == TW_Infinity )
        { setcc(0); return; }
     }
-  else if ( st0_tag == TW_Valid )
+  else if ( (st0_tag == TAG_Valid) || (st0_tag == TW_Denormal) )
     {
-      if ( st1_tag == TW_Zero )
+      if ( st1_tag == TAG_Zero )
        {
-         arith_invalid(st0_ptr); /* fprem(Valid,Zero) is invalid */
+         arith_invalid(0); /* fprem(Valid,Zero) is invalid */
          return;
        }
       else if ( st1_tag != TW_NaN )
        {
-#ifdef DENORM_OPERAND
-         if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+         if ( ((st0_tag == TW_Denormal) || (st1_tag == TW_Denormal))
+              && (denormal_operand() < 0) )
            return;
-#endif /* DENORM_OPERAND */
 
          if ( st1_tag == TW_Infinity )
            {
@@ -960,729 +1104,715 @@ static void do_fprem(FPU_REG *st0_ptr, int round)
     {
       if ( st1_tag != TW_NaN )
        {
-         arith_invalid(st0_ptr); /* fprem(Infinity,?) is invalid */
+         arith_invalid(0); /* fprem(Infinity,?) is invalid */
          return;
        }
     }
 
-  /* One of the registers must contain a NaN is we got here. */
+  /* One of the registers must contain a NaN if we got here. */
 
 #ifdef PARANOID
   if ( (st0_tag != TW_NaN) && (st1_tag != TW_NaN) )
       EXCEPTION(EX_INTERNAL | 0x118);
 #endif /* PARANOID */
 
-  real_2op_NaN(st1_ptr, st0_ptr, st0_ptr);
+  real_2op_NaN(st1_ptr, st1_tag, 0, st1_ptr);
 
 }
 
 
 /* ST(1) <- ST(1) * log ST;  pop ST */
-static void fyl2x(FPU_REG *st0_ptr)
+static void fyl2x(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st1_ptr = &st(1), exponent;
-  char st1_tag = st1_ptr->tag;
-  int e;
+  u_char st1_tag = FPU_gettagi(1);
+  u_char sign;
+  int e, tag;
 
   clear_C1();
-  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+
+  if ( (st0_tag == TAG_Valid) && (st1_tag == TAG_Valid) )
     {
-      if ( st0_ptr->sign == SIGN_POS )
+    both_valid:
+      /* Both regs are Valid or Denormal */
+      if ( signpositive(st0_ptr) )
        {
-#ifdef DENORM_OPERAND
-         if ( ((st0_ptr->exp <= EXP_UNDER) ||
-               (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
-           return;
-#endif /* DENORM_OPERAND */
+         if ( st0_tag == TW_Denormal )
+           FPU_to_exp16(st0_ptr, st0_ptr);
+         else
+           /* Convert st(0) for internal use. */
+           setexponent16(st0_ptr, exponent(st0_ptr));
 
          if ( (st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0) )
            {
              /* Special case. The result can be precise. */
-             e = st0_ptr->exp - EXP_BIAS;
-             if ( e > 0 )
+             u_char esign;
+             e = exponent16(st0_ptr);
+             if ( e >= 0 )
                {
                  exponent.sigh = e;
-                 exponent.sign = SIGN_POS;
+                 esign = SIGN_POS;
                }
              else
                {
                  exponent.sigh = -e;
-                 exponent.sign = SIGN_NEG;
+                 esign = SIGN_NEG;
                }
              exponent.sigl = 0;
-             exponent.exp = EXP_BIAS + 31;
-             exponent.tag = TW_Valid;
-             normalize_nuo(&exponent);
-             reg_mul(&exponent, st1_ptr, st1_ptr, FULL_PRECISION);
+             setexponent16(&exponent, 31);
+             tag = FPU_normalize_nuo(&exponent);
+             stdexp(&exponent);
+             setsign(&exponent, esign);
+             tag = FPU_mul(&exponent, tag, 1, FULL_PRECISION);
+             if ( tag >= 0 )
+               FPU_settagi(1, tag);
            }
          else
            {
              /* The usual case */
-             poly_l2(st0_ptr, st1_ptr, st1_ptr);
-             if ( st1_ptr->exp <= EXP_UNDER )
-               {
-                 /* A denormal result has been produced.
-                    Precision must have been lost, this is always
-                    an underflow. */
-                 arith_underflow(st1_ptr);
-               }
+             sign = getsign(st1_ptr);
+             if ( st1_tag == TW_Denormal )
+               FPU_to_exp16(st1_ptr, st1_ptr);
              else
-               set_precision_flag_up();  /* 80486 appears to always do this */
+               /* Convert st(1) for internal use. */
+               setexponent16(st1_ptr, exponent(st1_ptr));
+             poly_l2(st0_ptr, st1_ptr, sign);
            }
-         pop();
-         return;
        }
       else
        {
          /* negative */
-         if ( !arith_invalid(st1_ptr) )
-           pop();
-         return;
+         if ( arith_invalid(1) < 0 )
+           return;
        }
-    }
-  else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
-    {
-      stack_underflow_pop(1);
+
+      FPU_pop();
+
       return;
     }
-  else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+  if ( st1_tag == TAG_Special )
+    st1_tag = FPU_Special(st1_ptr);
+
+  if ( (st0_tag == TAG_Empty) || (st1_tag == TAG_Empty) )
     {
-      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-       pop();
+      FPU_stack_underflow_pop(1);
       return;
     }
-  else if ( (st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) )
+  else if ( (st0_tag <= TW_Denormal) && (st1_tag <= TW_Denormal) )
     {
-      /* one of the args is zero, the other valid, or both zero */
-      if ( st0_tag == TW_Zero )
+      if ( st0_tag == TAG_Zero )
        {
-         if ( st1_tag == TW_Zero )
+         if ( st1_tag == TAG_Zero )
            {
              /* Both args zero is invalid */
-             if ( !arith_invalid(st1_ptr) )
-               pop();
-           }
-#ifdef PECULIAR_486
-         /* This case is not specifically covered in the manual,
-            but divide-by-zero would seem to be the best response.
-            However, a real 80486 does it this way... */
-         else if ( st0_ptr->tag == TW_Infinity )
-           {
-             reg_move(&CONST_INF, st1_ptr);
-             pop();
+             if ( arith_invalid(1) < 0 )
+               return;
            }
-#endif /* PECULIAR_486 */
          else
            {
-             if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) )
-               pop();
+             u_char sign;
+             sign = getsign(st1_ptr)^SIGN_NEG;
+             if ( FPU_divide_by_zero(1, sign) < 0 )
+               return;
+
+             setsign(st1_ptr, sign);
            }
-         return;
        }
-      else
+      else if ( st1_tag == TAG_Zero )
        {
          /* st(1) contains zero, st(0) valid <> 0 */
          /* Zero is the valid answer */
-         char sign = st1_ptr->sign;
-
-         if ( st0_ptr->sign == SIGN_NEG )
+         sign = getsign(st1_ptr);
+         
+         if ( signnegative(st0_ptr) )
            {
              /* log(negative) */
-             if ( !arith_invalid(st1_ptr) )
-               pop();
-             return;
+             if ( arith_invalid(1) < 0 )
+               return;
            }
-
-#ifdef DENORM_OPERAND
-         if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+         else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
            return;
-#endif /* DENORM_OPERAND */
+         else
+           {
+             if ( exponent(st0_ptr) < 0 )
+               sign ^= SIGN_NEG;
 
-         if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS;
-         pop(); st0_ptr = &st(0);
-         reg_move(&CONST_Z, st0_ptr);
-         st0_ptr->sign = sign;
-         return;
+             FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
+             setsign(st1_ptr, sign);
+           }
+       }
+      else
+       {
+         /* One or both operands are denormals. */
+         if ( denormal_operand() < 0 )
+           return;
+         goto both_valid;
        }
     }
+  else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
+    {
+      if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 )
+       return;
+    }
   /* One or both arg must be an infinity */
   else if ( st0_tag == TW_Infinity )
     {
-      if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) )
+      if ( (signnegative(st0_ptr)) || (st1_tag == TAG_Zero) )
        {
          /* log(-infinity) or 0*log(infinity) */
-         if ( !arith_invalid(st1_ptr) )
-           pop();
-         return;
+         if ( arith_invalid(1) < 0 )
+           return;
        }
       else
        {
-         char sign = st1_ptr->sign;
+         u_char sign = getsign(st1_ptr);
 
-#ifdef DENORM_OPERAND
-         if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+         if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) )
            return;
-#endif /* DENORM_OPERAND */
 
-         pop(); st0_ptr = &st(0);
-         reg_move(&CONST_INF, st0_ptr);
-         st0_ptr->sign = sign;
-         return;
+         FPU_copy_to_reg1(&CONST_INF, TAG_Special);
+         setsign(st1_ptr, sign);
        }
     }
   /* st(1) must be infinity here */
-  else if ( (st0_tag == TW_Valid) && (st0_ptr->sign == SIGN_POS) )
+  else if ( ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal))
+           && ( signpositive(st0_ptr) ) )
     {
-      if ( st0_ptr->exp >= EXP_BIAS )
+      if ( exponent(st0_ptr) >= 0 )
        {
-         if ( (st0_ptr->exp == EXP_BIAS) &&
+         if ( (exponent(st0_ptr) == 0) &&
              (st0_ptr->sigh == 0x80000000) &&
              (st0_ptr->sigl == 0) )
            {
              /* st(0) holds 1.0 */
              /* infinity*log(1) */
-             if ( !arith_invalid(st1_ptr) )
-               pop();
-             return;
+             if ( arith_invalid(1) < 0 )
+               return;
            }
-         /* st(0) is positive and > 1.0 */
-         pop();
+         /* else st(0) is positive and > 1.0 */
        }
       else
        {
          /* st(0) is positive and < 1.0 */
 
-#ifdef DENORM_OPERAND
-         if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+         if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
            return;
-#endif /* DENORM_OPERAND */
 
-         st1_ptr->sign ^= SIGN_NEG;
-         pop();
+         changesign(st1_ptr);
        }
-      return;
     }
   else
     {
       /* st(0) must be zero or negative */
-      if ( st0_ptr->tag == TW_Zero )
+      if ( st0_tag == TAG_Zero )
        {
          /* This should be invalid, but a real 80486 is happy with it. */
+
 #ifndef PECULIAR_486
-         if ( !divide_by_zero(st1_ptr->sign, st1_ptr) )
+         sign = getsign(st1_ptr);
+         if ( FPU_divide_by_zero(1, sign) < 0 )
+           return;
 #endif /* PECULIAR_486 */
-           {
-             st1_ptr->sign ^= SIGN_NEG^SIGN_POS;
-             pop();
-           }
-       }
-      else
-       {
-         /* log(negative) */
-         if ( !arith_invalid(st1_ptr) )
-           pop();
+
+         changesign(st1_ptr);
        }
-      return;
+      else if ( arith_invalid(1) < 0 )   /* log(negative) */
+       return;
     }
+
+  FPU_pop();
 }
 
 
-static void fpatan(FPU_REG *st0_ptr)
+static void fpatan(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st1_ptr = &st(1);
-  char st1_tag = st1_ptr->tag;
+  u_char st1_tag = FPU_gettagi(1);
+  int tag;
 
   clear_C1();
-  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+  if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) )
     {
-#ifdef DENORM_OPERAND
-      if ( ((st0_ptr->exp <= EXP_UNDER) ||
-           (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
-       return;
-#endif /* DENORM_OPERAND */
+    valid_atan:
 
-      poly_atan(st0_ptr, st1_ptr, st1_ptr);
+      poly_atan(st0_ptr, st0_tag, st1_ptr, st1_tag);
 
-      if ( st1_ptr->exp <= EXP_UNDER )
-       {
-         /* A denormal result has been produced.
-            Precision must have been lost.
-            This is by definition an underflow. */
-         arith_underflow(st1_ptr);
-         pop();
-         return;
-       }
+      FPU_pop();
+
+      return;
     }
-  else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+  if ( st1_tag == TAG_Special )
+    st1_tag = FPU_Special(st1_ptr);
+
+  if ( ((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
+           || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
+           || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal)) )
     {
-      stack_underflow_pop(1);
+      if ( denormal_operand() < 0 )
+       return;
+
+      goto valid_atan;
+    }
+  else if ( (st0_tag == TAG_Empty) || (st1_tag == TAG_Empty) )
+    {
+      FPU_stack_underflow_pop(1);
       return;
     }
   else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
     {
-      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-         pop();
+      if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) >= 0 )
+         FPU_pop();
       return;
     }
   else if ( (st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) )
     {
-      char sign = st1_ptr->sign;
+      u_char sign = getsign(st1_ptr);
       if ( st0_tag == TW_Infinity )
        {
          if ( st1_tag == TW_Infinity )
            {
-             if ( st0_ptr->sign == SIGN_POS )
-               { reg_move(&CONST_PI4, st1_ptr); }
+             if ( signpositive(st0_ptr) )
+               {
+                 FPU_copy_to_reg1(&CONST_PI4, TAG_Valid);
+               }
              else
-               reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION);
+               {
+                 setpositive(st1_ptr);
+                 tag = FPU_u_add(&CONST_PI4, &CONST_PI2, st1_ptr,
+                                 FULL_PRECISION, SIGN_POS,
+                                 exponent(&CONST_PI4), exponent(&CONST_PI2));
+                 if ( tag >= 0 )
+                   FPU_settagi(1, tag);
+               }
            }
          else
            {
-#ifdef DENORM_OPERAND
-             if ( st1_tag != TW_Zero )
-               {
-                 if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-                   return;
-               }
-#endif /* DENORM_OPERAND */
+             if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) )
+               return;
 
-             if ( st0_ptr->sign == SIGN_POS )
+             if ( signpositive(st0_ptr) )
                {
-                 reg_move(&CONST_Z, st1_ptr);
-                 st1_ptr->sign = sign;   /* An 80486 preserves the sign */
-                 pop();
+                 FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
+                 setsign(st1_ptr, sign);   /* An 80486 preserves the sign */
+                 FPU_pop();
                  return;
                }
              else
-               reg_move(&CONST_PI, st1_ptr);
+               {
+                 FPU_copy_to_reg1(&CONST_PI, TAG_Valid);
+               }
            }
        }
       else
        {
          /* st(1) is infinity, st(0) not infinity */
-#ifdef DENORM_OPERAND
-         if ( st0_tag != TW_Zero )
-           {
-             if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-               return;
-           }
-#endif /* DENORM_OPERAND */
+         if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
+           return;
 
-         reg_move(&CONST_PI2, st1_ptr);
+         FPU_copy_to_reg1(&CONST_PI2, TAG_Valid);
        }
-      st1_ptr->sign = sign;
+      setsign(st1_ptr, sign);
     }
-  else if ( st1_tag == TW_Zero )
+  else if ( st1_tag == TAG_Zero )
     {
       /* st(0) must be valid or zero */
-      char sign = st1_ptr->sign;
+      u_char sign = getsign(st1_ptr);
+
+      if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
+       return;
 
-#ifdef DENORM_OPERAND
-      if ( st0_tag != TW_Zero )
+      if ( signpositive(st0_ptr) )
        {
-         if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-           return;
+         /* An 80486 preserves the sign */
+         FPU_pop();
+         return;
        }
-#endif /* DENORM_OPERAND */
 
-      if ( st0_ptr->sign == SIGN_POS )
-       { /* An 80486 preserves the sign */ pop(); return; }
-      else
-       reg_move(&CONST_PI, st1_ptr);
-      st1_ptr->sign = sign;
+      FPU_copy_to_reg1(&CONST_PI, TAG_Valid);
+      setsign(st1_ptr, sign);
     }
-  else if ( st0_tag == TW_Zero )
+  else if ( st0_tag == TAG_Zero )
     {
-      /* st(1) must be TW_Valid here */
-      char sign = st1_ptr->sign;
+      /* st(1) must be TAG_Valid here */
+      u_char sign = getsign(st1_ptr);
 
-#ifdef DENORM_OPERAND
-      if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+      if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) )
        return;
-#endif /* DENORM_OPERAND */
 
-      reg_move(&CONST_PI2, st1_ptr);
-      st1_ptr->sign = sign;
+      FPU_copy_to_reg1(&CONST_PI2, TAG_Valid);
+      setsign(st1_ptr, sign);
     }
 #ifdef PARANOID
   else
     EXCEPTION(EX_INTERNAL | 0x125);
 #endif /* PARANOID */
 
-  pop();
+  FPU_pop();
   set_precision_flag_up();  /* We do not really know if up or down */
 }
 
 
-static void fprem(FPU_REG *st0_ptr)
+static void fprem(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  do_fprem(st0_ptr, RC_CHOP);
+  do_fprem(st0_ptr, st0_tag, RC_CHOP);
 }
 
 
-static void fprem1(FPU_REG *st0_ptr)
+static void fprem1(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  do_fprem(st0_ptr, RC_RND);
+  do_fprem(st0_ptr, st0_tag, RC_RND);
 }
 
 
-static void fyl2xp1(FPU_REG *st0_ptr)
+static void fyl2xp1(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag, sign;
-  FPU_REG *st1_ptr = &st(1);
-  char st1_tag = st1_ptr->tag;
+  u_char sign, sign1;
+  FPU_REG *st1_ptr = &st(1), a, b;
+  u_char st1_tag = FPU_gettagi(1);
 
   clear_C1();
-  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+  if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) )
     {
-#ifdef DENORM_OPERAND
-      if ( ((st0_ptr->exp <= EXP_UNDER) ||
-           (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() )
+    valid_yl2xp1:
+
+      sign = getsign(st0_ptr);
+      sign1 = getsign(st1_ptr);
+
+      FPU_to_exp16(st0_ptr, &a);
+      FPU_to_exp16(st1_ptr, &b);
+
+      if ( poly_l2p1(sign, sign1, &a, &b, st1_ptr) )
        return;
-#endif /* DENORM_OPERAND */
 
-      if ( poly_l2p1(st0_ptr, st1_ptr, st1_ptr) )
-       {
-#ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
-         st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
-#else
-         if ( arith_invalid(st1_ptr) )  /* poly_l2p1() returned invalid */
-           return;
-#endif /* PECULIAR_486 */
-       }
-      if ( st1_ptr->exp <= EXP_UNDER )
-       {
-         /* A denormal result has been produced.
-            Precision must have been lost, this is always
-            an underflow. */
-         sign = st1_ptr->sign;
-         arith_underflow(st1_ptr);
-         st1_ptr->sign = sign;
-       }
-      else
-       set_precision_flag_up();   /* 80486 appears to always do this */
-      pop();
+      FPU_pop();
       return;
     }
-  else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+  if ( st1_tag == TAG_Special )
+    st1_tag = FPU_Special(st1_ptr);
+
+  if ( ((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
+           || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
+           || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal)) )
+    {
+      if ( denormal_operand() < 0 )
+       return;
+
+      goto valid_yl2xp1;
+    }
+  else if ( (st0_tag == TAG_Empty) | (st1_tag == TAG_Empty) )
     {
-      stack_underflow_pop(1);
+      FPU_stack_underflow_pop(1);
       return;
     }
-  else if ( st0_tag == TW_Zero )
+  else if ( st0_tag == TAG_Zero )
     {
-      if ( st1_tag <= TW_Zero )
+      switch ( st1_tag )
        {
-#ifdef DENORM_OPERAND
-         if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
-             (denormal_operand()) )
+       case TW_Denormal:
+         if ( denormal_operand() < 0 )
            return;
-#endif /* DENORM_OPERAND */
 
-         st0_ptr->sign ^= st1_ptr->sign;
-         reg_move(st0_ptr, st1_ptr);
-       }
-      else if ( st1_tag == TW_Infinity )
-       {
+       case TAG_Zero:
+       case TAG_Valid:
+         setsign(st0_ptr, getsign(st0_ptr) ^ getsign(st1_ptr));
+         FPU_copy_to_reg1(st0_ptr, st0_tag);
+         break;
+
+       case TW_Infinity:
          /* Infinity*log(1) */
-         if ( !arith_invalid(st1_ptr) )
-           pop();
-         return;
-       }
-      else if ( st1_tag == TW_NaN )
-       {
-         if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-           pop();
-         return;
-       }
+         if ( arith_invalid(1) < 0 )
+           return;
+         break;
+
+       case TW_NaN:
+         if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 )
+           return;
+         break;
+
+       default:
 #ifdef PARANOID
-      else
-       {
          EXCEPTION(EX_INTERNAL | 0x116);
          return;
-       }
 #endif /* PARANOID */
-      pop(); return;
+         break;
+       }
     }
-  else if ( st0_tag == TW_Valid )
+  else if ( (st0_tag == TAG_Valid) || (st0_tag == TW_Denormal) )
     {
-      if ( st1_tag == TW_Zero )
+      switch ( st1_tag )
        {
-         if ( st0_ptr->sign == SIGN_NEG )
+       case TAG_Zero:
+         if ( signnegative(st0_ptr) )
            {
-             if ( st0_ptr->exp >= EXP_BIAS )
+             if ( exponent(st0_ptr) >= 0 )
                {
                  /* st(0) holds <= -1.0 */
 #ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
-                 st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+                 changesign(st1_ptr);
 #else
-                 if ( arith_invalid(st1_ptr) ) return;
+                 if ( arith_invalid(1) < 0 )
+                   return;
 #endif /* PECULIAR_486 */
-                 pop(); return;
                }
-#ifdef DENORM_OPERAND
-             if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+             else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
                return;
-#endif /* DENORM_OPERAND */
-             st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
-             pop(); return;
+             else
+               changesign(st1_ptr);
            }
-#ifdef DENORM_OPERAND
-         if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+         else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
            return;
-#endif /* DENORM_OPERAND */
-         pop(); return;
-       }
-      if ( st1_tag == TW_Infinity )
-       {
-         if ( st0_ptr->sign == SIGN_NEG )
+         break;
+
+       case TW_Infinity:
+         if ( signnegative(st0_ptr) )
            {
-             if ( (st0_ptr->exp >= EXP_BIAS) &&
+             if ( (exponent(st0_ptr) >= 0) &&
                  !((st0_ptr->sigh == 0x80000000) &&
                    (st0_ptr->sigl == 0)) )
                {
                  /* st(0) holds < -1.0 */
 #ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
-                 st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+                 changesign(st1_ptr);
 #else
-                 if ( arith_invalid(st1_ptr) ) return;
+                 if ( arith_invalid(1) < 0 ) return;
 #endif /* PECULIAR_486 */
-                 pop(); return;
                }
-#ifdef DENORM_OPERAND
-             if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+             else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
                return;
-#endif /* DENORM_OPERAND */
-             st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
-             pop(); return;
+             else
+               changesign(st1_ptr);
            }
-#ifdef DENORM_OPERAND
-         if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+         else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
+           return;
+         break;
+
+       case TW_NaN:
+         if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 )
            return;
-#endif /* DENORM_OPERAND */
-         pop(); return;
-       }
-      if ( st1_tag == TW_NaN )
-       {
-         if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-           pop();
-         return;
        }
+
     }
   else if ( st0_tag == TW_NaN )
     {
-      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-       pop();
-      return;
+      if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 )
+       return;
     }
   else if ( st0_tag == TW_Infinity )
     {
       if ( st1_tag == TW_NaN )
        {
-         if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-           pop();
-         return;
+         if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 )
+           return;
        }
-      else if ( st0_ptr->sign == SIGN_NEG )
+      else if ( signnegative(st0_ptr) )
        {
-         int exponent = st1_ptr->exp;
 #ifndef PECULIAR_486
          /* This should have higher priority than denormals, but... */
-         if ( arith_invalid(st1_ptr) )  /* log(-infinity) */
+         if ( arith_invalid(1) < 0 )  /* log(-infinity) */
            return;
 #endif /* PECULIAR_486 */
-#ifdef DENORM_OPERAND
-         if ( st1_tag != TW_Zero )
-           {
-             if ( (exponent <= EXP_UNDER) && (denormal_operand()) )
-               return;
-           }
-#endif /* DENORM_OPERAND */
+         if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) )
+           return;
 #ifdef PECULIAR_486
          /* Denormal operands actually get higher priority */
-         if ( arith_invalid(st1_ptr) )  /* log(-infinity) */
+         if ( arith_invalid(1) < 0 )  /* log(-infinity) */
            return;
 #endif /* PECULIAR_486 */
-         pop();
-         return;
        }
-      else if ( st1_tag == TW_Zero )
+      else if ( st1_tag == TAG_Zero )
        {
          /* log(infinity) */
-         if ( !arith_invalid(st1_ptr) )
-           pop();
-         return;
+         if ( arith_invalid(1) < 0 )
+           return;
        }
-
+       
       /* st(1) must be valid here. */
 
-#ifdef DENORM_OPERAND
-      if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+      else if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) )
        return;
-#endif /* DENORM_OPERAND */
 
       /* The Manual says that log(Infinity) is invalid, but a real
         80486 sensibly says that it is o.k. */
-      { char sign = st1_ptr->sign;
-       reg_move(&CONST_INF, st1_ptr);
-       st1_ptr->sign = sign;
-      }
-      pop();
-      return;
+      else
+       {
+         u_char sign = getsign(st1_ptr);
+         FPU_copy_to_reg1(&CONST_INF, TAG_Special);
+         setsign(st1_ptr, sign);
+       }
     }
 #ifdef PARANOID
   else
     {
       EXCEPTION(EX_INTERNAL | 0x117);
+      return;
     }
 #endif /* PARANOID */
+
+  FPU_pop();
+  return;
+
 }
 
 
-static void fscale(FPU_REG *st0_ptr)
+static void fscale(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st1_ptr = &st(1);
-  char st1_tag = st1_ptr->tag;
+  u_char st1_tag = FPU_gettagi(1);
   int old_cw = control_word;
-  char sign = st0_ptr->sign;
+  u_char sign = getsign(st0_ptr);
 
   clear_C1();
-  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+  if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) )
     {
       long scale;
       FPU_REG tmp;
 
-#ifdef DENORM_OPERAND
-      if ( ((st0_ptr->exp <= EXP_UNDER) ||
-           (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
-       return;
-#endif /* DENORM_OPERAND */
+      /* Convert register for internal use. */
+      setexponent16(st0_ptr, exponent(st0_ptr));
+
+    valid_scale:
 
-      if ( st1_ptr->exp > EXP_BIAS + 30 )
+      if ( exponent(st1_ptr) > 18 )
        {
-         /* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */
-         char sign;
+         /* 2^18 is far too large, would require 2^(2^17) or 2^(-2^17) */
 
-         if ( st1_ptr->sign == SIGN_POS )
+       outrange_scale:
+         if ( signpositive(st1_ptr) )
            {
              EXCEPTION(EX_Overflow);
-             sign = st0_ptr->sign;
-             reg_move(&CONST_INF, st0_ptr);
-             st0_ptr->sign = sign;
+             FPU_copy_to_reg0(&CONST_INF, TAG_Special);
            }
          else
            {
              EXCEPTION(EX_Underflow);
-             sign = st0_ptr->sign;
-             reg_move(&CONST_Z, st0_ptr);
-             st0_ptr->sign = sign;
+             FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
            }
+         setsign(st0_ptr, sign);
          return;
        }
 
       control_word &= ~CW_RC;
       control_word |= RC_CHOP;
-      reg_move(st1_ptr, &tmp);
-      round_to_int(&tmp);               /* This can never overflow here */
+      reg_copy(st1_ptr, &tmp);
+      FPU_round_to_int(&tmp, st1_tag);      /* This can never overflow here */
       control_word = old_cw;
-      scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl;
-      scale += st0_ptr->exp;
-      st0_ptr->exp = scale;
+      scale = signnegative(st1_ptr) ? -tmp.sigl : tmp.sigl;
+      scale += exponent16(st0_ptr);
+      if ( (scale > 0x7000) || (scale < -0x7000) )
+       goto outrange_scale;
 
-      /* Use round_reg() to properly detect under/overflow etc */
-      round_reg(st0_ptr, 0, control_word);
+      setexponent16(st0_ptr, scale);
+
+      /* Use FPU_round() to properly detect remaining cases of
+        under/overflow etc */
+      FPU_round(st0_ptr, 0, 0, control_word, sign);
 
       return;
     }
-  else if ( st0_tag == TW_Valid )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+  if ( st1_tag == TAG_Special )
+    st1_tag = FPU_Special(st1_ptr);
+
+  if ( (st0_tag == TAG_Valid) || (st0_tag == TW_Denormal) )
     {
-      if ( st1_tag == TW_Zero )
+      switch ( st1_tag )
        {
-
-#ifdef DENORM_OPERAND
-         if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+       case TAG_Valid:
+         /* st(0) must be a denormal */
+         if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
            return;
-#endif /* DENORM_OPERAND */
 
+         FPU_to_exp16(st0_ptr, st0_ptr);  /* Will not be left on stack */
+         goto valid_scale;
+
+       case TAG_Zero:
+         if ( st0_tag == TW_Denormal )
+           denormal_operand();
          return;
-       }
-      if ( st1_tag == TW_Infinity )
-       {
-#ifdef DENORM_OPERAND
-         if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+
+       case TW_Denormal:
+         denormal_operand();
+         return;
+
+       case TW_Infinity:
+         if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
            return;
-#endif /* DENORM_OPERAND */
 
-         if ( st1_ptr->sign == SIGN_POS )
-           { reg_move(&CONST_INF, st0_ptr); }
+         if ( signpositive(st1_ptr) )
+           FPU_copy_to_reg0(&CONST_INF, TAG_Special);
          else
-             reg_move(&CONST_Z, st0_ptr);
-         st0_ptr->sign = sign;
+           FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
+         setsign(st0_ptr, sign);
+         return;
+
+       case TW_NaN:
+         real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
          return;
        }
-      if ( st1_tag == TW_NaN )
-       { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
     }
-  else if ( st0_tag == TW_Zero )
+  else if ( st0_tag == TAG_Zero )
     {
-      if ( st1_tag == TW_Valid )
+      switch ( st1_tag )
        {
+       case TAG_Valid:
+       case TAG_Zero:
+         return;
 
-#ifdef DENORM_OPERAND
-         if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-           return;
-#endif /* DENORM_OPERAND */
+       case TW_Denormal:
+         denormal_operand();
+         return;
 
+       case TW_Infinity:
+         if ( signpositive(st1_ptr) )
+           arith_invalid(0); /* Zero scaled by +Infinity */
+         return;
+
+       case TW_NaN:
+         real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
          return;
        }
-      else if ( st1_tag == TW_Zero ) { return; }
-      else if ( st1_tag == TW_Infinity )
-       {
-         if ( st1_ptr->sign == SIGN_NEG )
-           return;
-         else
-           {
-             arith_invalid(st0_ptr); /* Zero scaled by +Infinity */
-             return;
-           }
-       }
-      else if ( st1_tag == TW_NaN )
-       { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
     }
   else if ( st0_tag == TW_Infinity )
     {
-      if ( st1_tag == TW_Valid )
+      switch ( st1_tag )
        {
+       case TAG_Valid:
+       case TAG_Zero:
+         return;
 
-#ifdef DENORM_OPERAND
-         if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-           return;
-#endif /* DENORM_OPERAND */
+       case TW_Denormal:
+         denormal_operand();
+         return;
 
+       case TW_Infinity:
+         if ( signnegative(st1_ptr) )
+           arith_invalid(0); /* Infinity scaled by -Infinity */
          return;
-       }
-      if ( ((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS))
-         || (st1_tag == TW_Zero) )
-       return;
-      else if ( st1_tag == TW_Infinity )
-       {
-         arith_invalid(st0_ptr); /* Infinity scaled by -Infinity */
+
+       case TW_NaN:
+         real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
          return;
        }
-      else if ( st1_tag == TW_NaN )
-       { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
     }
   else if ( st0_tag == TW_NaN )
     {
-      if ( st1_tag != TW_Empty )
-       { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
+      if ( st1_tag != TAG_Empty )
+       { real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr); return; }
     }
 
 #ifdef PARANOID
-  if ( !((st0_tag == TW_Empty) || (st1_tag == TW_Empty)) )
+  if ( !((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) )
     {
       EXCEPTION(EX_INTERNAL | 0x115);
       return;
@@ -1690,7 +1820,7 @@ static void fscale(FPU_REG *st0_ptr)
 #endif
 
   /* At least one of st(0), st(1) must be empty */
-  stack_underflow();
+  FPU_stack_underflow();
 
 }
 
@@ -1698,21 +1828,22 @@ static void fscale(FPU_REG *st0_ptr)
 /*---------------------------------------------------------------------------*/
 
 static FUNC_ST0 const trig_table_a[] = {
-  f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp
+  f2xm1, fyl2x, fptan, fpatan,
+  fxtract, fprem1, (FUNC_ST0)fdecstp, (FUNC_ST0)fincstp
 };
 
-void trig_a(void)
+void FPU_triga(void)
 {
-  (trig_table_a[FPU_rm])(&st(0));
+  (trig_table_a[FPU_rm])(&st(0), FPU_gettag0());
 }
 
 
 static FUNC_ST0 const trig_table_b[] =
   {
-    fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos
+    fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, (FUNC_ST0)fsin, fcos
   };
 
-void trig_b(void)
+void FPU_trigb(void)
 {
-  (trig_table_b[FPU_rm])(&st(0));
+  (trig_table_b[FPU_rm])(&st(0), FPU_gettag0());
 }
index 93005909588c16cc7fd3b6c8948bba02104610ea..c4d0615af7b1f980219535a075256f5703fa001b 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Get the effective address from an FPU instruction.                        |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
+ | Copyright (C) 1992,1993,1994,1997                                         |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@suburbia.net             |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -19,9 +19,6 @@
 
 
 #include <linux/stddef.h>
-#include <linux/head.h>
-
-#include <asm/segment.h>
 
 #include "fpu_system.h"
 #include "exception.h"
@@ -41,7 +38,7 @@ static int reg_offset[] = {
        offsetof(struct info,___edi)
 };
 
-#define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info))
+#define REG_(x) (*(long *)(reg_offset[(x)]+(u_char *) FPU_info))
 
 static int reg_offset_vm86[] = {
        offsetof(struct info,___cs),
@@ -54,31 +51,35 @@ static int reg_offset_vm86[] = {
       };
 
 #define VM86_REG_(x) (*(unsigned short *) \
-                     (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info))
+                     (reg_offset_vm86[((unsigned)x)]+(u_char *) FPU_info))
+
+/* These are dummy, fs and gs are not saved on the stack. */
+#define ___FS ___ds
+#define ___GS ___ds
 
 static int reg_offset_pm[] = {
        offsetof(struct info,___cs),
        offsetof(struct info,___ds),
        offsetof(struct info,___es),
-       offsetof(struct info,___fs),
-       offsetof(struct info,___gs),
+       offsetof(struct info,___FS),
+       offsetof(struct info,___GS),
        offsetof(struct info,___ss),
        offsetof(struct info,___ds)
       };
 
 #define PM_REG_(x) (*(unsigned short *) \
-                     (reg_offset_pm[((unsigned)x)]+(char *) FPU_info))
+                     (reg_offset_pm[((unsigned)x)]+(u_char *) FPU_info))
 
 
 /* Decode the SIB byte. This function assumes mod != 0 */
 static int sib(int mod, unsigned long *fpu_eip)
 {
-  unsigned char ss,index,base;
+  u_char ss,index,base;
   long offset;
 
   RE_ENTRANT_CHECK_OFF;
   FPU_code_verify_area(1);
-  base = get_fs_byte((char *) (*fpu_eip));   /* The SIB byte */
+  FPU_get_user(base, (u_char *) (*fpu_eip));   /* The SIB byte */
   RE_ENTRANT_CHECK_ON;
   (*fpu_eip)++;
   ss = base >> 6;
@@ -105,18 +106,22 @@ static int sib(int mod, unsigned long *fpu_eip)
   if (mod == 1)
     {
       /* 8 bit signed displacement */
+      long displacement;
       RE_ENTRANT_CHECK_OFF;
       FPU_code_verify_area(1);
-      offset += (signed char) get_fs_byte((char *) (*fpu_eip));
+      FPU_get_user(displacement, (signed char *) (*fpu_eip));
+      offset += displacement;
       RE_ENTRANT_CHECK_ON;
       (*fpu_eip)++;
     }
   else if (mod == 2 || base == 5) /* The second condition also has mod==0 */
     {
       /* 32 bit displacement */
+      long displacement;
       RE_ENTRANT_CHECK_OFF;
       FPU_code_verify_area(4);
-      offset += (signed) get_fs_long((unsigned long *) (*fpu_eip));
+      FPU_get_user(displacement, (long *) (*fpu_eip));
+      offset += displacement;
       RE_ENTRANT_CHECK_ON;
       (*fpu_eip) += 4;
     }
@@ -125,39 +130,61 @@ static int sib(int mod, unsigned long *fpu_eip)
 }
 
 
-static unsigned long vm86_segment(unsigned char segment,
-                                 unsigned short *selector)
-{ 
+static unsigned long vm86_segment(u_char segment,
+                                 struct address *addr)
+{
   segment--;
 #ifdef PARANOID
   if ( segment > PREFIX_SS_ )
     {
       EXCEPTION(EX_INTERNAL|0x130);
-      math_abort(FPU_info,SIGSEGV);
+      math_abort(SIGSEGV);
     }
 #endif /* PARANOID */
-  *selector = VM86_REG_(segment);
+  addr->selector = VM86_REG_(segment);
   return (unsigned long)VM86_REG_(segment) << 4;
 }
 
 
 /* This should work for 16 and 32 bit protected mode. */
-static long pm_address(unsigned char FPU_modrm, unsigned char segment,
-                      unsigned short *selector, long offset)
+static long pm_address(u_char FPU_modrm, u_char segment,
+                      struct address *addr, long offset)
 { 
   struct desc_struct descriptor;
   unsigned long base_address, limit, address, seg_top;
+  unsigned short selector;
 
   segment--;
+
 #ifdef PARANOID
+  /* segment is unsigned, so this also detects if segment was 0: */
   if ( segment > PREFIX_SS_ )
     {
       EXCEPTION(EX_INTERNAL|0x132);
-      math_abort(FPU_info,SIGSEGV);
+      math_abort(SIGSEGV);
     }
 #endif /* PARANOID */
 
-  *selector = PM_REG_(segment);
+  switch ( segment )
+    {
+      /* fs and gs aren't used by the kernel, so they still have their
+        user-space values. */
+    case PREFIX_FS_-1:
+      /* The cast is needed here to get gcc 2.8.0 to use a 16 bit register
+        in the assembler statement. */
+
+      __asm__("mov %%fs,%0":"=r" (selector));
+      addr->selector = selector;
+      break;
+    case PREFIX_GS_-1:
+      /* The cast is needed here to get gcc 2.8.0 to use a 16 bit register
+        in the assembler statement. */
+      __asm__("mov %%gs,%0":"=r" (selector));
+      addr->selector = selector;
+      break;
+    default:
+      addr->selector = PM_REG_(segment);
+    }
 
   descriptor = LDT_DESCRIPTOR(PM_REG_(segment));
   base_address = SEG_BASE_ADDR(descriptor);
@@ -211,12 +238,11 @@ static long pm_address(unsigned char FPU_modrm, unsigned char segment,
 
 */
 
-void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
+void *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
                  struct address *addr,
-/*               unsigned short *selector, unsigned long *offset, */
                  fpu_addr_modes addr_modes)
 {
-  unsigned char mod;
+  u_char mod;
   unsigned rm = FPU_modrm & 7;
   long *cpu_reg_ptr;
   int address = 0;     /* Initialized just to stop compiler warnings. */
@@ -226,7 +252,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
   if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
       && (addr_modes.override.segment == PREFIX_CS_) )
     {
-      math_abort(FPU_info,SIGSEGV);
+      math_abort(SIGSEGV);
     }
 
   addr->selector = FPU_DS;   /* Default, for 32 bit non-segmented mode. */
@@ -248,7 +274,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
              /* Special case: disp32 */
              RE_ENTRANT_CHECK_OFF;
              FPU_code_verify_area(4);
-             address = get_fs_long((unsigned long *) (*fpu_eip));
+             FPU_get_user(address, (unsigned long *) (*fpu_eip));
              (*fpu_eip) += 4;
              RE_ENTRANT_CHECK_ON;
              addr->offset = address;
@@ -265,7 +291,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
          /* 8 bit signed displacement */
          RE_ENTRANT_CHECK_OFF;
          FPU_code_verify_area(1);
-         address = (signed char) get_fs_byte((char *) (*fpu_eip));
+         FPU_get_user(address, (signed char *) (*fpu_eip));
          RE_ENTRANT_CHECK_ON;
          (*fpu_eip)++;
          break;
@@ -273,7 +299,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
          /* 32 bit displacement */
          RE_ENTRANT_CHECK_OFF;
          FPU_code_verify_area(4);
-         address = (signed) get_fs_long((unsigned long *) (*fpu_eip));
+         FPU_get_user(address, (long *) (*fpu_eip));
          (*fpu_eip) += 4;
          RE_ENTRANT_CHECK_ON;
          break;
@@ -291,13 +317,12 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
     case 0:
       break;
     case VM86:
-      address += vm86_segment(addr_modes.override.segment,
-                             (unsigned short *)&(addr->selector));
+      address += vm86_segment(addr_modes.override.segment, addr);
       break;
     case PM16:
     case SEG32:
       address = pm_address(FPU_modrm, addr_modes.override.segment,
-                          (unsigned short *)&(addr->selector), address);
+                          addr, address);
       break;
     default:
       EXCEPTION(EX_INTERNAL|0x133);
@@ -307,12 +332,11 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
 }
 
 
-void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
+void *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
                     struct address *addr,
-/*                  unsigned short *selector, unsigned long *offset, */
                     fpu_addr_modes addr_modes)
 {
-  unsigned char mod;
+  u_char mod;
   unsigned rm = FPU_modrm & 7;
   int address = 0;     /* Default used for mod == 0 */
 
@@ -321,7 +345,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
   if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
       && (addr_modes.override.segment == PREFIX_CS_) )
     {
-      math_abort(FPU_info,SIGSEGV);
+      math_abort(SIGSEGV);
     }
 
   addr->selector = FPU_DS;   /* Default, for 32 bit non-segmented mode. */
@@ -336,7 +360,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
          /* Special case: disp16 */
          RE_ENTRANT_CHECK_OFF;
          FPU_code_verify_area(2);
-         address = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip));
+         FPU_get_user(address, (unsigned short *) (*fpu_eip));
          (*fpu_eip) += 2;
          RE_ENTRANT_CHECK_ON;
          goto add_segment;
@@ -346,7 +370,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
       /* 8 bit signed displacement */
       RE_ENTRANT_CHECK_OFF;
       FPU_code_verify_area(1);
-      address = (signed char) get_fs_byte((signed char *) (*fpu_eip));
+      FPU_get_user(address, (signed char *) (*fpu_eip));
       RE_ENTRANT_CHECK_ON;
       (*fpu_eip)++;
       break;
@@ -354,7 +378,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
       /* 16 bit displacement */
       RE_ENTRANT_CHECK_OFF;
       FPU_code_verify_area(2);
-      address = (unsigned) get_fs_word((unsigned short *) (*fpu_eip));
+      FPU_get_user(address, (unsigned short *) (*fpu_eip));
       (*fpu_eip) += 2;
       RE_ENTRANT_CHECK_ON;
       break;
@@ -407,13 +431,12 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
     case 0:
       break;
     case VM86:
-      address += vm86_segment(addr_modes.override.segment,
-                             (unsigned short *)&(addr->selector));
+      address += vm86_segment(addr_modes.override.segment, addr);
       break;
     case PM16:
     case SEG32:
       address = pm_address(FPU_modrm, addr_modes.override.segment,
-                          (unsigned short *)&(addr->selector), address);
+                          addr, address);
       break;
     default:
       EXCEPTION(EX_INTERNAL|0x131);
index 647aa67dcc1d760e37f0b02cf4c4bd4e4c10cf7a..2ee33bb6235f1f013c0d8b740130b0ea9a68bef0 100644 (file)
@@ -4,9 +4,9 @@
  | This file contains most of the code to interpret the FPU instructions     |
  | which load and store from user memory.                                    |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
+ | Copyright (C) 1992,1993,1994,1997,2001                                    |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -18,8 +18,6 @@
  |    other processes using the emulator while swapping is in progress.      |
  +---------------------------------------------------------------------------*/
 
-#include <asm/segment.h>
-
 #include "fpu_system.h"
 #include "exception.h"
 #include "fpu_emu.h"
 #define _PUSH_ 3   /* Need to check for space to push onto stack */
 #define _null_ 4   /* Function illegal or not implemented */
 
-#define pop_0()        { st0_ptr->tag = TW_Empty; top++; }
+#define pop_0()        { FPU_settag0(TAG_Empty); FPU_top++; }
 
 
-static unsigned char const type_table[32] = {
+static u_char const type_table[32] = {
   _PUSH_, _PUSH_, _PUSH_, _PUSH_,
   _null_, _null_, _null_, _null_,
   _REG0_, _REG0_, _REG0_, _REG0_,
@@ -46,25 +44,27 @@ static unsigned char const type_table[32] = {
   _NONE_, _REG0_, _NONE_, _REG0_
   };
 
-unsigned char const data_sizes_16[32] = {
+u_char const data_sizes_16[32] = {
   4,  4,  8,  2,  0,  0,  0,  0,
   4,  4,  8,  2,  4,  4,  8,  2,
   14, 0, 94, 10,  2, 10,  0,  8,  
   14, 0, 94, 10,  2, 10,  2,  8
 };
 
-unsigned char const data_sizes_32[32] = {
+u_char const data_sizes_32[32] = {
   4,  4,  8,  2,  0,  0,  0,  0,
   4,  4,  8,  2,  4,  4,  8,  2,
   28, 0,108, 10,  2, 10,  0,  8,  
   28, 0,108, 10,  2, 10,  2,  8
 };
 
-int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
+int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
                     void *data_address)
 {
   FPU_REG loaded_data;
   FPU_REG *st0_ptr;
+  u_char st0_tag = TAG_Empty;  /* This is just to stop a gcc warning. */
+  u_char loaded_tag;
 
   st0_ptr = NULL;    /* Initialized just to stop compiler warnings. */
 
@@ -73,12 +73,12 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
       if ( addr_modes.default_mode == SEG32 )
        {
          if ( access_limit < data_sizes_32[type] )
-           math_abort(FPU_info,SIGSEGV);
+           math_abort(SIGSEGV);
        }
       else if ( addr_modes.default_mode == PM16 )
        {
          if ( access_limit < data_sizes_16[type] )
-           math_abort(FPU_info,SIGSEGV);
+           math_abort(SIGSEGV);
        }
 #ifdef PARANOID
       else
@@ -93,13 +93,14 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
     case _REG0_:
       st0_ptr = &st(0);       /* Some of these instructions pop after
                                 storing */
+      st0_tag = FPU_gettag0();
       break;
     case _PUSH_:
       {
-       st0_ptr = &st(-1);
-       if ( st0_ptr->tag != TW_Empty )
-         { stack_overflow(); return 0; }
-       top--;
+       if ( FPU_gettagi(-1) != TAG_Empty )
+         { FPU_stack_overflow(); return 0; }
+       FPU_top--;
+       st0_ptr = &st(0);
       }
       break;
     case _null_:
@@ -116,92 +117,97 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
     {
     case 000:       /* fld m32real */
       clear_C1();
-      reg_load_single((float *)data_address, &loaded_data);
-      if ( (loaded_data.tag == TW_NaN) &&
-         real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) )
+      loaded_tag = FPU_load_single((float *)data_address, &loaded_data);
+      if ( (loaded_tag == TAG_Special)
+          && isNaN(&loaded_data)
+          && (real_1op_NaN(&loaded_data) < 0) )
        {
-         top++;
+         FPU_top++;
          break;
        }
-      reg_move(&loaded_data, st0_ptr);
+      FPU_copy_to_reg0(&loaded_data, loaded_tag);
       break;
     case 001:      /* fild m32int */
       clear_C1();
-      reg_load_int32((long *)data_address, st0_ptr);
+      loaded_tag = FPU_load_int32((long *)data_address, &loaded_data);
+      FPU_copy_to_reg0(&loaded_data, loaded_tag);
       break;
     case 002:      /* fld m64real */
       clear_C1();
-      reg_load_double((double *)data_address, &loaded_data);
-      if ( (loaded_data.tag == TW_NaN) &&
-         real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) )
+      loaded_tag = FPU_load_double((double *)data_address, &loaded_data);
+      if ( (loaded_tag == TAG_Special)
+          && isNaN(&loaded_data)
+          && (real_1op_NaN(&loaded_data) < 0) )
        {
-         top++;
+         FPU_top++;
          break;
        }
-      reg_move(&loaded_data, st0_ptr);
+      FPU_copy_to_reg0(&loaded_data, loaded_tag);
       break;
     case 003:      /* fild m16int */
       clear_C1();
-      reg_load_int16((short *)data_address, st0_ptr);
+      loaded_tag = FPU_load_int16((short *)data_address, &loaded_data);
+      FPU_copy_to_reg0(&loaded_data, loaded_tag);
       break;
     case 010:      /* fst m32real */
       clear_C1();
-      reg_store_single((float *)data_address, st0_ptr);
+      FPU_store_single(st0_ptr, st0_tag, (float *)data_address);
       break;
     case 011:      /* fist m32int */
       clear_C1();
-      reg_store_int32((long *)data_address, st0_ptr);
+      FPU_store_int32(st0_ptr, st0_tag, (long *)data_address);
       break;
     case 012:     /* fst m64real */
       clear_C1();
-      reg_store_double((double *)data_address, st0_ptr);
+      FPU_store_double(st0_ptr, st0_tag, (double *)data_address);
       break;
     case 013:     /* fist m16int */
       clear_C1();
-      reg_store_int16((short *)data_address, st0_ptr);
+      FPU_store_int16(st0_ptr, st0_tag, (short *)data_address);
       break;
     case 014:     /* fstp m32real */
       clear_C1();
-      if ( reg_store_single((float *)data_address, st0_ptr) )
+      if ( FPU_store_single(st0_ptr, st0_tag, (float *)data_address) )
        pop_0();  /* pop only if the number was actually stored
                     (see the 80486 manual p16-28) */
       break;
     case 015:     /* fistp m32int */
       clear_C1();
-      if ( reg_store_int32((long *)data_address, st0_ptr) )
+      if ( FPU_store_int32(st0_ptr, st0_tag, (long *)data_address) )
        pop_0();  /* pop only if the number was actually stored
                     (see the 80486 manual p16-28) */
       break;
     case 016:     /* fstp m64real */
       clear_C1();
-      if ( reg_store_double((double *)data_address, st0_ptr) )
+      if ( FPU_store_double(st0_ptr, st0_tag, (double *)data_address) )
        pop_0();  /* pop only if the number was actually stored
                     (see the 80486 manual p16-28) */
       break;
     case 017:     /* fistp m16int */
       clear_C1();
-      if ( reg_store_int16((short *)data_address, st0_ptr) )
+      if ( FPU_store_int16(st0_ptr, st0_tag, (short *)data_address) )
        pop_0();  /* pop only if the number was actually stored
                     (see the 80486 manual p16-28) */
       break;
     case 020:     /* fldenv  m14/28byte */
-      fldenv(addr_modes, (char *)data_address);
+      fldenv(addr_modes, (u_char *)data_address);
       /* Ensure that the values just loaded are not changed by
         fix-up operations. */
       return 1;
     case 022:     /* frstor m94/108byte */
-      frstor(addr_modes, (char *)data_address);
+      frstor(addr_modes, (u_char *)data_address);
       /* Ensure that the values just loaded are not changed by
         fix-up operations. */
       return 1;
     case 023:     /* fbld m80dec */
       clear_C1();
-      reg_load_bcd((char *)data_address, st0_ptr);
+      loaded_tag = FPU_load_bcd((u_char *)data_address);
+      FPU_settag0(loaded_tag);
       break;
     case 024:     /* fldcw */
       RE_ENTRANT_CHECK_OFF;
       FPU_verify_area(VERIFY_READ, data_address, 2);
-      control_word = get_fs_word((unsigned short *) data_address);
+      FPU_get_user(control_word, (unsigned short *) data_address);
       RE_ENTRANT_CHECK_ON;
       if ( partial_status & ~control_word & CW_Exceptions )
        partial_status |= (SW_Summary | SW_Backward);
@@ -213,45 +219,47 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
       return 1;
     case 025:      /* fld m80real */
       clear_C1();
-      reg_load_extended((long double *)data_address, st0_ptr);
+      loaded_tag = FPU_load_extended((long double *)data_address, 0);
+      FPU_settag0(loaded_tag);
       break;
     case 027:      /* fild m64int */
       clear_C1();
-      reg_load_int64((long long *)data_address, st0_ptr);
+      loaded_tag = FPU_load_int64((long long *)data_address);
+      FPU_settag0(loaded_tag);
       break;
     case 030:     /* fstenv  m14/28byte */
-      fstenv(addr_modes, (char *)data_address);
+      fstenv(addr_modes, (u_char *)data_address);
       return 1;
     case 032:      /* fsave */
-      fsave(addr_modes, (char *)data_address);
+      fsave(addr_modes, (u_char *)data_address);
       return 1;
     case 033:      /* fbstp m80dec */
       clear_C1();
-      if ( reg_store_bcd((char *)data_address, st0_ptr) )
+      if ( FPU_store_bcd(st0_ptr, st0_tag, (u_char *)data_address) )
        pop_0();  /* pop only if the number was actually stored
                     (see the 80486 manual p16-28) */
       break;
     case 034:      /* fstcw m16int */
       RE_ENTRANT_CHECK_OFF;
       FPU_verify_area(VERIFY_WRITE,data_address,2);
-      put_fs_word(control_word, (short *) data_address);
+      FPU_put_user(control_word, (unsigned short *) data_address);
       RE_ENTRANT_CHECK_ON;
       return 1;
     case 035:      /* fstp m80real */
       clear_C1();
-      if ( reg_store_extended((long double *)data_address, st0_ptr) )
+      if ( FPU_store_extended(st0_ptr, st0_tag, (long double *)data_address) )
        pop_0();  /* pop only if the number was actually stored
                     (see the 80486 manual p16-28) */
       break;
     case 036:      /* fstsw m2byte */
       RE_ENTRANT_CHECK_OFF;
       FPU_verify_area(VERIFY_WRITE,data_address,2);
-      put_fs_word(status_word(),(short *) data_address);
+      FPU_put_user(status_word(),(unsigned short *) data_address);
       RE_ENTRANT_CHECK_ON;
       return 1;
     case 037:      /* fistp m64int */
       clear_C1();
-      if ( reg_store_int64((long long *)data_address, st0_ptr) )
+      if ( FPU_store_int64(st0_ptr, st0_tag, (long long *)data_address) )
        pop_0();  /* pop only if the number was actually stored
                     (see the 80486 manual p16-28) */
       break;
index f8b9018c1bd94068776cfeccdb6597fbeaff9312..026bb4cc543de78ec320b37b973bb2f25bb23d11 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  |  Header file for the FPU-emu poly*.c source files.                        |
  |                                                                           |
- | Copyright (C) 1994                                                        |
+ | Copyright (C) 1994,1999                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  | Declarations and definitions for functions operating on Xsig (12-byte     |
  | extended-significand) quantities.                                         |
@@ -55,15 +55,20 @@ asmlinkage void div_Xsig(Xsig *x1, const Xsig *x2, const Xsig *dest);
    actually be in-line.
    */
 
-/* Multiply two fixed-point 32 bit numbers. */
-extern inline void mul_32_32(const unsigned long arg1,
-                            const unsigned long arg2,
-                            unsigned long *out)
+/* Multiply two fixed-point 32 bit numbers, producing a 32 bit result.
+   The answer is the ms word of the product. */
+/* Some versions of gcc make it difficult to stop eax from being clobbered.
+   Merely specifying that it is used doesn't work...
+ */
+extern inline unsigned long mul_32_32(const unsigned long arg1,
+                                     const unsigned long arg2)
 {
-  asm volatile ("movl %1,%%eax; mull %2; movl %%edx,%0" \
-               :"=g" (*out) \
-               :"g" (arg1), "g" (arg2) \
-               :"ax","dx");
+  int retval;
+  asm volatile ("mull %2; movl %%edx,%%eax" \
+               :"=a" (retval) \
+               :"0" (arg1), "g" (arg2) \
+               :"dx");
+  return retval;
 }
 
 
@@ -83,7 +88,7 @@ extern inline void add_Xsig_Xsig(Xsig *dest, const Xsig *x2)
 /* Note: the constraints in the asm statement didn't always work properly
    with gcc 2.5.8.  Changing from using edi to using ecx got around the
    problem, but keep fingers crossed! */
-extern inline int add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp)
+extern inline void add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp)
 {
   asm volatile ("movl %2,%%ecx; movl %3,%%esi;
                  movl (%%esi),%%eax; addl %%eax,(%%ecx);
index 09cb472200524f7340d6022ae768dfd67376775b..9766ad5e97438a3d2d5e2a1862142e09a66fed07 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Function to compute 2^x-1 by a polynomial approximation.                  |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1994,1997                                         |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -13,6 +13,7 @@
 #include "exception.h"
 #include "reg_constant.h"
 #include "fpu_emu.h"
+#include "fpu_system.h"
 #include "control_w.h"
 #include "poly.h"
 
@@ -48,20 +49,19 @@ static const Xsig *shiftterm[] = { &shiftterm0, &shiftterm1,
 
 
 /*--- poly_2xm1() -----------------------------------------------------------+
- | Requires an argument which is TW_Valid and < 1.                           |
+ | Requires st(0) which is TAG_Valid and < 1.                                |
  +---------------------------------------------------------------------------*/
-int    poly_2xm1(FPU_REG const *arg, FPU_REG *result)
+int    poly_2xm1(u_char sign, FPU_REG *arg, FPU_REG *result)
 {
-  long int               exponent, shift;
-  unsigned long long     Xll;
-  Xsig                   accumulator, Denom, argSignif;
+  long int              exponent, shift;
+  unsigned long long    Xll;
+  Xsig                  accumulator, Denom, argSignif;
+  u_char                tag;
 
-
-  exponent = arg->exp - EXP_BIAS;
+  exponent = exponent16(arg);
 
 #ifdef PARANOID
-  if (   (exponent >= 0)       /* Don't want a |number| >= 1.0 */
-      || (arg->tag != TW_Valid) )
+  if ( exponent >= 0 )         /* Don't want a |number| >= 1.0 */
     {
       /* Number negative, too large, or not Valid. */
       EXCEPTION(EX_INTERNAL|0x127);
@@ -94,7 +94,7 @@ int   poly_2xm1(FPU_REG const *arg, FPU_REG *result)
   if ( exponent < -2 )
     {
       /* Shift the argument right by the required places. */
-      if ( shrx(&Xll, -2-exponent) >= 0x80000000U )
+      if ( FPU_shrx(&Xll, -2-exponent) >= 0x80000000U )
        Xll++;  /* round up */
     }
 
@@ -118,7 +118,7 @@ int poly_2xm1(FPU_REG const *arg, FPU_REG *result)
       exponent = 1;
     }
 
-  if ( arg->sign != SIGN_POS )
+  if ( sign != SIGN_POS )
     {
       /* The argument is negative, use the identity:
             f(-x) = -f(x) / (1 + f(x))
@@ -142,10 +142,14 @@ int       poly_2xm1(FPU_REG const *arg, FPU_REG *result)
   /* Convert to 64 bit signed-compatible */
   exponent += round_Xsig(&accumulator);
 
+  result = &st(0);
   significand(result) = XSIG_LL(accumulator);
-  result->tag = TW_Valid;
-  result->exp = exponent + EXP_BIAS;
-  result->sign = arg->sign;
+  setexponent16(result, exponent);
+
+  tag = FPU_round(result, 1, 0, FULL_PRECISION, sign);
+
+  setsign(result, sign);
+  FPU_settag0(tag);
 
   return 0;
 
index 36b0fcc55e161c5d43fb0204ab0432c5f1041d75..82f702952f690982e841a655de465595e5ed429d 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Compute the arctan of a FPU_REG, using a polynomial approximation.        |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1994,1997                                         |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -13,6 +13,7 @@
 #include "exception.h"
 #include "reg_constant.h"
 #include "fpu_emu.h"
+#include "fpu_system.h"
 #include "status_w.h"
 #include "control_w.h"
 #include "poly.h"
@@ -51,31 +52,57 @@ static const Xsig pi_signif = MK_XSIG(0xc90fdaa2, 0x2168c234, 0xc4c6628b);
 /*--- poly_atan() -----------------------------------------------------------+
  |                                                                           |
  +---------------------------------------------------------------------------*/
-void   poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result)
+void   poly_atan(FPU_REG *st0_ptr, u_char st0_tag,
+                 FPU_REG *st1_ptr, u_char st1_tag)
 {
-  char                 transformed, inverted,
-                        sign1 = arg1->sign, sign2 = arg2->sign;
-  long int             exponent, dummy_exp;
-  Xsig                  accumulator, Numer, Denom, accumulatore, argSignif,
-                        argSq, argSqSq;
+  u_char       transformed, inverted,
+                sign1, sign2;
+  int           exponent;
+  long int     dummy_exp;
+  Xsig          accumulator, Numer, Denom, accumulatore, argSignif,
+                argSq, argSqSq;
+  u_char        tag;
   
+  sign1 = getsign(st0_ptr);
+  sign2 = getsign(st1_ptr);
+  if ( st0_tag == TAG_Valid )
+    {
+      exponent = exponent(st0_ptr);
+    }
+  else
+    {
+      /* This gives non-compatible stack contents... */
+      FPU_to_exp16(st0_ptr, st0_ptr);
+      exponent = exponent16(st0_ptr);
+    }
+  if ( st1_tag == TAG_Valid )
+    {
+      exponent -= exponent(st1_ptr);
+    }
+  else
+    {
+      /* This gives non-compatible stack contents... */
+      FPU_to_exp16(st1_ptr, st1_ptr);
+      exponent -= exponent16(st1_ptr);
+    }
 
-  arg1->sign = arg2->sign = SIGN_POS;
-  if ( (compare(arg2) & ~COMP_Denormal) == COMP_A_lt_B )
+  if ( (exponent < 0) || ((exponent == 0) &&
+                         ((st0_ptr->sigh < st1_ptr->sigh) ||
+                          ((st0_ptr->sigh == st1_ptr->sigh) &&
+                           (st0_ptr->sigl < st1_ptr->sigl))) ) )
     {
       inverted = 1;
-      exponent = arg1->exp - arg2->exp;
       Numer.lsw = Denom.lsw = 0;
-      XSIG_LL(Numer) = significand(arg1);
-      XSIG_LL(Denom) = significand(arg2);
+      XSIG_LL(Numer) = significand(st0_ptr);
+      XSIG_LL(Denom) = significand(st1_ptr);
     }
   else
     {
       inverted = 0;
-      exponent = arg2->exp - arg1->exp;
+      exponent = -exponent;
       Numer.lsw = Denom.lsw = 0;
-      XSIG_LL(Numer) = significand(arg2);
-      XSIG_LL(Denom) = significand(arg1);
+      XSIG_LL(Numer) = significand(st1_ptr);
+      XSIG_LL(Denom) = significand(st0_ptr);
      }
   div_Xsig(&Numer, &Denom, &argSignif);
   exponent += norm_Xsig(&argSignif);
@@ -108,10 +135,10 @@ void      poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result)
          if ( exponent < -1 )
            shr_Xsig(&Numer, -1-exponent);
          negate_Xsig(&Numer);
-
+      
          shr_Xsig(&Denom, -exponent);
          Denom.msw |= 0x80000000;
-
+      
          div_Xsig(&Numer, &Denom, &argSignif);
 
          exponent = -1 + norm_Xsig(&argSignif);
@@ -189,9 +216,14 @@ void       poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result)
     }
 
   exponent += round_Xsig(&accumulator);
-  significand(result) = XSIG_LL(accumulator);
-  result->exp = exponent + EXP_BIAS;
-  result->tag = TW_Valid;
-  result->sign = sign2;
+
+  significand(st1_ptr) = XSIG_LL(accumulator);
+  setexponent16(st1_ptr, exponent);
+
+  tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign2);
+  FPU_settagi(1, tag);
+
+  set_precision_flag_up();  /* We do not really know if up or down,
+                              use this as the default. */
 
 }
index 2ff2c4889b91ac8572cd45811c9b46ba6bc76e82..dd00e1d5b0743929508ad45926285eac43548ee8 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Compute the base 2 log of a FPU_REG, using a polynomial approximation.    |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1994,1997                                         |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 #include "exception.h"
 #include "reg_constant.h"
 #include "fpu_emu.h"
+#include "fpu_system.h"
 #include "control_w.h"
 #include "poly.h"
 
 
-
-static void log2_kernel(FPU_REG const *arg,
+static void log2_kernel(FPU_REG const *arg, u_char argsign,
                        Xsig *accum_result, long int *expon);
 
 
 /*--- poly_l2() -------------------------------------------------------------+
  |   Base 2 logarithm by a polynomial approximation.                         |
  +---------------------------------------------------------------------------*/
-void   poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result)
+void   poly_l2(FPU_REG *st0_ptr, FPU_REG *st1_ptr, u_char st1_sign)
 {
   long int            exponent, expon, expon_expon;
   Xsig                 accumulator, expon_accum, yaccum;
-  char                sign;
+  u_char                      sign, argsign;
   FPU_REG              x;
+  int                  tag;
 
+  exponent = exponent16(st0_ptr);
 
-  exponent = arg->exp - EXP_BIAS;
-
-  /* From arg, make a number > sqrt(2)/2 and < sqrt(2) */
-  if ( arg->sigh > (unsigned)0xb504f334 )
+  /* From st0_ptr, make a number > sqrt(2)/2 and < sqrt(2) */
+  if ( st0_ptr->sigh > (unsigned)0xb504f334 )
     {
-      /* Treat as  sqrt(2)/2 < arg < 1 */
-      significand(&x) = - significand(arg);
-      x.sign = SIGN_NEG;
-      x.tag = TW_Valid;
-      x.exp = EXP_BIAS-1;
+      /* Treat as  sqrt(2)/2 < st0_ptr < 1 */
+      significand(&x) = - significand(st0_ptr);
+      setexponent16(&x, -1);
       exponent++;
-      normalize(&x);
+      argsign = SIGN_NEG;
     }
   else
     {
-      /* Treat as  1 <= arg < sqrt(2) */
-      x.sigh = arg->sigh - 0x80000000;
-      x.sigl = arg->sigl;
-      x.sign = SIGN_POS;
-      x.tag = TW_Valid;
-      x.exp = EXP_BIAS;
-      normalize(&x);
+      /* Treat as  1 <= st0_ptr < sqrt(2) */
+      x.sigh = st0_ptr->sigh - 0x80000000;
+      x.sigl = st0_ptr->sigl;
+      setexponent16(&x, 0);
+      argsign = SIGN_POS;
     }
+  tag = FPU_normalize_nuo(&x);
 
-  if ( x.tag == TW_Zero )
+  if ( tag == TAG_Zero )
     {
       expon = 0;
       accumulator.msw = accumulator.midw = accumulator.lsw = 0;
     }
   else
     {
-      log2_kernel(&x, &accumulator, &expon);
+      log2_kernel(&x, argsign, &accumulator, &expon);
     }
 
-  sign = exponent < 0;
-  if ( sign ) exponent = -exponent;
+  if ( exponent < 0 )
+    {
+      sign = SIGN_NEG;
+      exponent = -exponent;
+    }
+  else
+    sign = SIGN_POS;
   expon_accum.msw = exponent; expon_accum.midw = expon_accum.lsw = 0;
   if ( exponent )
     {
       expon_expon = 31 + norm_Xsig(&expon_accum);
       shr_Xsig(&accumulator, expon_expon - expon);
 
-      if ( sign ^ (x.sign == SIGN_NEG) )
+      if ( sign ^ argsign )
        negate_Xsig(&accumulator);
       add_Xsig_Xsig(&accumulator, &expon_accum);
     }
   else
     {
       expon_expon = expon;
-      sign = x.sign;
+      sign = argsign;
     }
 
-  yaccum.lsw = 0; XSIG_LL(yaccum) = significand(y);
+  yaccum.lsw = 0; XSIG_LL(yaccum) = significand(st1_ptr);
   mul_Xsig_Xsig(&accumulator, &yaccum);
 
   expon_expon += round_Xsig(&accumulator);
 
   if ( accumulator.msw == 0 )
     {
-      reg_move(&CONST_Z, y);
-    }
-  else
-    {
-      result->exp = expon_expon + y->exp + 1;
-      significand(result) = XSIG_LL(accumulator);
-      result->tag = TW_Valid; /* set the tags to Valid */
-      result->sign = sign ^ y->sign;
+      FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
+      return;
     }
 
+  significand(st1_ptr) = XSIG_LL(accumulator);
+  setexponent16(st1_ptr, expon_expon + exponent16(st1_ptr) + 1);
+
+  tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign ^ st1_sign);
+  FPU_settagi(1, tag);
+
+  set_precision_flag_up();  /* 80486 appears to always do this */
+
   return;
+
 }
 
 
@@ -111,47 +116,62 @@ void      poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result)
  |   Base 2 logarithm by a polynomial approximation.                         |
  |   log2(x+1)                                                               |
  +---------------------------------------------------------------------------*/
-int    poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result)
+int    poly_l2p1(u_char sign0, u_char sign1,
+                 FPU_REG *st0_ptr, FPU_REG *st1_ptr, FPU_REG *dest)
 {
-  char                 sign;
-  long int             exponent;
-  Xsig                 accumulator, yaccum;
+  u_char               tag;
+  long int             exponent;
+  Xsig                 accumulator, yaccum;
 
-
-  sign = arg->sign;
-
-  if ( arg->exp < EXP_BIAS )
+  if ( exponent16(st0_ptr) < 0 )
     {
-      log2_kernel(arg, &accumulator, &exponent);
+      log2_kernel(st0_ptr, sign0, &accumulator, &exponent);
 
       yaccum.lsw = 0;
-      XSIG_LL(yaccum) = significand(y);
+      XSIG_LL(yaccum) = significand(st1_ptr);
       mul_Xsig_Xsig(&accumulator, &yaccum);
 
       exponent += round_Xsig(&accumulator);
 
-      result->exp = exponent + y->exp + 1;
-      significand(result) = XSIG_LL(accumulator);
-      result->tag = TW_Valid; /* set the tags to Valid */
-      result->sign = sign ^ y->sign;
+      exponent += exponent16(st1_ptr) + 1;
+      if ( exponent < EXP_WAY_UNDER ) exponent = EXP_WAY_UNDER;
+
+      significand(dest) = XSIG_LL(accumulator);
+      setexponent16(dest, exponent);
 
-      return 0;
+      tag = FPU_round(dest, 1, 0, FULL_PRECISION, sign0 ^ sign1);
+      FPU_settagi(1, tag);
+
+      if ( tag == TAG_Valid )
+       set_precision_flag_up();   /* 80486 appears to always do this */
     }
   else
     {
-      /* The magnitude of arg is far too large. */
-      reg_move(y, result);
-      if ( sign != SIGN_POS )
+      /* The magnitude of st0_ptr is far too large. */
+
+      if ( sign0 != SIGN_POS )
        {
          /* Trying to get the log of a negative number. */
-         return 1;
+#ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
+         changesign(st1_ptr);
+#else
+         if ( arith_invalid(1) < 0 )
+           return 1;
+#endif /* PECULIAR_486 */
        }
+
+      /* 80486 appears to do this */
+      if ( sign0 == SIGN_NEG )
+       set_precision_flag_down();
       else
-       {
-         return 0;
-       }
+       set_precision_flag_up();
     }
 
+  if ( exponent(dest) <= EXP_UNDER )
+    EXCEPTION(EX_Underflow);
+
+  return 0;
+
 }
 
 
@@ -180,20 +200,17 @@ static const unsigned long leadterm = 0xb8000000;
  |   Base 2 logarithm by a polynomial approximation.                         |
  |   log2(x+1)                                                               |
  +---------------------------------------------------------------------------*/
-static void log2_kernel(FPU_REG const *arg, Xsig *accum_result,
+static void log2_kernel(FPU_REG const *arg, u_char argsign, Xsig *accum_result,
                        long int *expon)
 {
-  char                 sign;
   long int             exponent, adj;
   unsigned long long   Xsq;
   Xsig                 accumulator, Numer, Denom, argSignif, arg_signif;
 
-  sign = arg->sign;
-
-  exponent = arg->exp - EXP_BIAS;
+  exponent = exponent16(arg);
   Numer.lsw = Denom.lsw = 0;
   XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg);
-  if ( sign == SIGN_POS )
+  if ( argsign == SIGN_POS )
     {
       shr_Xsig(&Denom, 2 - (1 + exponent));
       Denom.msw |= 0x80000000;
index 701b291dcc22d289acb5b9ba1bfed34ef86ba99a..a36313fb06f14988f3dbed1faf1ab75ce29f47f1 100644 (file)
@@ -4,9 +4,9 @@
  |  Computation of an approximation of the sin function and the cosine       |
  |  function by a polynomial.                                                |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1994,1997,1999                                    |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@melbpc.org.au                             |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -15,6 +15,7 @@
 #include "exception.h"
 #include "reg_constant.h"
 #include "fpu_emu.h"
+#include "fpu_system.h"
 #include "control_w.h"
 #include "poly.h"
 
@@ -62,35 +63,26 @@ static const unsigned long long neg_terms_h[N_COEFF_NH] =
 /*--- poly_sine() -----------------------------------------------------------+
  |                                                                           |
  +---------------------------------------------------------------------------*/
-void   poly_sine(FPU_REG const *arg, FPU_REG *result)
+void   poly_sine(FPU_REG *st0_ptr)
 {
   int                 exponent, echange;
   Xsig                accumulator, argSqrd, argTo4;
   unsigned long       fix_up, adj;
   unsigned long long  fixed_arg;
+  FPU_REG            result;
 
-
-#ifdef PARANOID
-  if ( arg->tag == TW_Zero )
-    {
-      /* Return 0.0 */
-      reg_move(&CONST_Z, result);
-      return;
-    }
-#endif /* PARANOID */
-
-  exponent = arg->exp - EXP_BIAS;
+  exponent = exponent(st0_ptr);
 
   accumulator.lsw = accumulator.midw = accumulator.msw = 0;
 
   /* Split into two ranges, for arguments below and above 1.0 */
   /* The boundary between upper and lower is approx 0.88309101259 */
-  if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xe21240aa)) )
+  if ( (exponent < -1) || ((exponent == -1) && (st0_ptr->sigh <= 0xe21240aa)) )
     {
       /* The argument is <= 0.88309101259 */
 
-      argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0;
-      mul64_Xsig(&argSqrd, &significand(arg));
+      argSqrd.msw = st0_ptr->sigh; argSqrd.midw = st0_ptr->sigl; argSqrd.lsw = 0;
+      mul64_Xsig(&argSqrd, &significand(st0_ptr));
       shr_Xsig(&argSqrd, 2*(-1-exponent));
       argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw;
       argTo4.lsw = argSqrd.lsw;
@@ -107,29 +99,29 @@ void       poly_sine(FPU_REG const *arg, FPU_REG *result)
       shr_Xsig(&accumulator, 2);    /* Divide by four */
       accumulator.msw |= 0x80000000;  /* Add 1.0 */
 
-      mul64_Xsig(&accumulator, &significand(arg));
-      mul64_Xsig(&accumulator, &significand(arg));
-      mul64_Xsig(&accumulator, &significand(arg));
+      mul64_Xsig(&accumulator, &significand(st0_ptr));
+      mul64_Xsig(&accumulator, &significand(st0_ptr));
+      mul64_Xsig(&accumulator, &significand(st0_ptr));
 
       /* Divide by four, FPU_REG compatible, etc */
-      exponent = 3*exponent + EXP_BIAS;
+      exponent = 3*exponent;
 
       /* The minimum exponent difference is 3 */
-      shr_Xsig(&accumulator, arg->exp - exponent);
+      shr_Xsig(&accumulator, exponent(st0_ptr) - exponent);
 
       negate_Xsig(&accumulator);
-      XSIG_LL(accumulator) += significand(arg);
+      XSIG_LL(accumulator) += significand(st0_ptr);
 
       echange = round_Xsig(&accumulator);
 
-      result->exp = arg->exp + echange;
+      setexponentpos(&result, exponent(st0_ptr) + echange);
     }
   else
     {
       /* The argument is > 0.88309101259 */
-      /* We use sin(arg) = cos(pi/2-arg) */
+      /* We use sin(st(0)) = cos(pi/2-st(0)) */
 
-      fixed_arg = significand(arg);
+      fixed_arg = significand(st0_ptr);
 
       if ( exponent == 0 )
        {
@@ -140,6 +132,9 @@ void        poly_sine(FPU_REG const *arg, FPU_REG *result)
        }
       /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
       fixed_arg = 0x921fb54442d18469LL - fixed_arg;
+      /* There is a special case which arises due to rounding, to fix here. */
+      if ( fixed_arg == 0xffffffffffffffffLL )
+       fixed_arg = 0;
 
       XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0;
       mul64_Xsig(&argSqrd, &fixed_arg);
@@ -180,10 +175,9 @@ void       poly_sine(FPU_REG const *arg, FPU_REG *result)
       if ( argSqrd.msw & 0xffc00000 )
        {
          /* Get about 32 bit precision in these: */
-         mul_32_32(0x898cc517, argSqrd.msw, &adj);
-         fix_up -= adj/6;
+         fix_up -= mul_32_32(0x898cc517, argSqrd.msw) / 6;
        }
-      mul_32_32(fix_up, LL_MSW(fixed_arg), &fix_up);
+      fix_up = mul_32_32(fix_up, LL_MSW(fixed_arg));
 
       adj = accumulator.lsw;    /* temp save */
       accumulator.lsw -= fix_up;
@@ -192,16 +186,16 @@ void      poly_sine(FPU_REG const *arg, FPU_REG *result)
 
       echange = round_Xsig(&accumulator);
 
-      result->exp = EXP_BIAS - 1 + echange;
+      setexponentpos(&result, echange - 1);
     }
 
-  significand(result) = XSIG_LL(accumulator);
-  result->tag = TW_Valid;
-  result->sign = arg->sign;
+  significand(&result) = XSIG_LL(accumulator);
+  setsign(&result, getsign(st0_ptr));
+  FPU_copy_to_reg0(&result, TAG_Valid);
 
 #ifdef PARANOID
-  if ( (result->exp >= EXP_BIAS)
-      && (significand(result) > 0x8000000000000000LL) )
+  if ( (exponent(&result) >= 0)
+      && (significand(&result) > 0x8000000000000000LL) )
     {
       EXCEPTION(EX_INTERNAL|0x150);
     }
@@ -214,42 +208,35 @@ void      poly_sine(FPU_REG const *arg, FPU_REG *result)
 /*--- poly_cos() ------------------------------------------------------------+
  |                                                                           |
  +---------------------------------------------------------------------------*/
-void   poly_cos(FPU_REG const *arg, FPU_REG *result)
+void   poly_cos(FPU_REG *st0_ptr)
 {
+  FPU_REG            result;
   long int            exponent, exp2, echange;
   Xsig                accumulator, argSqrd, fix_up, argTo4;
-  unsigned long       adj;
   unsigned long long  fixed_arg;
 
-
 #ifdef PARANOID
-  if ( arg->tag == TW_Zero )
-    {
-      /* Return 1.0 */
-      reg_move(&CONST_1, result);
-      return;
-    }
-
-  if ( (arg->exp > EXP_BIAS)
-      || ((arg->exp == EXP_BIAS)
-         && (significand(arg) > 0xc90fdaa22168c234LL)) )
+  if ( (exponent(st0_ptr) > 0)
+      || ((exponent(st0_ptr) == 0)
+         && (significand(st0_ptr) > 0xc90fdaa22168c234LL)) )
     {
       EXCEPTION(EX_Invalid);
-      reg_move(&CONST_QNaN, result);
+      FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
       return;
     }
 #endif /* PARANOID */
 
-  exponent = arg->exp - EXP_BIAS;
+  exponent = exponent(st0_ptr);
 
   accumulator.lsw = accumulator.midw = accumulator.msw = 0;
 
-  if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xb00d6f54)) )
+  if ( (exponent < -1) || ((exponent == -1) && (st0_ptr->sigh <= 0xb00d6f54)) )
     {
       /* arg is < 0.687705 */
 
-      argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0;
-      mul64_Xsig(&argSqrd, &significand(arg));
+      argSqrd.msw = st0_ptr->sigh; argSqrd.midw = st0_ptr->sigl;
+      argSqrd.lsw = 0;
+      mul64_Xsig(&argSqrd, &significand(st0_ptr));
 
       if ( exponent < -1 )
        {
@@ -270,8 +257,8 @@ void        poly_cos(FPU_REG const *arg, FPU_REG *result)
                      N_COEFF_PH-1);
       negate_Xsig(&accumulator);
 
-      mul64_Xsig(&accumulator, &significand(arg));
-      mul64_Xsig(&accumulator, &significand(arg));
+      mul64_Xsig(&accumulator, &significand(st0_ptr));
+      mul64_Xsig(&accumulator, &significand(st0_ptr));
       shr_Xsig(&accumulator, -2*(1+exponent));
 
       shr_Xsig(&accumulator, 3);
@@ -290,20 +277,20 @@ void      poly_cos(FPU_REG const *arg, FPU_REG *result)
       if ( accumulator.msw == 0 )
        {
          /* The result is 1.0 */
-         reg_move(&CONST_1, result);
+         FPU_copy_to_reg0(&CONST_1, TAG_Valid);
+         return;
        }
       else
        {
-         significand(result) = XSIG_LL(accumulator);
+         significand(&result) = XSIG_LL(accumulator);
       
          /* will be a valid positive nr with expon = -1 */
-         *(short *)&(result->sign) = 0;
-         result->exp = EXP_BIAS - 1;
+         setexponentpos(&result, -1);
        }
     }
   else
     {
-      fixed_arg = significand(arg);
+      fixed_arg = significand(st0_ptr);
 
       if ( exponent == 0 )
        {
@@ -314,6 +301,9 @@ void        poly_cos(FPU_REG const *arg, FPU_REG *result)
        }
       /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
       fixed_arg = 0x921fb54442d18469LL - fixed_arg;
+      /* There is a special case which arises due to rounding, to fix here. */
+      if ( fixed_arg == 0xffffffffffffffffLL )
+       fixed_arg = 0;
 
       exponent = -1;
       exp2 = -1;
@@ -377,10 +367,8 @@ void       poly_cos(FPU_REG const *arg, FPU_REG *result)
       if ( argSqrd.msw & 0xffc00000 )
        {
          /* Get about 32 bit precision in these: */
-         mul_32_32(0x898cc517, argSqrd.msw, &adj);
-         fix_up.msw -= adj/2;
-         mul_32_32(0x898cc517, argTo4.msw, &adj);
-         fix_up.msw += adj/24;
+         fix_up.msw -= mul_32_32(0x898cc517, argSqrd.msw) / 2;
+         fix_up.msw += mul_32_32(0x898cc517, argTo4.msw) / 24;
        }
 
       exp2 += norm_Xsig(&accumulator);
@@ -392,14 +380,15 @@ void      poly_cos(FPU_REG const *arg, FPU_REG *result)
 
       echange = round_Xsig(&accumulator);
 
-      result->exp = exp2 + EXP_BIAS + echange;
-      *(short *)&(result->sign) = 0;      /* Is a valid positive nr */
-      significand(result) = XSIG_LL(accumulator);
+      setexponentpos(&result, exp2 + echange);
+      significand(&result) = XSIG_LL(accumulator);
     }
 
+  FPU_copy_to_reg0(&result, TAG_Valid);
+
 #ifdef PARANOID
-  if ( (result->exp >= EXP_BIAS)
-      && (significand(result) > 0x8000000000000000LL) )
+  if ( (exponent(&result) >= 0)
+      && (significand(&result) > 0x8000000000000000LL) )
     {
       EXCEPTION(EX_INTERNAL|0x151);
     }
index fe6f1f17e90135dc9b8aa407dfb0b04d43e4ee5d..8df3e03b6e6f658c9fdcd98e300e10235765c64e 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Compute the tan of a FPU_REG, using a polynomial approximation.           |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
+ | Copyright (C) 1992,1993,1994,1997,1999                                    |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -13,6 +13,7 @@
 #include "exception.h"
 #include "reg_constant.h"
 #include "fpu_emu.h"
+#include "fpu_system.h"
 #include "control_w.h"
 #include "poly.h"
 
@@ -52,7 +53,7 @@ static const unsigned long long twothirds = 0xaaaaaaaaaaaaaaabLL;
 /*--- poly_tan() ------------------------------------------------------------+
  |                                                                           |
  +---------------------------------------------------------------------------*/
-void   poly_tan(FPU_REG const *arg, FPU_REG *result)
+void   poly_tan(FPU_REG *st0_ptr)
 {
   long int             exponent;
   int                   invert;
@@ -60,20 +61,20 @@ void        poly_tan(FPU_REG const *arg, FPU_REG *result)
                         argSignif, fix_up;
   unsigned long         adj;
 
-  exponent = arg->exp - EXP_BIAS;
+  exponent = exponent(st0_ptr);
 
 #ifdef PARANOID
-  if ( arg->sign != 0 )        /* Can't hack a number < 0.0 */
-    { arith_invalid(result); return; }  /* Need a positive number */
+  if ( signnegative(st0_ptr) ) /* Can't hack a number < 0.0 */
+    { arith_invalid(0); return; }  /* Need a positive number */
 #endif /* PARANOID */
 
   /* Split the problem into two domains, smaller and larger than pi/4 */
-  if ( (exponent == 0) || ((exponent == -1) && (arg->sigh > 0xc90fdaa2)) )
+  if ( (exponent == 0) || ((exponent == -1) && (st0_ptr->sigh > 0xc90fdaa2)) )
     {
       /* The argument is greater than (approx) pi/4 */
       invert = 1;
       accum.lsw = 0;
-      XSIG_LL(accum) = significand(arg);
+      XSIG_LL(accum) = significand(st0_ptr);
  
       if ( exponent == 0 )
        {
@@ -83,6 +84,14 @@ void poly_tan(FPU_REG const *arg, FPU_REG *result)
        }
       /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
       XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum);
+      /* This is a special case which arises due to rounding. */
+      if ( XSIG_LL(accum) == 0xffffffffffffffffLL )
+       {
+         FPU_settag0(TAG_Valid);
+         significand(st0_ptr) = 0x8a51e04daabda360LL;
+         setexponent16(st0_ptr, (0x41 + EXTENDED_Ebias) | SIGN_Negative);
+         return;
+       }
 
       argSignif.lsw = accum.lsw;
       XSIG_LL(argSignif) = XSIG_LL(accum);
@@ -92,12 +101,12 @@ void       poly_tan(FPU_REG const *arg, FPU_REG *result)
     {
       invert = 0;
       argSignif.lsw = 0;
-      XSIG_LL(accum) = XSIG_LL(argSignif) = significand(arg);
+      XSIG_LL(accum) = XSIG_LL(argSignif) = significand(st0_ptr);
  
       if ( exponent < -1 )
        {
          /* shift the argument right by the required places */
-         if ( shrx(&XSIG_LL(accum), -1-exponent) >= 0x80000000U )
+         if ( FPU_shrx(&XSIG_LL(accum), -1-exponent) >= 0x80000000U )
            XSIG_LL(accum) ++;  /* round up */
        }
     }
@@ -176,11 +185,11 @@ void      poly_tan(FPU_REG const *arg, FPU_REG *result)
       else if ( exponent > -30 )
        {
          adj = accum.msw >> -(exponent+1);      /* tan */
-         mul_32_32(adj, adj, &adj);           /* tan^2 */
+         adj = mul_32_32(adj, adj);             /* tan^2 */
        }
       else
        adj = 0;
-      mul_32_32(0x898cc517, adj, &adj);        /* delta * tan^2 */
+      adj = mul_32_32(0x898cc517, adj);          /* delta * tan^2 */
 
       fix_up.msw += adj;
       if ( !(fix_up.msw & 0x80000000) )   /* did fix_up overflow ? */
@@ -206,8 +215,8 @@ void        poly_tan(FPU_REG const *arg, FPU_REG *result)
 
   /* Transfer the result */
   round_Xsig(&accum);
-  *(short *)&(result->sign) = 0;
-  significand(result) = XSIG_LL(accum);
-  result->exp = EXP_BIAS + exponent;
+  FPU_settag0(TAG_Valid);
+  significand(st0_ptr) = XSIG_LL(accum);
+  setexponent16(st0_ptr, exponent + EXTENDED_Ebias);  /* Result is positive. */
 
 }
index 639c1f3d14a968f2e18e7a2df963268c10b9d368..05e86d624a30450c9feeb94b09204e92bb6b35fa 100644 (file)
@@ -3,16 +3,19 @@
  |                                                                           |
  | Functions to add or subtract two registers and put the result in a third. |
  |                                                                           |
- | Copyright (C) 1992,1993                                                   |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1997                                              |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
 /*---------------------------------------------------------------------------+
- | For each function, the destination may be any FPU_REG, including one of   |
+ |  For each function, the destination may be any FPU_REG, including one of  |
  | the source FPU_REGs.                                                      |
+ |  Each function returns 0 if the answer is o.k., otherwise a non-zero      |
+ | value is returned, indicating either an exception condition or an         |
+ | internal error.                                                           |
  +---------------------------------------------------------------------------*/
 
 #include "exception.h"
 #include "control_w.h"
 #include "fpu_system.h"
 
+static
+int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
+                    FPU_REG const *b, u_char tagb, u_char signb,
+                    FPU_REG *dest, int deststnr, int control_w);
 
-int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
+/*
+  Operates on st(0) and st(n), or on st(0) and temporary data.
+  The destination must be one of the source st(x).
+  */
+int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w)
 {
-  char saved_sign = dest->sign;
-  int diff;
+  FPU_REG *a = &st(0);
+  FPU_REG *dest = &st(deststnr);
+  u_char signb = getsign(b);
+  u_char taga = FPU_gettag0();
+  u_char signa = getsign(a);
+  u_char saved_sign = getsign(dest);
+  int diff, tag, expa, expb;
   
-  if ( !(a->tag | b->tag) )
+  if ( !(taga | tagb) )
     {
+      expa = exponent(a);
+      expb = exponent(b);
+
+    valid_add:
       /* Both registers are valid */
-      if (!(a->sign ^ b->sign))
+      if (!(signa ^ signb))
        {
          /* signs are the same */
-         dest->sign = a->sign;
-         if ( reg_u_add(a, b, dest, control_w) )
-           {
-             dest->sign = saved_sign;
-             return 1;
-           }
-         return 0;
+         tag = FPU_u_add(a, b, dest, control_w, signa, expa, expb);
        }
-      
-      /* The signs are different, so do a subtraction */
-      diff = a->exp - b->exp;
-      if (!diff)
+      else
        {
-         diff = a->sigh - b->sigh;  /* Works only if ms bits are identical */
+         /* The signs are different, so do a subtraction */
+         diff = expa - expb;
          if (!diff)
            {
-             diff = a->sigl > b->sigl;
+             diff = a->sigh - b->sigh;  /* This works only if the ms bits
+                                           are identical. */
              if (!diff)
-               diff = -(a->sigl < b->sigl);
-           }
-       }
-      
-      if (diff > 0)
-       {
-         dest->sign = a->sign;
-         if ( reg_u_sub(a, b, dest, control_w) )
-           {
-             dest->sign = saved_sign;
-             return 1;
-           }
-       }
-      else if ( diff == 0 )
-       {
-#ifdef DENORM_OPERAND
-         if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
-             denormal_operand() )
-           return 1;
-#endif /* DENORM_OPERAND */
-         reg_move(&CONST_Z, dest);
-         /* sign depends upon rounding mode */
-         dest->sign = ((control_w & CW_RC) != RC_DOWN)
-           ? SIGN_POS : SIGN_NEG;
-       }
-      else
-       {
-         dest->sign = b->sign;
-         if ( reg_u_sub(b, a, dest, control_w) )
-           {
-             dest->sign = saved_sign;
-             return 1;
-           }
-       }
-      return 0;
-    }
-  else
-    {
-      if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
-       { return real_2op_NaN(a, b, dest); }
-      else if (a->tag == TW_Zero)
-       {
-         if (b->tag == TW_Zero)
-           {
-             char different_signs = a->sign ^ b->sign;
-             /* Both are zero, result will be zero. */
-             reg_move(a, dest);
-             if (different_signs)
                {
-                 /* Signs are different. */
-                 /* Sign of answer depends upon rounding mode. */
-                 dest->sign = ((control_w & CW_RC) != RC_DOWN)
-                   ? SIGN_POS : SIGN_NEG;
+                 diff = a->sigl > b->sigl;
+                 if (!diff)
+                   diff = -(a->sigl < b->sigl);
                }
            }
-         else
+      
+         if (diff > 0)
            {
-#ifdef DENORM_OPERAND
-             if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
-                 denormal_operand() )
-               return 1;
-#endif /* DENORM_OPERAND */
-             reg_move(b, dest);
+             tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb);
            }
-         return 0;
-       }
-      else if (b->tag == TW_Zero)
-       {
-#ifdef DENORM_OPERAND
-         if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
-             denormal_operand() )
-           return 1;
-#endif /* DENORM_OPERAND */
-         reg_move(a, dest); return 0;
-       }
-      else if (a->tag == TW_Infinity)
-       {
-         if (b->tag != TW_Infinity)
+         else if ( diff < 0 )
            {
-#ifdef DENORM_OPERAND
-             if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
-                 denormal_operand() )
-               return 1;
-#endif /* DENORM_OPERAND */
-             reg_move(a, dest); return 0;
+             tag = FPU_u_sub(b, a, dest, control_w, signb, expb, expa);
            }
-         if (a->sign == b->sign)
+         else
            {
-             /* They are both + or - infinity */
-             reg_move(a, dest); return 0;
+             FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
+             /* sign depends upon rounding mode */
+             setsign(dest, ((control_w & CW_RC) != RC_DOWN)
+                     ? SIGN_POS : SIGN_NEG);
+             return TAG_Zero;
            }
-         return arith_invalid(dest);   /* Infinity-Infinity is undefined. */
        }
-      else if (b->tag == TW_Infinity)
+
+      if ( tag < 0 )
        {
-#ifdef DENORM_OPERAND
-         if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
-             denormal_operand() )
-           return 1;
-#endif /* DENORM_OPERAND */
-         reg_move(b, dest); return 0;
+         setsign(dest, saved_sign);
+         return tag;
        }
+      FPU_settagi(deststnr, tag);
+      return tag;
     }
-#ifdef PARANOID
-  EXCEPTION(EX_INTERNAL|0x101);
-#endif
-  return 1;
+
+  if ( taga == TAG_Special )
+    taga = FPU_Special(a);
+  if ( tagb == TAG_Special )
+    tagb = FPU_Special(b);
+
+  if ( ((taga == TAG_Valid) && (tagb == TW_Denormal))
+           || ((taga == TW_Denormal) && (tagb == TAG_Valid))
+           || ((taga == TW_Denormal) && (tagb == TW_Denormal)) )
+    {
+      FPU_REG x, y;
+
+      if ( denormal_operand() < 0 )
+       return FPU_Exception;
+
+      FPU_to_exp16(a, &x);
+      FPU_to_exp16(b, &y);
+      a = &x;
+      b = &y;
+      expa = exponent16(a);
+      expb = exponent16(b);
+      goto valid_add;
+    }
+
+  if ( (taga == TW_NaN) || (tagb == TW_NaN) )
+    {
+      if ( deststnr == 0 )
+       return real_2op_NaN(b, tagb, deststnr, a);
+      else
+       return real_2op_NaN(a, taga, deststnr, a);
+    }
+
+  return add_sub_specials(a, taga, signa, b, tagb, signb,
+                         dest, deststnr, control_w);
 }
 
 
 /* Subtract b from a.  (a-b) -> dest */
-int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
+int FPU_sub(int flags, int rm, int control_w)
 {
-  char saved_sign = dest->sign;
-  int diff;
+  FPU_REG const *a, *b;
+  FPU_REG *dest;
+  u_char taga, tagb, signa, signb, saved_sign, sign;
+  int diff, tag, expa, expb, deststnr;
+
+  a = &st(0);
+  taga = FPU_gettag0();
+
+  deststnr = 0;
+  if ( flags & LOADED )
+    {
+      b = (FPU_REG *)rm;
+      tagb = flags & 0x0f;
+    }
+  else
+    {
+      b = &st(rm);
+      tagb = FPU_gettagi(rm);
+
+      if ( flags & DEST_RM )
+       deststnr = rm;
+    }
+
+  signa = getsign(a);
+  signb = getsign(b);
+
+  if ( flags & REV )
+    {
+      signa ^= SIGN_NEG;
+      signb ^= SIGN_NEG;
+    }
 
-  if ( !(a->tag | b->tag) )
+  dest = &st(deststnr);
+  saved_sign = getsign(dest);
+
+  if ( !(taga | tagb) )
     {
+      expa = exponent(a);
+      expb = exponent(b);
+
+    valid_subtract:
       /* Both registers are valid */
-      diff = a->exp - b->exp;
+
+      diff = expa - expb;
+
       if (!diff)
        {
          diff = a->sigh - b->sigh;  /* Works only if ms bits are identical */
@@ -182,137 +193,182 @@ int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
            }
        }
 
-      switch (a->sign*2 + b->sign)
+      switch ( (((int)signa)*2 + signb) / SIGN_NEG )
        {
        case 0: /* P - P */
        case 3: /* N - N */
          if (diff > 0)
            {
              /* |a| > |b| */
-             dest->sign = a->sign;
-             if ( reg_u_sub(a, b, dest, control_w) )
-               {
-                 dest->sign = saved_sign;
-                 return 1;
-               }
-             return 0;
+             tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb);
            }
          else if ( diff == 0 )
            {
-#ifdef DENORM_OPERAND
-             if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
-                 denormal_operand() )
-               return 1;
-#endif /* DENORM_OPERAND */
-             reg_move(&CONST_Z, dest);
+             FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
+
              /* sign depends upon rounding mode */
-             dest->sign = ((control_w & CW_RC) != RC_DOWN)
-               ? SIGN_POS : SIGN_NEG;
+             setsign(dest, ((control_w & CW_RC) != RC_DOWN)
+               ? SIGN_POS : SIGN_NEG);
+             return TAG_Zero;
            }
          else
            {
-             dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
-             if ( reg_u_sub(b, a, dest, control_w) )
-               {
-                 dest->sign = saved_sign;
-                 return 1;
-               }
+             sign = signa ^ SIGN_NEG;
+             tag = FPU_u_sub(b, a, dest, control_w, sign, expb, expa);
            }
          break;
        case 1: /* P - N */
-         dest->sign = SIGN_POS;
-         if ( reg_u_add(a, b, dest, control_w) )
-           {
-             dest->sign = saved_sign;
-             return 1;
-           }
+         tag = FPU_u_add(a, b, dest, control_w, SIGN_POS, expa, expb);
          break;
        case 2: /* N - P */
-         dest->sign = SIGN_NEG;
-         if ( reg_u_add(a, b, dest, control_w) )
-           {
-             dest->sign = saved_sign;
-             return 1;
-           }
+         tag = FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa, expb);
          break;
+#ifdef PARANOID
+       default:
+         EXCEPTION(EX_INTERNAL|0x111);
+         return -1;
+#endif
+       }
+      if ( tag < 0 )
+       {
+         setsign(dest, saved_sign);
+         return tag;
        }
-      return 0;
+      FPU_settagi(deststnr, tag);
+      return tag;
     }
-  else
+
+  if ( taga == TAG_Special )
+    taga = FPU_Special(a);
+  if ( tagb == TAG_Special )
+    tagb = FPU_Special(b);
+
+  if ( ((taga == TAG_Valid) && (tagb == TW_Denormal))
+           || ((taga == TW_Denormal) && (tagb == TAG_Valid))
+           || ((taga == TW_Denormal) && (tagb == TW_Denormal)) )
     {
-      if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
-       { return real_2op_NaN(b, a, dest); }
-      else if (b->tag == TW_Zero)
-       { 
-         if (a->tag == TW_Zero)
-           {
-             char same_signs = !(a->sign ^ b->sign);
-             /* Both are zero, result will be zero. */
-             reg_move(a, dest); /* Answer for different signs. */
-             if (same_signs)
-               {
-                 /* Sign depends upon rounding mode */
-                 dest->sign = ((control_w & CW_RC) != RC_DOWN)
-                   ? SIGN_POS : SIGN_NEG;
-               }
-           }
-         else
-           {
-#ifdef DENORM_OPERAND
-             if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
-                 denormal_operand() )
-               return 1;
-#endif /* DENORM_OPERAND */
-             reg_move(a, dest);
-           }
-         return 0;
+      FPU_REG x, y;
+
+      if ( denormal_operand() < 0 )
+       return FPU_Exception;
+
+      FPU_to_exp16(a, &x);
+      FPU_to_exp16(b, &y);
+      a = &x;
+      b = &y;
+      expa = exponent16(a);
+      expb = exponent16(b);
+
+      goto valid_subtract;
+    }
+
+  if ( (taga == TW_NaN) || (tagb == TW_NaN) )
+    {
+      FPU_REG const *d1, *d2;
+      if ( flags & REV )
+       {
+         d1 = b;
+         d2 = a;
        }
-      else if (a->tag == TW_Zero)
+      else
        {
-#ifdef DENORM_OPERAND
-         if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
-             denormal_operand() )
-           return 1;
-#endif /* DENORM_OPERAND */
-         reg_move(b, dest);
-         dest->sign ^= SIGN_POS^SIGN_NEG;
-         return 0;
+         d1 = a;
+         d2 = b;
        }
-      else if (a->tag == TW_Infinity)
+      if ( flags & LOADED )
+       return real_2op_NaN(b, tagb, deststnr, d1);
+      if ( flags & DEST_RM )
+       return real_2op_NaN(a, taga, deststnr, d2);
+      else
+       return real_2op_NaN(b, tagb, deststnr, d2);
+    }
+
+    return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG,
+                           dest, deststnr, control_w);
+}
+
+
+static
+int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
+                    FPU_REG const *b, u_char tagb, u_char signb,
+                    FPU_REG *dest, int deststnr, int control_w)
+{
+  if ( ((taga == TW_Denormal) || (tagb == TW_Denormal))
+       && (denormal_operand() < 0) )
+    return FPU_Exception;
+
+  if (taga == TAG_Zero)
+    {
+      if (tagb == TAG_Zero)
        {
-         if (b->tag != TW_Infinity)
+         /* Both are zero, result will be zero. */
+         u_char different_signs = signa ^ signb;
+
+         FPU_copy_to_regi(a, TAG_Zero, deststnr);
+         if ( different_signs )
            {
-#ifdef DENORM_OPERAND
-             if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
-                 denormal_operand() )
-               return 1;
-#endif /* DENORM_OPERAND */
-             reg_move(a, dest); return 0;
+             /* Signs are different. */
+             /* Sign of answer depends upon rounding mode. */
+             setsign(dest, ((control_w & CW_RC) != RC_DOWN)
+                     ? SIGN_POS : SIGN_NEG);
            }
-         /* Both args are Infinity */
-         if (a->sign == b->sign)
+         else
+           setsign(dest, signa);  /* signa may differ from the sign of a. */
+         return TAG_Zero;
+       }
+      else
+       {
+         reg_copy(b, dest);
+         if ( (tagb == TW_Denormal) && (b->sigh & 0x80000000) )
            {
-             /* Infinity-Infinity is undefined. */
-             return arith_invalid(dest);
+             /* A pseudoDenormal, convert it. */
+             addexponent(dest, 1);
+             tagb = TAG_Valid;
            }
-         reg_move(a, dest);
-         return 0;
+         else if ( tagb > TAG_Empty )
+           tagb = TAG_Special;
+         setsign(dest, signb);  /* signb may differ from the sign of b. */
+         FPU_settagi(deststnr, tagb);
+         return tagb;
        }
-      else if (b->tag == TW_Infinity)
+    }
+  else if (tagb == TAG_Zero)
+    {
+      reg_copy(a, dest);
+      if ( (taga == TW_Denormal) && (a->sigh & 0x80000000) )
        {
-#ifdef DENORM_OPERAND
-         if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
-             denormal_operand() )
-           return 1;
-#endif /* DENORM_OPERAND */
-         reg_move(b, dest);
-         dest->sign ^= SIGN_POS^SIGN_NEG;
-         return 0;
+         /* A pseudoDenormal */
+         addexponent(dest, 1);
+         taga = TAG_Valid;
        }
+      else if ( taga > TAG_Empty )
+       taga = TAG_Special;
+      setsign(dest, signa);  /* signa may differ from the sign of a. */
+      FPU_settagi(deststnr, taga);
+      return taga;
     }
+  else if (taga == TW_Infinity)
+    {
+      if ( (tagb != TW_Infinity) || (signa == signb) )
+       {
+         FPU_copy_to_regi(a, TAG_Special, deststnr);
+         setsign(dest, signa);  /* signa may differ from the sign of a. */
+         return taga;
+       }
+      /* Infinity-Infinity is undefined. */
+      return arith_invalid(deststnr);
+    }
+  else if (tagb == TW_Infinity)
+    {
+      FPU_copy_to_regi(b, TAG_Special, deststnr);
+      setsign(dest, signb);  /* signb may differ from the sign of b. */
+      return tagb;
+    }
+
 #ifdef PARANOID
-  EXCEPTION(EX_INTERNAL|0x110);
+  EXCEPTION(EX_INTERNAL|0x101);
 #endif
-  return 1;
+
+  return FPU_Exception;
 }
 
index 703318ada55f714949f49471b07d8175be1bfc91..0f9da21495f430a4c4ced27a6da488be02ea5dc4 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Compare two floating point registers                                      |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1994,1997                                         |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 #include "status_w.h"
 
 
-int compare(FPU_REG const *b)
+static void setflags(unsigned char flags)
 {
-  int diff;
-  char        st0_tag;
-  FPU_REG      *st0_ptr;
+  *(unsigned char *)&FPU_EFLAGS &= ~SFLAGS;
+  *(unsigned char *)&FPU_EFLAGS |= (flags & SFLAGS);
+}
+
+
+static int compare(FPU_REG const *b, int tagb)
+{
+  int diff, exp0, expb;
+  u_char               st0_tag;
+  FPU_REG      *st0_ptr;
+  FPU_REG      x, y;
+  u_char               st0_sign, signb = getsign(b);
 
   st0_ptr = &st(0);
-  st0_tag = st0_ptr->tag;
+  st0_tag = FPU_gettag0();
+  st0_sign = getsign(st0_ptr);
+
+  if ( tagb == TAG_Special )
+    tagb = FPU_Special(b);
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
 
-  if ( st0_tag | b->tag )
+  if ( ((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
+       || ((tagb != TAG_Valid) && (tagb != TW_Denormal)) )
     {
-      if ( st0_tag == TW_Zero )
+      if ( st0_tag == TAG_Zero )
        {
-         if ( b->tag == TW_Zero ) return COMP_A_eq_B;
-         if ( b->tag == TW_Valid )
-           {
-             return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
-#ifdef DENORM_OPERAND
-               | ((b->exp <= EXP_UNDER) ?
-                  COMP_Denormal : 0)
-#endif /* DENORM_OPERAND */
-                 ;
-           }
+         if ( tagb == TAG_Zero ) return COMP_A_eq_B;
+         if ( tagb == TAG_Valid )
+           return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
+         if ( tagb == TW_Denormal )
+           return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
+           | COMP_Denormal;
        }
-      else if ( b->tag == TW_Zero )
+      else if ( tagb == TAG_Zero )
        {
-         if ( st0_tag == TW_Valid )
-           {
-             return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
-                     : COMP_A_lt_B)
-#ifdef DENORM_OPERAND
-               | ((st0_ptr->exp <= EXP_UNDER )
-                  ? COMP_Denormal : 0 )
-#endif /* DENORM_OPERAND */
-                 ;
-           }
+         if ( st0_tag == TAG_Valid )
+           return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
+         if ( st0_tag == TW_Denormal )
+           return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
+           | COMP_Denormal;
        }
 
       if ( st0_tag == TW_Infinity )
        {
-         if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) )
-           {
-             return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
-                     : COMP_A_lt_B)
-#ifdef DENORM_OPERAND
-             | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ?
-               COMP_Denormal : 0 )
-#endif /* DENORM_OPERAND */
-;
-           }
-         else if ( b->tag == TW_Infinity )
+         if ( (tagb == TAG_Valid) || (tagb == TAG_Zero) )
+           return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
+         else if ( tagb == TW_Denormal )
+           return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
+             | COMP_Denormal;
+         else if ( tagb == TW_Infinity )
            {
              /* The 80486 book says that infinities can be equal! */
-             return (st0_ptr->sign == b->sign) ? COMP_A_eq_B :
-               ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
+             return (st0_sign == signb) ? COMP_A_eq_B :
+               ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
            }
          /* Fall through to the NaN code */
        }
-      else if ( b->tag == TW_Infinity )
+      else if ( tagb == TW_Infinity )
        {
-         if ( (st0_tag == TW_Valid) || (st0_tag == TW_Zero) )
-           {
-             return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
-#ifdef DENORM_OPERAND
-               | (((st0_tag == TW_Valid)
-                   && (st0_ptr->exp <= EXP_UNDER)) ?
-                  COMP_Denormal : 0)
-#endif /* DENORM_OPERAND */
-                 ;
-           }
+         if ( (st0_tag == TAG_Valid) || (st0_tag == TAG_Zero) )
+           return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
+         if ( st0_tag == TW_Denormal )
+           return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
+               | COMP_Denormal;
          /* Fall through to the NaN code */
        }
 
       /* The only possibility now should be that one of the arguments
         is a NaN */
-      if ( (st0_tag == TW_NaN) || (b->tag == TW_NaN) )
+      if ( (st0_tag == TW_NaN) || (tagb == TW_NaN) )
        {
-         if ( ((st0_tag == TW_NaN) && !(st0_ptr->sigh & 0x40000000))
-             || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) )
-           /* At least one arg is a signaling NaN */
+         int signalling = 0, unsupported = 0;
+         if ( st0_tag == TW_NaN )
+           {
+             signalling = (st0_ptr->sigh & 0xc0000000) == 0x80000000;
+             unsupported = !((exponent(st0_ptr) == EXP_OVER)
+                             && (st0_ptr->sigh & 0x80000000));
+           }
+         if ( tagb == TW_NaN )
+           {
+             signalling |= (b->sigh & 0xc0000000) == 0x80000000;
+             unsupported |= !((exponent(b) == EXP_OVER)
+                              && (b->sigh & 0x80000000));
+           }
+         if ( signalling || unsupported )
            return COMP_No_Comp | COMP_SNaN | COMP_NaN;
          else
            /* Neither is a signaling NaN */
            return COMP_No_Comp | COMP_NaN;
        }
-
+      
       EXCEPTION(EX_Invalid);
     }
+  
+  if (st0_sign != signb)
+    {
+      return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
+       | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
+           COMP_Denormal : 0);
+    }
+
+  if ( (st0_tag == TW_Denormal) || (tagb == TW_Denormal) )
+    {
+      FPU_to_exp16(st0_ptr, &x);
+      FPU_to_exp16(b, &y);
+      st0_ptr = &x;
+      b = &y;
+      exp0 = exponent16(st0_ptr);
+      expb = exponent16(b);
+    }
+  else
+    {
+      exp0 = exponent(st0_ptr);
+      expb = exponent(b);
+    }
 
 #ifdef PARANOID
   if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
   if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
 #endif /* PARANOID */
 
-
-  if (st0_ptr->sign != b->sign)
-    {
-      return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
-#ifdef DENORM_OPERAND
-       |
-         ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
-          COMP_Denormal : 0)
-#endif /* DENORM_OPERAND */
-           ;
-    }
-
-  diff = st0_ptr->exp - b->exp;
+  diff = exp0 - expb;
   if ( diff == 0 )
     {
       diff = st0_ptr->sigh - b->sigh;  /* Works only if ms bits are
@@ -142,42 +160,30 @@ int compare(FPU_REG const *b)
 
   if ( diff > 0 )
     {
-      return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
-#ifdef DENORM_OPERAND
-       |
-         ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
-          COMP_Denormal : 0)
-#endif /* DENORM_OPERAND */
-           ;
+      return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
+       | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
+           COMP_Denormal : 0);
     }
   if ( diff < 0 )
     {
-      return ((st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
-#ifdef DENORM_OPERAND
-       |
-         ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
-          COMP_Denormal : 0)
-#endif /* DENORM_OPERAND */
-           ;
+      return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
+       | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
+           COMP_Denormal : 0);
     }
 
   return COMP_A_eq_B
-#ifdef DENORM_OPERAND
-    |
-      ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
-       COMP_Denormal : 0)
-#endif /* DENORM_OPERAND */
-       ;
+    | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
+       COMP_Denormal : 0);
 
 }
 
 
 /* This function requires that st(0) is not empty */
-int compare_st_data(FPU_REG const *loaded_data)
+int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
 {
   int f, c;
 
-  c = compare(loaded_data);
+  c = compare(loaded_data, loaded_tag);
 
   if (c & COMP_NaN)
     {
@@ -209,28 +215,30 @@ int compare_st_data(FPU_REG const *loaded_data)
   setcc(f);
   if (c & COMP_Denormal)
     {
-      return denormal_operand();
+      return denormal_operand() < 0;
     }
   return 0;
 }
 
 
-static int compare_st_st(int nr)
+static int compare_st_st(int nr, int ftype)
 {
   int f, c;
+  FPU_REG *st_ptr;
 
   if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) )
     {
-      setcc(SW_C3 | SW_C2 | SW_C0);
+      setccf(ftype, SW_C3 | SW_C2 | SW_C0);
       /* Stack fault */
       EXCEPTION(EX_StackUnder);
       return !(control_word & CW_Invalid);
     }
 
-  c = compare(&st(nr));
+  st_ptr = &st(nr);
+  c = compare(st_ptr, FPU_gettagi(nr));
   if (c & COMP_NaN)
     {
-      setcc(SW_C3 | SW_C2 | SW_C0);
+      setccf(ftype, SW_C3 | SW_C2 | SW_C0);
       EXCEPTION(EX_Invalid);
       return !(control_word & CW_Invalid);
     }
@@ -256,31 +264,33 @@ static int compare_st_st(int nr)
        break;
 #endif /* PARANOID */
       }
-  setcc(f);
+  setccf(ftype, f);
   if (c & COMP_Denormal)
     {
-      return denormal_operand();
+      return denormal_operand() < 0;
     }
   return 0;
 }
 
 
-static int compare_u_st_st(int nr)
+static int compare_u_st_st(int nr, int ftype)
 {
   int f, c;
+  FPU_REG *st_ptr;
 
   if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) )
     {
-      setcc(SW_C3 | SW_C2 | SW_C0);
+      setccf(ftype, SW_C3 | SW_C2 | SW_C0);
       /* Stack fault */
       EXCEPTION(EX_StackUnder);
       return !(control_word & CW_Invalid);
     }
 
-  c = compare(&st(nr));
+  st_ptr = &st(nr);
+  c = compare(st_ptr, FPU_gettagi(nr));
   if (c & COMP_NaN)
     {
-      setcc(SW_C3 | SW_C2 | SW_C0);
+      setccf(ftype, SW_C3 | SW_C2 | SW_C0);
       if (c & COMP_SNaN)       /* This is the only difference between
                                  un-ordered and ordinary comparisons */
        {
@@ -309,12 +319,12 @@ static int compare_u_st_st(int nr)
        EXCEPTION(EX_INTERNAL|0x123);
        f = SW_C3 | SW_C2 | SW_C0;
        break;
-#endif /* PARANOID */
+#endif /* PARANOID */ 
       }
-  setcc(f);
+  setccf(ftype, f);
   if (c & COMP_Denormal)
     {
-      return denormal_operand();
+      return denormal_operand() < 0;
     }
   return 0;
 }
@@ -324,15 +334,15 @@ static int compare_u_st_st(int nr)
 void fcom_st()
 {
   /* fcom st(i) */
-  compare_st_st(FPU_rm);
+  compare_st_st(FPU_rm, 0);
 }
 
 
 void fcompst()
 {
   /* fcomp st(i) */
-  if ( !compare_st_st(FPU_rm) )
-    pop();
+  if ( !compare_st_st(FPU_rm, 0) )
+    FPU_pop();
 }
 
 
@@ -344,7 +354,7 @@ void fcompp()
       FPU_illegal();
       return;
     }
-  if ( !compare_st_st(1) )
+  if ( !compare_st_st(1, 0) )
       poppop();
 }
 
@@ -352,7 +362,7 @@ void fcompp()
 void fucom_()
 {
   /* fucom st(i) */
-  compare_u_st_st(FPU_rm);
+  compare_u_st_st(FPU_rm, 0);
 
 }
 
@@ -360,8 +370,8 @@ void fucom_()
 void fucomp()
 {
   /* fucomp st(i) */
-  if ( !compare_u_st_st(FPU_rm) )
-    pop();
+  if ( !compare_u_st_st(FPU_rm, 0) )
+    FPU_pop();
 }
 
 
@@ -370,9 +380,27 @@ void fucompp()
   /* fucompp */
   if (FPU_rm == 1)
     {
-      if ( !compare_u_st_st(1) )
+      if ( !compare_u_st_st(1, 0) )
        poppop();
     }
   else
     FPU_illegal();
 }
+
+
+#ifndef ONLY_486_FPU
+void fcomi()
+{
+  /* fcom st(i) */
+  compare_st_st(FPU_rm, 1);
+}
+
+
+void fucomi()
+{
+  /* fucom st(i) */
+  compare_u_st_st(FPU_rm, 1);
+
+}
+#endif /* ONLY_486_FPU */
+
index 1b2458eea2356c34a90402f490c52489e6c7fd1c..ffbfebd5186c4afa3e350aa46de95e04c2d94bd3 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | All of the constant FPU_REGs                                              |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1996                                         |
+ | Copyright (C) 1992,1993,1994,1997                                         |
  |                     W. Metzenthen, 22 Parker St, Ormond, Vic 3163,        |
- |                     Australia.  E-mail   billm@jacobi.maths.monash.edu.au |
+ |                     Australia.  E-mail   billm@suburbia.net               |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 #include "control_w.h"
 
 
-FPU_REG const CONST_1    = { SIGN_POS, TW_Valid, EXP_BIAS,
-                           0x00000000, 0x80000000 };
-FPU_REG const CONST_2    = { SIGN_POS, TW_Valid, EXP_BIAS+1,
-                           0x00000000, 0x80000000 };
-FPU_REG const CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1,
-                           0x00000000, 0x80000000 };
-FPU_REG const CONST_L2T  = { SIGN_POS, TW_Valid, EXP_BIAS+1,
-                           0xcd1b8afe, 0xd49a784b };
-FPU_REG const CONST_L2E  = { SIGN_POS, TW_Valid, EXP_BIAS,
-                           0x5c17f0bc, 0xb8aa3b29 };
-FPU_REG const CONST_PI   = { SIGN_POS, TW_Valid, EXP_BIAS+1,
-                           0x2168c235, 0xc90fdaa2 };
-FPU_REG const CONST_PI2  = { SIGN_POS, TW_Valid, EXP_BIAS,
-                           0x2168c235, 0xc90fdaa2 };
-FPU_REG const CONST_PI4  = { SIGN_POS, TW_Valid, EXP_BIAS-1,
-                           0x2168c235, 0xc90fdaa2 };
-FPU_REG const CONST_LG2  = { SIGN_POS, TW_Valid, EXP_BIAS-2,
-                           0xfbcff799, 0x9a209a84 };
-FPU_REG const CONST_LN2  = { SIGN_POS, TW_Valid, EXP_BIAS-1,
-                           0xd1cf79ac, 0xb17217f7 };
+#define MAKE_REG(s,e,l,h) { l, h, \
+                            ((EXTENDED_Ebias+(e)) | ((SIGN_##s != 0)*0x8000)) }
+
+FPU_REG const CONST_1    = MAKE_REG(POS, 0, 0x00000000, 0x80000000);
+FPU_REG const CONST_2    = MAKE_REG(POS, 1, 0x00000000, 0x80000000);
+FPU_REG const CONST_HALF = MAKE_REG(POS, -1, 0x00000000, 0x80000000);
+FPU_REG const CONST_L2T  = MAKE_REG(POS, 1, 0xcd1b8afe, 0xd49a784b);
+FPU_REG const CONST_L2E  = MAKE_REG(POS, 0, 0x5c17f0bc, 0xb8aa3b29);
+FPU_REG const CONST_PI   = MAKE_REG(POS, 1, 0x2168c235, 0xc90fdaa2);
+FPU_REG const CONST_PI2  = MAKE_REG(POS, 0, 0x2168c235, 0xc90fdaa2);
+FPU_REG const CONST_PI4  = MAKE_REG(POS, -1, 0x2168c235, 0xc90fdaa2);
+FPU_REG const CONST_LG2  = MAKE_REG(POS, -2, 0xfbcff799, 0x9a209a84);
+FPU_REG const CONST_LN2  = MAKE_REG(POS, -1, 0xd1cf79ac, 0xb17217f7);
 
 /* Extra bits to take pi/2 to more than 128 bits precision. */
-FPU_REG const CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66,
-                           0xfc8f8cbb, 0xece675d1 };
+FPU_REG const CONST_PI2extra = MAKE_REG(NEG, -66,
+                                        0xfc8f8cbb, 0xece675d1);
 
 /* Only the sign (and tag) is used in internal zeroes */
-FPU_REG const CONST_Z    = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 };
+FPU_REG const CONST_Z    = MAKE_REG(POS, EXP_UNDER, 0x0, 0x0);
 
 /* Only the sign and significand (and tag) are used in internal NaNs */
 /* The 80486 never generates one of these 
-FPU_REG const CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 };
+FPU_REG const CONST_SNAN = MAKE_REG(POS, EXP_OVER, 0x00000001, 0x80000000);
  */
 /* This is the real indefinite QNaN */
-FPU_REG const CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 };
+FPU_REG const CONST_QNaN = MAKE_REG(NEG, EXP_OVER, 0x00000000, 0xC0000000);
 
 /* Only the sign (and tag) is used in internal infinities */
-FPU_REG const CONST_INF  = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 };
-
+FPU_REG const CONST_INF  = MAKE_REG(POS, EXP_OVER, 0x00000000, 0x80000000);
 
 
-static void fld_const(FPU_REG const *c, int adj)
+static void fld_const(FPU_REG const *c, int adj, u_char tag)
 {
   FPU_REG *st_new_ptr;
 
   if ( STACK_OVERFLOW )
     {
-      stack_overflow();
+      FPU_stack_overflow();
       return;
     }
   push();
-  reg_move(c, st_new_ptr);
+  reg_copy(c, st_new_ptr);
   st_new_ptr->sigl += adj;  /* For all our fldxxx constants, we don't need to
                               borrow or carry. */
+  FPU_settag0(tag);
   clear_C1();
 }
 
@@ -80,37 +73,37 @@ static void fld_const(FPU_REG const *c, int adj)
 
 static void fld1(int rc)
 {
-  fld_const(&CONST_1, 0);
+  fld_const(&CONST_1, 0, TAG_Valid);
 }
 
 static void fldl2t(int rc)
 {
-  fld_const(&CONST_L2T, (rc == RC_UP) ? 1 : 0);
+  fld_const(&CONST_L2T, (rc == RC_UP) ? 1 : 0, TAG_Valid);
 }
 
 static void fldl2e(int rc)
 {
-  fld_const(&CONST_L2E, DOWN_OR_CHOP(rc) ? -1 : 0);
+  fld_const(&CONST_L2E, DOWN_OR_CHOP(rc) ? -1 : 0, TAG_Valid);
 }
 
 static void fldpi(int rc)
 {
-  fld_const(&CONST_PI, DOWN_OR_CHOP(rc) ? -1 : 0);
+  fld_const(&CONST_PI, DOWN_OR_CHOP(rc) ? -1 : 0, TAG_Valid);
 }
 
 static void fldlg2(int rc)
 {
-  fld_const(&CONST_LG2, DOWN_OR_CHOP(rc) ? -1 : 0);
+  fld_const(&CONST_LG2, DOWN_OR_CHOP(rc) ? -1 : 0, TAG_Valid);
 }
 
 static void fldln2(int rc)
 {
-  fld_const(&CONST_LN2, DOWN_OR_CHOP(rc) ? -1 : 0);
+  fld_const(&CONST_LN2, DOWN_OR_CHOP(rc) ? -1 : 0, TAG_Valid);
 }
 
 static void fldz(int rc)
 {
-  fld_const(&CONST_Z, 0);
+  fld_const(&CONST_Z, 0, TAG_Zero);
 }
 
 typedef void (*FUNC_RC)(int);
diff --git a/arch/i386/math-emu/reg_convert.c b/arch/i386/math-emu/reg_convert.c
new file mode 100644 (file)
index 0000000..45a2587
--- /dev/null
@@ -0,0 +1,53 @@
+/*---------------------------------------------------------------------------+
+ |  reg_convert.c                                                            |
+ |                                                                           |
+ |  Convert register representation.                                         |
+ |                                                                           |
+ | Copyright (C) 1992,1993,1994,1996,1997                                    |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
+ |                                                                           |
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_emu.h"
+
+
+int FPU_to_exp16(FPU_REG const *a, FPU_REG *x)
+{
+  int sign = getsign(a);
+
+  *(long long *)&(x->sigl) = *(const long long *)&(a->sigl);
+
+  /* Set up the exponent as a 16 bit quantity. */
+  setexponent16(x, exponent(a));
+
+  if ( exponent16(x) == EXP_UNDER )
+    {
+      /* The number is a de-normal or pseudodenormal. */
+      /* We only deal with the significand and exponent. */
+
+      if (x->sigh & 0x80000000)
+       {
+         /* Is a pseudodenormal. */
+         /* This is non-80486 behaviour because the number
+            loses its 'denormal' identity. */
+         addexponent(x, 1);
+       }
+      else
+       {
+         /* Is a denormal. */
+         addexponent(x, 1);
+         FPU_normalize_nuo(x);
+       }
+    }
+
+  if ( !(x->sigh & 0x80000000) )
+    {
+      EXCEPTION(EX_INTERNAL | 0x180);
+    }
+
+  return sign;
+}
+
diff --git a/arch/i386/math-emu/reg_div.S b/arch/i386/math-emu/reg_div.S
deleted file mode 100644 (file)
index d2fda48..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-       .file   "reg_div.S"
-/*---------------------------------------------------------------------------+
- |  reg_div.S                                                                |
- |                                                                           |
- | Divide one FPU_REG by another and put the result in a destination FPU_REG.|
- |                                                                           |
- | Copyright (C) 1992,1993,1994,1995                                         |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@jacobi.maths.monash.edu.au |
- |                                                                           |
- | Call from C as:                                                           |
- |   void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest,                     |
- |                                    unsigned int control_word)             |
- |                                                                           |
- +---------------------------------------------------------------------------*/
-
-#include "exception.h"
-#include "fpu_emu.h"
-
-
-.text
-ENTRY(reg_div)
-       pushl   %ebp
-       movl    %esp,%ebp
-#ifndef NON_REENTRANT_FPU
-       subl    $28,%esp        /* Needed by divide_kernel */
-#endif /* NON_REENTRANT_FPU */
-
-       pushl   %esi
-       pushl   %edi
-       pushl   %ebx
-
-       movl    PARAM1,%esi
-       movl    PARAM2,%ebx
-       movl    PARAM3,%edi
-
-       movb    TAG(%esi),%al
-       orb     TAG(%ebx),%al
-
-       jne     L_div_special           /* Not (both numbers TW_Valid) */
-
-#ifdef DENORM_OPERAND
-/* Check for denormals */
-       cmpl    EXP_UNDER,EXP(%esi)
-       jg      xL_arg1_not_denormal
-
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
-
-xL_arg1_not_denormal:
-       cmpl    EXP_UNDER,EXP(%ebx)
-       jg      xL_arg2_not_denormal
-
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
-
-xL_arg2_not_denormal:
-#endif /* DENORM_OPERAND */
-
-/* Both arguments are TW_Valid */
-       movb    TW_Valid,TAG(%edi)
-
-       movb    SIGN(%esi),%cl
-       cmpb    %cl,SIGN(%ebx)
-       setne   (%edi)        /* Set the sign, requires SIGN_NEG=1, SIGN_POS=0 */
-
-       movl    EXP(%esi),%edx
-       movl    EXP(%ebx),%eax
-       subl    %eax,%edx
-       addl    EXP_BIAS,%edx
-       movl    %edx,EXP(%edi)
-
-       jmp     SYMBOL_NAME(divide_kernel)
-
-
-/*-----------------------------------------------------------------------*/
-L_div_special:
-       cmpb    TW_NaN,TAG(%esi)        /* A NaN with anything to give NaN */
-       je      L_arg1_NaN
-
-       cmpb    TW_NaN,TAG(%ebx)        /* A NaN with anything to give NaN */
-       jne     L_no_NaN_arg
-
-/* Operations on NaNs */
-L_arg1_NaN:
-L_arg2_NaN:
-       pushl   %edi                    /* Destination */
-       pushl   %esi
-       pushl   %ebx                    /* Ordering is important here */
-       call    SYMBOL_NAME(real_2op_NaN)
-       jmp     LDiv_exit
-
-/* Invalid operations */
-L_zero_zero:
-L_inf_inf:
-       pushl   %edi                    /* Destination */
-       call    SYMBOL_NAME(arith_invalid) /* 0/0 or Infinity/Infinity */
-       jmp     LDiv_exit
-
-L_no_NaN_arg:
-       cmpb    TW_Infinity,TAG(%esi)
-       jne     L_arg1_not_inf
-
-       cmpb    TW_Infinity,TAG(%ebx)
-       je      L_inf_inf               /* invalid operation */
-
-       cmpb    TW_Valid,TAG(%ebx)
-       je      L_inf_valid
-
-#ifdef PARANOID
-       /* arg2 must be zero or valid */
-       cmpb    TW_Zero,TAG(%ebx)
-       ja      L_unknown_tags
-#endif /* PARANOID */
-
-       /* Note that p16-9 says that infinity/0 returns infinity */
-       jmp     L_copy_arg1             /* Answer is Inf */
-
-L_inf_valid:
-#ifdef DENORM_OPERAND
-       cmpl    EXP_UNDER,EXP(%ebx)
-       jg      L_copy_arg1             /* Answer is Inf */
-
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
-#endif /* DENORM_OPERAND */
-
-       jmp     L_copy_arg1             /* Answer is Inf */
-
-L_arg1_not_inf:
-       cmpb    TW_Zero,TAG(%ebx)       /* Priority to div-by-zero error */
-       jne     L_arg2_not_zero
-
-       cmpb    TW_Zero,TAG(%esi)
-       je      L_zero_zero             /* invalid operation */
-
-#ifdef PARANOID
-       /* arg1 must be valid */
-       cmpb    TW_Valid,TAG(%esi)
-       ja      L_unknown_tags
-#endif /* PARANOID */
-
-/* Division by zero error */
-       pushl   %edi                    /* destination */
-       movb    SIGN(%esi),%al
-       xorb    SIGN(%ebx),%al
-       pushl   %eax                    /* lower 8 bits have the sign */
-       call    SYMBOL_NAME(divide_by_zero)
-       jmp     LDiv_exit
-
-L_arg2_not_zero:
-       cmpb    TW_Infinity,TAG(%ebx)
-       jne     L_arg2_not_inf
-
-#ifdef DENORM_OPERAND
-       cmpb    TW_Valid,TAG(%esi)
-       jne     L_return_zero
-
-       cmpl    EXP_UNDER,EXP(%esi)
-       jg      L_return_zero           /* Answer is zero */
-
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
-#endif /* DENORM_OPERAND */
-
-       jmp     L_return_zero           /* Answer is zero */
-
-L_arg2_not_inf:
-
-#ifdef PARANOID
-       cmpb    TW_Zero,TAG(%esi)
-       jne     L_unknown_tags
-#endif /* PARANOID */
-
-       /* arg1 is zero, arg2 is not Infinity or a NaN */
-
-#ifdef DENORM_OPERAND
-       cmpl    EXP_UNDER,EXP(%ebx)
-       jg      L_copy_arg1             /* Answer is zero */
-
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
-#endif /* DENORM_OPERAND */
-
-L_copy_arg1:
-       movb    TAG(%esi),%ax
-       movb    %ax,TAG(%edi)
-       movl    EXP(%esi),%eax
-       movl    %eax,EXP(%edi)
-       movl    SIGL(%esi),%eax
-       movl    %eax,SIGL(%edi)
-       movl    SIGH(%esi),%eax
-       movl    %eax,SIGH(%edi)
-
-LDiv_set_result_sign:
-       movb    SIGN(%esi),%cl
-       cmpb    %cl,SIGN(%ebx)
-       jne     LDiv_negative_result
-
-       movb    SIGN_POS,SIGN(%edi)
-       xorl    %eax,%eax               /* Valid result */
-       jmp     LDiv_exit
-
-LDiv_negative_result:
-       movb    SIGN_NEG,SIGN(%edi)
-       xorl    %eax,%eax               /* Valid result */
-
-LDiv_exit:
-#ifndef NON_REENTRANT_FPU
-       leal    -40(%ebp),%esp
-#else
-       leal    -12(%ebp),%esp
-#endif /* NON_REENTRANT_FPU */
-
-       popl    %ebx
-       popl    %edi
-       popl    %esi
-       leave
-       ret
-
-
-L_return_zero:
-       xorl    %eax,%eax
-       movl    %eax,SIGH(%edi)
-       movl    %eax,SIGL(%edi)
-       movl    EXP_UNDER,EXP(%edi)
-       movb    TW_Zero,TAG(%edi)
-       jmp     LDiv_set_result_sign
-
-#ifdef PARANOID
-L_unknown_tags:
-       pushl   EX_INTERNAL | 0x208
-       call    EXCEPTION
-
-       /* Generate a NaN for unknown tags */
-       movl    SYMBOL_NAME(CONST_QNaN),%eax
-       movl    %eax,(%edi)
-       movl    SYMBOL_NAME(CONST_QNaN)+4,%eax
-       movl    %eax,SIGL(%edi)
-       movl    SYMBOL_NAME(CONST_QNaN)+8,%eax
-       movl    %eax,SIGH(%edi)
-       jmp     LDiv_exit               /* %eax is nz */
-#endif /* PARANOID */
diff --git a/arch/i386/math-emu/reg_divide.c b/arch/i386/math-emu/reg_divide.c
new file mode 100644 (file)
index 0000000..aac408f
--- /dev/null
@@ -0,0 +1,206 @@
+/*---------------------------------------------------------------------------+
+ |  reg_divide.c                                                             |
+ |                                                                           |
+ | Divide one FPU_REG by another and put the result in a destination FPU_REG.|
+ |                                                                           |
+ | Copyright (C) 1996                                                        |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@jacobi.maths.monash.edu.au                |
+ |                                                                           |
+ |    Return value is the tag of the answer, or-ed with FPU_Exception if     |
+ |    one was raised, or -1 on internal error.                               |
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | The destination may be any FPU_REG, including one of the source FPU_REGs. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "fpu_system.h"
+
+/*
+  Divide one register by another and put the result into a third register.
+  */
+int FPU_div(int flags, int rm, int control_w)
+{
+  FPU_REG x, y;
+  FPU_REG const *a, *b, *st0_ptr, *st_ptr;
+  FPU_REG *dest;
+  u_char taga, tagb, signa, signb, sign, saved_sign;
+  int tag, deststnr;
+
+  if ( flags & DEST_RM )
+    deststnr = rm;
+  else
+    deststnr = 0;
+
+  if ( flags & REV )
+    {
+      b = &st(0);
+      st0_ptr = b;
+      tagb = FPU_gettag0();
+      if ( flags & LOADED )
+       {
+         a = (FPU_REG *)rm;
+         taga = flags & 0x0f;
+       }
+      else
+       {
+         a = &st(rm);
+         st_ptr = a;
+         taga = FPU_gettagi(rm);
+       }
+    }
+  else
+    {
+      a = &st(0);
+      st0_ptr = a;
+      taga = FPU_gettag0();
+      if ( flags & LOADED )
+       {
+         b = (FPU_REG *)rm;
+         tagb = flags & 0x0f;
+       }
+      else
+       {
+         b = &st(rm);
+         st_ptr = b;
+         tagb = FPU_gettagi(rm);
+       }
+    }
+
+  signa = getsign(a);
+  signb = getsign(b);
+
+  sign = signa ^ signb;
+
+  dest = &st(deststnr);
+  saved_sign = getsign(dest);
+
+  if ( !(taga | tagb) )
+    {
+      /* Both regs Valid, this should be the most common case. */
+      reg_copy(a, &x);
+      reg_copy(b, &y);
+      setpositive(&x);
+      setpositive(&y);
+      tag = FPU_u_div(&x, &y, dest, control_w, sign);
+
+      if ( tag < 0 )
+       return tag;
+
+      FPU_settagi(deststnr, tag);
+      return tag;
+    }
+
+  if ( taga == TAG_Special )
+    taga = FPU_Special(a);
+  if ( tagb == TAG_Special )
+    tagb = FPU_Special(b);
+
+  if ( ((taga == TAG_Valid) && (tagb == TW_Denormal))
+           || ((taga == TW_Denormal) && (tagb == TAG_Valid))
+           || ((taga == TW_Denormal) && (tagb == TW_Denormal)) )
+    {
+      if ( denormal_operand() < 0 )
+       return FPU_Exception;
+
+      FPU_to_exp16(a, &x);
+      FPU_to_exp16(b, &y);
+      tag = FPU_u_div(&x, &y, dest, control_w, sign);
+      if ( tag < 0 )
+       return tag;
+
+      FPU_settagi(deststnr, tag);
+      return tag;
+    }
+  else if ( (taga <= TW_Denormal) && (tagb <= TW_Denormal) )
+    {
+      if ( tagb != TAG_Zero )
+       {
+         /* Want to find Zero/Valid */
+         if ( tagb == TW_Denormal )
+           {
+             if ( denormal_operand() < 0 )
+               return FPU_Exception;
+           }
+
+         /* The result is zero. */
+         FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
+         setsign(dest, sign);
+         return TAG_Zero;
+       }
+      /* We have an exception condition, either 0/0 or Valid/Zero. */
+      if ( taga == TAG_Zero )
+       {
+         /* 0/0 */
+         return arith_invalid(deststnr);
+       }
+      /* Valid/Zero */
+      return FPU_divide_by_zero(deststnr, sign);
+    }
+  /* Must have infinities, NaNs, etc */
+  else if ( (taga == TW_NaN) || (tagb == TW_NaN) )
+    {
+      if ( flags & LOADED )
+       return real_2op_NaN((FPU_REG *)rm, flags & 0x0f, 0, st0_ptr);
+
+      if ( flags & DEST_RM )
+       {
+         int tag;
+         tag = FPU_gettag0();
+         if ( tag == TAG_Special )
+           tag = FPU_Special(st0_ptr);
+         return real_2op_NaN(st0_ptr, tag, rm, (flags & REV) ? st0_ptr : &st(rm));
+       }
+      else
+       {
+         int tag;
+         tag = FPU_gettagi(rm);
+         if ( tag == TAG_Special )
+           tag = FPU_Special(&st(rm));
+         return real_2op_NaN(&st(rm), tag, 0, (flags & REV) ? st0_ptr : &st(rm));
+       }
+    }
+  else if (taga == TW_Infinity)
+    {
+      if (tagb == TW_Infinity)
+       {
+         /* infinity/infinity */
+         return arith_invalid(deststnr);
+       }
+      else
+       {
+         /* tagb must be Valid or Zero */
+         if ( (tagb == TW_Denormal) && (denormal_operand() < 0) )
+           return FPU_Exception;
+         
+         /* Infinity divided by Zero or Valid does
+            not raise and exception, but returns Infinity */
+         FPU_copy_to_regi(a, TAG_Special, deststnr);
+         setsign(dest, sign);
+         return taga;
+       }
+    }
+  else if (tagb == TW_Infinity)
+    {
+      if ( (taga == TW_Denormal) && (denormal_operand() < 0) )
+       return FPU_Exception;
+
+      /* The result is zero. */
+      FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
+      setsign(dest, sign);
+      return TAG_Zero;
+    }
+#ifdef PARANOID
+  else
+    {
+      EXCEPTION(EX_INTERNAL|0x102);
+      return FPU_Exception;
+    }
+#endif /* PARANOID */ 
+
+}
index bff99aba48893d8aa3d30893fc96671e4fbf45e8..88f86de7c39722cc4368e924ac2da5942afc56c2 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | All of the functions which transfer data between user memory and FPU_REGs.|
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1996                                         |
+ | Copyright (C) 1992,1993,1994,1996,1997,2001                               |
  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
- |                  E-mail   billm@jacobi.maths.monash.edu.au                |
+ |                  E-mail   billm@melbpc.org.au                             |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
  |    other processes using the emulator while swapping is in progress.      |
  +---------------------------------------------------------------------------*/
 
-#include <asm/segment.h>
-
+#include "fpu_emu.h"
 #include "fpu_system.h"
 #include "exception.h"
 #include "reg_constant.h"
-#include "fpu_emu.h"
 #include "control_w.h"
 #include "status_w.h"
 
 
-#define EXTENDED_Ebias 0x3fff
-#define EXTENDED_Emin (-0x3ffe)  /* smallest valid exponent */
-
 #define DOUBLE_Emax 1023         /* largest valid exponent */
 #define DOUBLE_Ebias 1023
 #define DOUBLE_Emin (-1022)      /* smallest valid exponent */
 #define SINGLE_Ebias 127
 #define SINGLE_Emin (-126)       /* smallest valid exponent */
 
-static void write_to_extended(FPU_REG *rp, char *d);
 
-
-/* Get a long double from user memory */
-int reg_load_extended(long double *s, FPU_REG *loaded_data)
+static u_char normalize_no_excep(FPU_REG *r, int exp, int sign)
 {
-  unsigned long sigl, sigh, exp;
+  u_char tag;
 
-  RE_ENTRANT_CHECK_OFF;
-  FPU_verify_area(VERIFY_READ, s, 10);
-  sigl = get_fs_long((unsigned long *) s);
-  sigh = get_fs_long(1 + (unsigned long *) s);
-  exp = get_fs_word(4 + (unsigned short *) s);
-  RE_ENTRANT_CHECK_ON;
+  setexponent16(r, exp);
 
-  loaded_data->tag = TW_Valid;   /* Default */
-  loaded_data->sigl = sigl;
-  loaded_data->sigh = sigh;
-  if (exp & 0x8000)
-    loaded_data->sign = SIGN_NEG;
-  else
-    loaded_data->sign = SIGN_POS;
-  exp &= 0x7fff;
-  loaded_data->exp = exp - EXTENDED_Ebias + EXP_BIAS;
+  tag = FPU_normalize_nuo(r);
+  stdexp(r);
+  if ( sign )
+    setnegative(r);
+
+  return tag;
+}
+
+
+int FPU_tagof(FPU_REG *ptr)
+{
+  int exp;
 
+  exp = exponent16(ptr) & 0x7fff;
   if ( exp == 0 )
     {
-      if ( !(sigh | sigl) )
+      if ( !(ptr->sigh | ptr->sigl) )
        {
-         loaded_data->tag = TW_Zero;
-         return 0;
+         return TAG_Zero;
        }
       /* The number is a de-normal or pseudodenormal. */
-      if (sigh & 0x80000000)
-       {
-         /* Is a pseudodenormal. */
-         /* Convert it for internal use. */
-         /* This is non-80486 behaviour because the number
-            loses its 'denormal' identity. */
-         loaded_data->exp++;
-         return 1;
-       }
-      else
-       {
-         /* Is a denormal. */
-         /* Convert it for internal use. */
-         loaded_data->exp++;
-         normalize_nuo(loaded_data);
-         return 0;
-       }
+      return TAG_Special;
     }
-  else if ( exp == 0x7fff )
-    {
-      if ( !((sigh ^ 0x80000000) | sigl) )
-       {
-         /* Matches the bit pattern for Infinity. */
-         loaded_data->exp = EXP_Infinity;
-         loaded_data->tag = TW_Infinity;
-         return 0;
-       }
 
-      loaded_data->exp = EXP_NaN;
-      loaded_data->tag = TW_NaN;
-      if ( !(sigh & 0x80000000) )
-       {
-         /* NaNs have the ms bit set to 1. */
-         /* This is therefore an Unsupported NaN data type. */
-         /* This is non 80486 behaviour */
-         /* This should generate an Invalid Operand exception
-            later, so we convert it to a SNaN */
-         loaded_data->sigh = 0x80000000;
-         loaded_data->sigl = 0x00000001;
-         loaded_data->sign = SIGN_NEG;
-         return 1;
-       }
-      return 0;
+  if ( exp == 0x7fff )
+    {
+      /* Is an Infinity, a NaN, or an unsupported data type. */
+      return TAG_Special;
     }
 
-  if ( !(sigh & 0x80000000) )
+  if ( !(ptr->sigh & 0x80000000) )
     {
       /* Unsupported data type. */
       /* Valid numbers have the ms bit set to 1. */
       /* Unnormal. */
-      /* Convert it for internal use. */
-      /* This is non-80486 behaviour */
-      /* This should generate an Invalid Operand exception
-        later, so we convert it to a SNaN */
-      loaded_data->sigh = 0x80000000;
-      loaded_data->sigl = 0x00000001;
-      loaded_data->sign = SIGN_NEG;
-      loaded_data->exp = EXP_NaN;
-      loaded_data->tag = TW_NaN;
-      return 1;
+      return TAG_Special;
     }
-  return 0;
+
+  return TAG_Valid;
+}
+
+
+/* Get a long double from user memory */
+int FPU_load_extended(long double *s, int stnr)
+{
+  FPU_REG *sti_ptr = &st(stnr);
+
+  RE_ENTRANT_CHECK_OFF;
+  FPU_verify_area(VERIFY_READ, s, 10);
+  FPU_copy_from_user(sti_ptr, s, 10);
+  RE_ENTRANT_CHECK_ON;
+
+  return FPU_tagof(sti_ptr);
 }
 
 
 /* Get a double from user memory */
-int reg_load_double(double *dfloat, FPU_REG *loaded_data)
+int FPU_load_double(double *dfloat, FPU_REG *loaded_data)
 {
-  int exp;
+  int exp, tag, negative;
   unsigned m64, l64;
 
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_READ, dfloat, 8);
-  m64 = get_fs_long(1 + (unsigned long *) dfloat);
-  l64 = get_fs_long((unsigned long *) dfloat);
+  FPU_get_user(m64, 1 + (unsigned long *) dfloat);
+  FPU_get_user(l64, (unsigned long *) dfloat);
   RE_ENTRANT_CHECK_ON;
 
-  if (m64 & 0x80000000)
-    loaded_data->sign = SIGN_NEG;
-  else
-    loaded_data->sign = SIGN_POS;
-  exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias;
+  negative = (m64 & 0x80000000) ? SIGN_Negative : SIGN_Positive;
+  exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias + EXTENDED_Ebias;
   m64 &= 0xfffff;
-  if (exp > DOUBLE_Emax)
+  if ( exp > DOUBLE_Emax + EXTENDED_Ebias )
     {
       /* Infinity or NaN */
       if ((m64 == 0) && (l64 == 0))
@@ -162,93 +119,87 @@ int reg_load_double(double *dfloat, FPU_REG *loaded_data)
          /* +- infinity */
          loaded_data->sigh = 0x80000000;
          loaded_data->sigl = 0x00000000;
-         loaded_data->exp = EXP_Infinity;
-         loaded_data->tag = TW_Infinity;
-         return 0;
+         exp = EXP_Infinity + EXTENDED_Ebias;
+         tag = TAG_Special;
        }
       else
        {
          /* Must be a signaling or quiet NaN */
-         loaded_data->exp = EXP_NaN;
-         loaded_data->tag = TW_NaN;
+         exp = EXP_NaN + EXTENDED_Ebias;
          loaded_data->sigh = (m64 << 11) | 0x80000000;
          loaded_data->sigh |= l64 >> 21;
          loaded_data->sigl = l64 << 11;
-         return 0; /* The calling function must look for NaNs */
+         tag = TAG_Special;    /* The calling function must look for NaNs */
        }
     }
-  else if ( exp < DOUBLE_Emin )
+  else if ( exp < DOUBLE_Emin + EXTENDED_Ebias )
     {
       /* Zero or de-normal */
       if ((m64 == 0) && (l64 == 0))
        {
          /* Zero */
-         int c = loaded_data->sign;
-         reg_move(&CONST_Z, loaded_data);
-         loaded_data->sign = c;
-         return 0;
+         reg_copy(&CONST_Z, loaded_data);
+         exp = 0;
+         tag = TAG_Zero;
        }
       else
        {
          /* De-normal */
-         loaded_data->exp = DOUBLE_Emin + EXP_BIAS;
-         loaded_data->tag = TW_Valid;
          loaded_data->sigh = m64 << 11;
          loaded_data->sigh |= l64 >> 21;
          loaded_data->sigl = l64 << 11;
-         normalize_nuo(loaded_data);
-         return denormal_operand();
+
+         return normalize_no_excep(loaded_data, DOUBLE_Emin, negative)
+           | (denormal_operand() < 0 ? FPU_Exception : 0);
        }
     }
   else
     {
-      loaded_data->exp = exp + EXP_BIAS;
-      loaded_data->tag = TW_Valid;
       loaded_data->sigh = (m64 << 11) | 0x80000000;
       loaded_data->sigh |= l64 >> 21;
       loaded_data->sigl = l64 << 11;
 
-      return 0;
+      tag = TAG_Valid;
     }
+
+  setexponent16(loaded_data, exp | negative);
+
+  return tag;
 }
 
 
 /* Get a float from user memory */
-int reg_load_single(float *single, FPU_REG *loaded_data)
+int FPU_load_single(float *single, FPU_REG *loaded_data)
 {
   unsigned m32;
-  int exp;
+  int exp, tag, negative;
 
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_READ, single, 4);
-  m32 = get_fs_long((unsigned long *) single);
+  FPU_get_user(m32, (unsigned long *) single);
   RE_ENTRANT_CHECK_ON;
 
-  if (m32 & 0x80000000)
-    loaded_data->sign = SIGN_NEG;
-  else
-    loaded_data->sign = SIGN_POS;
+  negative = (m32 & 0x80000000) ? SIGN_Negative : SIGN_Positive;
+
   if (!(m32 & 0x7fffffff))
     {
       /* Zero */
-      int c = loaded_data->sign;
-      reg_move(&CONST_Z, loaded_data);
-      loaded_data->sign = c;
-      return 0;
+      reg_copy(&CONST_Z, loaded_data);
+      addexponent(loaded_data, negative);
+      return TAG_Zero;
     }
-  exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
+  exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias + EXTENDED_Ebias;
   m32 = (m32 & 0x7fffff) << 8;
-  if ( exp < SINGLE_Emin )
+  if ( exp < SINGLE_Emin + EXTENDED_Ebias )
     {
       /* De-normals */
-      loaded_data->exp = SINGLE_Emin + EXP_BIAS;
-      loaded_data->tag = TW_Valid;
       loaded_data->sigh = m32;
       loaded_data->sigl = 0;
-      normalize_nuo(loaded_data);
-      return denormal_operand();
+
+      return normalize_no_excep(loaded_data, SINGLE_Emin, negative)
+       | (denormal_operand() < 0 ? FPU_Exception : 0);
     }
-  else if ( exp > SINGLE_Emax )
+  else if ( exp > SINGLE_Emax + EXTENDED_Ebias )
     {
     /* Infinity or NaN */
       if ( m32 == 0 )
@@ -256,131 +207,129 @@ int reg_load_single(float *single, FPU_REG *loaded_data)
          /* +- infinity */
          loaded_data->sigh = 0x80000000;
          loaded_data->sigl = 0x00000000;
-         loaded_data->exp = EXP_Infinity;
-         loaded_data->tag = TW_Infinity;
-         return 0;
+         exp = EXP_Infinity + EXTENDED_Ebias;
+         tag = TAG_Special;
        }
       else
        {
          /* Must be a signaling or quiet NaN */
-         loaded_data->exp = EXP_NaN;
-         loaded_data->tag = TW_NaN;
+         exp = EXP_NaN + EXTENDED_Ebias;
          loaded_data->sigh = m32 | 0x80000000;
          loaded_data->sigl = 0;
-         return 0; /* The calling function must look for NaNs */
+         tag = TAG_Special;  /* The calling function must look for NaNs */
        }
     }
   else
     {
-      loaded_data->exp = exp + EXP_BIAS;
       loaded_data->sigh = m32 | 0x80000000;
       loaded_data->sigl = 0;
-      loaded_data->tag = TW_Valid;
-      return 0;
+      tag = TAG_Valid;
     }
+
+  setexponent16(loaded_data, exp | negative);  /* Set the sign. */
+
+  return tag;
 }
 
 
 /* Get a long long from user memory */
-void reg_load_int64(long long *_s, FPU_REG *loaded_data)
+int FPU_load_int64(long long *_s)
 {
-  int e;
   long long s;
+  int sign;
+  FPU_REG *st0_ptr = &st(0);
 
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_READ, _s, 8);
-  ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s);
-  ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s);
+  copy_from_user(&s,_s,8);
   RE_ENTRANT_CHECK_ON;
 
   if (s == 0)
-    { reg_move(&CONST_Z, loaded_data); return; }
+    {
+      reg_copy(&CONST_Z, st0_ptr);
+      return TAG_Zero;
+    }
 
   if (s > 0)
-    loaded_data->sign = SIGN_POS;
+    sign = SIGN_Positive;
   else
   {
     s = -s;
-    loaded_data->sign = SIGN_NEG;
+    sign = SIGN_Negative;
   }
 
-  e = EXP_BIAS + 63;
-  significand(loaded_data) = s;
-  loaded_data->exp = e;
-  loaded_data->tag = TW_Valid;
-  normalize_nuo(loaded_data);
+  significand(st0_ptr) = s;
+
+  return normalize_no_excep(st0_ptr, 63, sign);
 }
 
 
 /* Get a long from user memory */
-void reg_load_int32(long *_s, FPU_REG *loaded_data)
+int FPU_load_int32(long *_s, FPU_REG *loaded_data)
 {
   long s;
-  int e;
+  int negative;
 
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_READ, _s, 4);
-  s = (long)get_fs_long((unsigned long *) _s);
+  FPU_get_user(s, _s);
   RE_ENTRANT_CHECK_ON;
 
   if (s == 0)
-    { reg_move(&CONST_Z, loaded_data); return; }
+    { reg_copy(&CONST_Z, loaded_data); return TAG_Zero; }
 
   if (s > 0)
-    loaded_data->sign = SIGN_POS;
+    negative = SIGN_Positive;
   else
-  {
-    s = -s;
-    loaded_data->sign = SIGN_NEG;
-  }
+    {
+      s = -s;
+      negative = SIGN_Negative;
+    }
 
-  e = EXP_BIAS + 31;
   loaded_data->sigh = s;
   loaded_data->sigl = 0;
-  loaded_data->exp = e;
-  loaded_data->tag = TW_Valid;
-  normalize_nuo(loaded_data);
+
+  return normalize_no_excep(loaded_data, 31, negative);
 }
 
 
 /* Get a short from user memory */
-void reg_load_int16(short *_s, FPU_REG *loaded_data)
+int FPU_load_int16(short *_s, FPU_REG *loaded_data)
 {
-  int s, e;
+  int s, negative;
 
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_READ, _s, 2);
   /* Cast as short to get the sign extended. */
-  s = (short)get_fs_word((unsigned short *) _s);
+  FPU_get_user(s, _s);
   RE_ENTRANT_CHECK_ON;
 
   if (s == 0)
-    { reg_move(&CONST_Z, loaded_data); return; }
+    { reg_copy(&CONST_Z, loaded_data); return TAG_Zero; }
 
   if (s > 0)
-    loaded_data->sign = SIGN_POS;
+    negative = SIGN_Positive;
   else
-  {
-    s = -s;
-    loaded_data->sign = SIGN_NEG;
-  }
+    {
+      s = -s;
+      negative = SIGN_Negative;
+    }
 
-  e = EXP_BIAS + 15;
   loaded_data->sigh = s << 16;
-
   loaded_data->sigl = 0;
-  loaded_data->exp = e;
-  loaded_data->tag = TW_Valid;
-  normalize_nuo(loaded_data);
+
+  return normalize_no_excep(loaded_data, 15, negative);
 }
 
 
 /* Get a packed bcd array from user memory */
-void reg_load_bcd(char *s, FPU_REG *loaded_data)
+int FPU_load_bcd(u_char *s)
 {
+  FPU_REG *st0_ptr = &st(0);
   int pos;
-  unsigned char bcd;
+  u_char bcd;
   long long l=0;
+  int sign;
 
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_READ, s, 10);
@@ -389,7 +338,7 @@ void reg_load_bcd(char *s, FPU_REG *loaded_data)
     {
       l *= 10;
       RE_ENTRANT_CHECK_OFF;
-      bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos);
+      FPU_get_user(bcd, (u_char *) s+pos);
       RE_ENTRANT_CHECK_ON;
       l += bcd >> 4;
       l *= 10;
@@ -397,30 +346,27 @@ void reg_load_bcd(char *s, FPU_REG *loaded_data)
     }
  
   RE_ENTRANT_CHECK_OFF;
-  loaded_data->sign =
-    ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ?
-      SIGN_NEG : SIGN_POS;
+  FPU_get_user(sign, (u_char *) s+9);
+  sign = sign & 0x80 ? SIGN_Negative : SIGN_Positive;
   RE_ENTRANT_CHECK_ON;
 
-  if (l == 0)
+  if ( l == 0 )
     {
-      char sign = loaded_data->sign;
-      reg_move(&CONST_Z, loaded_data);
-      loaded_data->sign = sign;
+      reg_copy(&CONST_Z, st0_ptr);
+      addexponent(st0_ptr, sign);   /* Set the sign. */
+      return TAG_Zero;
     }
   else
     {
-      significand(loaded_data) = l;
-      loaded_data->exp = EXP_BIAS + 63;
-      loaded_data->tag = TW_Valid;
-      normalize_nuo(loaded_data);
+      significand(st0_ptr) = l;
+      return normalize_no_excep(st0_ptr, 63, sign);
     }
 }
 
 /*===========================================================================*/
 
 /* Put a long double into user memory */
-int reg_store_extended(long double *d, FPU_REG *st0_ptr)
+int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag, long double *d)
 {
   /*
     The only exception raised by an attempt to store to an
@@ -428,12 +374,16 @@ int reg_store_extended(long double *d, FPU_REG *st0_ptr)
     attempting to store from an empty register.
    */
 
-  if ( st0_ptr->tag != TW_Empty )
+  if ( st0_tag != TAG_Empty )
     {
       RE_ENTRANT_CHECK_OFF;
       FPU_verify_area(VERIFY_WRITE, d, 10);
+
+      FPU_put_user(st0_ptr->sigl, (unsigned long *) d);
+      FPU_put_user(st0_ptr->sigh, (unsigned long *) ((u_char *)d + 4));
+      FPU_put_user(exponent16(st0_ptr), (unsigned short *) ((u_char *)d + 8));
       RE_ENTRANT_CHECK_ON;
-      write_to_extended(st0_ptr, (char *) d);
+
       return 1;
     }
 
@@ -445,9 +395,9 @@ int reg_store_extended(long double *d, FPU_REG *st0_ptr)
       /* Put out the QNaN indefinite */
       RE_ENTRANT_CHECK_OFF;
       FPU_verify_area(VERIFY_WRITE,d,10);
-      put_fs_long(0, (unsigned long *) d);
-      put_fs_long(0xc0000000, 1 + (unsigned long *) d);
-      put_fs_word(0xffff, 4 + (short *) d);
+      FPU_put_user(0, (unsigned long *) d);
+      FPU_put_user(0xc0000000, 1 + (unsigned long *) d);
+      FPU_put_user(0xffff, 4 + (short *) d);
       RE_ENTRANT_CHECK_ON;
       return 1;
     }
@@ -458,38 +408,26 @@ int reg_store_extended(long double *d, FPU_REG *st0_ptr)
 
 
 /* Put a double into user memory */
-int reg_store_double(double *dfloat, FPU_REG *st0_ptr)
+int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double *dfloat)
 {
   unsigned long l[2];
   unsigned long increment = 0; /* avoid gcc warnings */
-  char st0_tag = st0_ptr->tag;
+  int precision_loss;
+  int exp;
+  FPU_REG tmp;
 
-  if (st0_tag == TW_Valid)
+  if ( st0_tag == TAG_Valid )
     {
-      int precision_loss;
-      int exp;
-      FPU_REG tmp;
-
-      reg_move(st0_ptr, &tmp);
-      exp = tmp.exp - EXP_BIAS;
+      reg_copy(st0_ptr, &tmp);
+      exp = exponent(&tmp);
 
       if ( exp < DOUBLE_Emin )     /* It may be a denormal */
        {
-         /* A denormal will always underflow. */
-#ifndef PECULIAR_486
-         /* An 80486 is supposed to be able to generate
-            a denormal exception here, but... */
-         if ( st0_ptr->exp <= EXP_UNDER )
-           {
-             /* Underflow has priority. */
-             if ( control_word & CW_Underflow )
-               denormal_operand();
-           }
-#endif /* PECULIAR_486 */
+         addexponent(&tmp, -DOUBLE_Emin + 52);  /* largest exp to be 51 */
 
-         tmp.exp += -DOUBLE_Emin + 52;  /* largest exp to be 51 */
+       denormal_arg:
 
-         if ( (precision_loss = round_to_int(&tmp)) )
+         if ( (precision_loss = FPU_round_to_int(&tmp, st0_tag)) )
            {
 #ifdef PECULIAR_486
              /* Did it round to a non-denormal ? */
@@ -526,10 +464,10 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr)
                    ((tmp.sigl & 0xc00) == 0xc00);            /* odd -> even */
                  break;
                case RC_DOWN:   /* towards -infinity */
-                 increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff;
+                 increment = signpositive(&tmp) ? 0 : tmp.sigl & 0x7ff;
                  break;
                case RC_UP:     /* towards +infinity */
-                 increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0;
+                 increment = signpositive(&tmp) ? tmp.sigl & 0x7ff : 0;
                  break;
                case RC_CHOP:
                  increment = 0;
@@ -600,33 +538,64 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr)
            }
        }
     }
-  else if (st0_tag == TW_Zero)
+  else if (st0_tag == TAG_Zero)
     {
       /* Number is zero */
       l[0] = 0;
       l[1] = 0;
     }
-  else if (st0_tag == TW_Infinity)
+  else if ( st0_tag == TAG_Special )
     {
-      l[0] = 0;
-      l[1] = 0x7ff00000;
-    }
-  else if (st0_tag == TW_NaN)
-    {
-      /* See if we can get a valid NaN from the FPU_REG */
-      l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21);
-      l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
-      if ( !(st0_ptr->sigh & 0x40000000) )
+      st0_tag = FPU_Special(st0_ptr);
+      if ( st0_tag == TW_Denormal )
        {
-         /* It is a signalling NaN */
-         EXCEPTION(EX_Invalid);
-         if ( !(control_word & CW_Invalid) )
-           return 0;
-         l[1] |= (0x40000000 >> 11);
+         /* A denormal will always underflow. */
+#ifndef PECULIAR_486
+         /* An 80486 is supposed to be able to generate
+            a denormal exception here, but... */
+         /* Underflow has priority. */
+         if ( control_word & CW_Underflow )
+           denormal_operand();
+#endif /* PECULIAR_486 */
+         reg_copy(st0_ptr, &tmp);
+         goto denormal_arg;
+       }
+      else if (st0_tag == TW_Infinity)
+       {
+         l[0] = 0;
+         l[1] = 0x7ff00000;
+       }
+      else if (st0_tag == TW_NaN)
+       {
+         /* Is it really a NaN ? */
+         if ( (exponent(st0_ptr) == EXP_OVER)
+              && (st0_ptr->sigh & 0x80000000) )
+           {
+             /* See if we can get a valid NaN from the FPU_REG */
+             l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21);
+             l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
+             if ( !(st0_ptr->sigh & 0x40000000) )
+               {
+                 /* It is a signalling NaN */
+                 EXCEPTION(EX_Invalid);
+                 if ( !(control_word & CW_Invalid) )
+                   return 0;
+                 l[1] |= (0x40000000 >> 11);
+               }
+             l[1] |= 0x7ff00000;
+           }
+         else
+           {
+             /* It is an unsupported data type */
+             EXCEPTION(EX_Invalid);
+             if ( !(control_word & CW_Invalid) )
+               return 0;
+             l[0] = 0;
+             l[1] = 0xfff80000;
+           }
        }
-      l[1] |= 0x7ff00000;
     }
-  else if ( st0_tag == TW_Empty )
+  else if ( st0_tag == TAG_Empty )
     {
       /* Empty register (stack underflow) */
       EXCEPTION(EX_StackUnder);
@@ -636,21 +605,21 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr)
          /* Put out the QNaN indefinite */
          RE_ENTRANT_CHECK_OFF;
          FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
-         put_fs_long(0, (unsigned long *) dfloat);
-         put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat);
+         FPU_put_user(0, (unsigned long *) dfloat);
+         FPU_put_user(0xfff80000, 1 + (unsigned long *) dfloat);
          RE_ENTRANT_CHECK_ON;
          return 1;
        }
       else
        return 0;
     }
-  if ( st0_ptr->sign )
+  if ( getsign(st0_ptr) )
     l[1] |= 0x80000000;
 
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
-  put_fs_long(l[0], (unsigned long *)dfloat);
-  put_fs_long(l[1], 1 + (unsigned long *)dfloat);
+  FPU_put_user(l[0], (unsigned long *)dfloat);
+  FPU_put_user(l[1], 1 + (unsigned long *)dfloat);
   RE_ENTRANT_CHECK_ON;
 
   return 1;
@@ -658,38 +627,27 @@ int reg_store_double(double *dfloat, FPU_REG *st0_ptr)
 
 
 /* Put a float into user memory */
-int reg_store_single(float *single, FPU_REG *st0_ptr)
+int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float *single)
 {
   long templ;
   unsigned long increment = 0;         /* avoid gcc warnings */
-  char st0_tag = st0_ptr->tag;
+  int precision_loss;
+  int exp;
+  FPU_REG tmp;
 
-  if (st0_tag == TW_Valid)
+  if ( st0_tag == TAG_Valid )
     {
-      int precision_loss;
-      int exp;
-      FPU_REG tmp;
 
-      reg_move(st0_ptr, &tmp);
-      exp = tmp.exp - EXP_BIAS;
+      reg_copy(st0_ptr, &tmp);
+      exp = exponent(&tmp);
 
       if ( exp < SINGLE_Emin )
        {
-         /* A denormal will always underflow. */
-#ifndef PECULIAR_486
-         /* An 80486 is supposed to be able to generate
-            a denormal exception here, but... */
-         if ( st0_ptr->exp <= EXP_UNDER )
-           {
-             /* Underflow has priority. */
-             if ( control_word & CW_Underflow )
-               denormal_operand();
-           }
-#endif /* PECULIAR_486 */
+         addexponent(&tmp, -SINGLE_Emin + 23);  /* largest exp to be 22 */
 
-         tmp.exp += -SINGLE_Emin + 23;  /* largest exp to be 22 */
+       denormal_arg:
 
-         if ( (precision_loss = round_to_int(&tmp)) )
+         if ( (precision_loss = FPU_round_to_int(&tmp, st0_tag)) )
            {
 #ifdef PECULIAR_486
              /* Did it round to a non-denormal ? */
@@ -703,15 +661,15 @@ int reg_store_single(float *single, FPU_REG *st0_ptr)
                  EXCEPTION(EX_Underflow);
                  /* This is a special case: see sec 16.2.5.1 of
                     the 80486 book */
-                 if ( !(control_word & EX_Underflow) )
+                 if ( !(control_word & CW_Underflow) )
                    return 0;
                }
              EXCEPTION(precision_loss);
-             if ( !(control_word & EX_Precision) )
+             if ( !(control_word & CW_Precision) )
                return 0;
            }
          templ = tmp.sigl;
-       }
+      }
       else
        {
          if ( tmp.sigl | (tmp.sigh & 0x000000ff) )
@@ -725,15 +683,15 @@ int reg_store_single(float *single, FPU_REG *st0_ptr)
                case RC_RND:
                  increment = ((sigh & 0xff) > 0x80)       /* more than half */
                    || (((sigh & 0xff) == 0x80) && sigl)   /* more than half */
-                     || ((sigh & 0x180) == 0x180);        /* round to even */
+                   || ((sigh & 0x180) == 0x180);        /* round to even */
                  break;
                case RC_DOWN:   /* towards -infinity */
-                 increment = (tmp.sign == SIGN_POS)
-                             ? 0 : (sigl | (sigh & 0xff));
+                 increment = signpositive(&tmp)
+                   ? 0 : (sigl | (sigh & 0xff));
                  break;
                case RC_UP:     /* towards +infinity */
-                 increment = (tmp.sign == SIGN_POS)
-                             ? (sigl | (sigh & 0xff)) : 0;
+                 increment = signpositive(&tmp)
+                   ? (sigl | (sigh & 0xff)) : 0;
                  break;
                case RC_CHOP:
                  increment = 0;
@@ -766,7 +724,7 @@ int reg_store_single(float *single, FPU_REG *st0_ptr)
            }
          else
            precision_loss = 0;
-
+      
          templ = (tmp.sigh >> 8) & 0x007fffff;
 
          if ( exp > SINGLE_Emax )
@@ -797,29 +755,66 @@ int reg_store_single(float *single, FPU_REG *st0_ptr)
            }
        }
     }
-  else if (st0_tag == TW_Zero)
+  else if (st0_tag == TAG_Zero)
     {
       templ = 0;
     }
-  else if (st0_tag == TW_Infinity)
+  else if ( st0_tag == TAG_Special )
     {
-      templ = 0x7f800000;
-    }
-  else if (st0_tag == TW_NaN)
-    {
-      /* See if we can get a valid NaN from the FPU_REG */
-      templ = st0_ptr->sigh >> 8;
-      if ( !(st0_ptr->sigh & 0x40000000) )
+      st0_tag = FPU_Special(st0_ptr);
+      if (st0_tag == TW_Denormal)
        {
-         /* It is a signalling NaN */
-         EXCEPTION(EX_Invalid);
-         if ( !(control_word & CW_Invalid) )
-           return 0;
-         templ |= (0x40000000 >> 8);
+         reg_copy(st0_ptr, &tmp);
+
+         /* A denormal will always underflow. */
+#ifndef PECULIAR_486
+         /* An 80486 is supposed to be able to generate
+            a denormal exception here, but... */
+         /* Underflow has priority. */
+         if ( control_word & CW_Underflow )
+           denormal_operand();
+#endif /* PECULIAR_486 */ 
+         goto denormal_arg;
+       }
+      else if (st0_tag == TW_Infinity)
+       {
+         templ = 0x7f800000;
+       }
+      else if (st0_tag == TW_NaN)
+       {
+         /* Is it really a NaN ? */
+         if ( (exponent(st0_ptr) == EXP_OVER) && (st0_ptr->sigh & 0x80000000) )
+           {
+             /* See if we can get a valid NaN from the FPU_REG */
+             templ = st0_ptr->sigh >> 8;
+             if ( !(st0_ptr->sigh & 0x40000000) )
+               {
+                 /* It is a signalling NaN */
+                 EXCEPTION(EX_Invalid);
+                 if ( !(control_word & CW_Invalid) )
+                   return 0;
+                 templ |= (0x40000000 >> 8);
+               }
+             templ |= 0x7f800000;
+           }
+         else
+           {
+             /* It is an unsupported data type */
+             EXCEPTION(EX_Invalid);
+             if ( !(control_word & CW_Invalid) )
+               return 0;
+             templ = 0xffc00000;
+           }
+       }
+#ifdef PARANOID
+      else
+       {
+         EXCEPTION(EX_INTERNAL|0x164);
+         return 0;
        }
-      templ |= 0x7f800000;
+#endif
     }
-  else if ( st0_tag == TW_Empty )
+  else if ( st0_tag == TAG_Empty )
     {
       /* Empty register (stack underflow) */
       EXCEPTION(EX_StackUnder);
@@ -829,7 +824,7 @@ int reg_store_single(float *single, FPU_REG *st0_ptr)
          /* Put out the QNaN indefinite */
          RE_ENTRANT_CHECK_OFF;
          FPU_verify_area(VERIFY_WRITE,(void *)single,4);
-         put_fs_long(0xffc00000, (unsigned long *) single);
+         FPU_put_user(0xffc00000, (unsigned long *) single);
          RE_ENTRANT_CHECK_ON;
          return 1;
        }
@@ -843,12 +838,12 @@ int reg_store_single(float *single, FPU_REG *st0_ptr)
       return 0;
     }
 #endif
-  if (st0_ptr->sign)
+  if ( getsign(st0_ptr) )
     templ |= 0x80000000;
 
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_WRITE,(void *)single,4);
-  put_fs_long(templ,(unsigned long *) single);
+  FPU_put_user(templ,(unsigned long *) single);
   RE_ENTRANT_CHECK_ON;
 
   return 1;
@@ -856,34 +851,37 @@ int reg_store_single(float *single, FPU_REG *st0_ptr)
 
 
 /* Put a long long into user memory */
-int reg_store_int64(long long *d, FPU_REG *st0_ptr)
+int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag, long long *d)
 {
   FPU_REG t;
   long long tll;
   int precision_loss;
-  char st0_tag = st0_ptr->tag;
 
-  if ( st0_tag == TW_Empty )
+  if ( st0_tag == TAG_Empty )
     {
       /* Empty register (stack underflow) */
       EXCEPTION(EX_StackUnder);
       goto invalid_operand;
     }
-  else if ( (st0_tag == TW_Infinity) ||
-          (st0_tag == TW_NaN) )
+  else if ( st0_tag == TAG_Special )
     {
-      EXCEPTION(EX_Invalid);
-      goto invalid_operand;
+      st0_tag = FPU_Special(st0_ptr);
+      if ( (st0_tag == TW_Infinity) ||
+          (st0_tag == TW_NaN) )
+       {
+         EXCEPTION(EX_Invalid);
+         goto invalid_operand;
+       }
     }
 
-  reg_move(st0_ptr, &t);
-  precision_loss = round_to_int(&t);
+  reg_copy(st0_ptr, &t);
+  precision_loss = FPU_round_to_int(&t, st0_tag);
   ((long *)&tll)[0] = t.sigl;
   ((long *)&tll)[1] = t.sigh;
   if ( (precision_loss == 1) ||
       ((t.sigh & 0x80000000) &&
        !((t.sigh == 0x80000000) && (t.sigl == 0) &&
-        (t.sign == SIGN_NEG))) )
+        signnegative(&t))) )
     {
       EXCEPTION(EX_Invalid);
       /* This is a special case: see sec 16.2.5.1 of the 80486 book */
@@ -900,14 +898,13 @@ int reg_store_int64(long long *d, FPU_REG *st0_ptr)
     {
       if ( precision_loss )
        set_precision_flag(precision_loss);
-      if ( t.sign )
+      if ( signnegative(&t) )
        tll = - tll;
     }
 
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_WRITE,(void *)d,8);
-  put_fs_long(((long *)&tll)[0],(unsigned long *) d);
-  put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d);
+  copy_to_user(d, &tll, 8);
   RE_ENTRANT_CHECK_ON;
 
   return 1;
@@ -915,30 +912,33 @@ int reg_store_int64(long long *d, FPU_REG *st0_ptr)
 
 
 /* Put a long into user memory */
-int reg_store_int32(long *d, FPU_REG *st0_ptr)
+int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long *d)
 {
   FPU_REG t;
   int precision_loss;
-  char st0_tag = st0_ptr->tag;
 
-  if ( st0_tag == TW_Empty )
+  if ( st0_tag == TAG_Empty )
     {
       /* Empty register (stack underflow) */
       EXCEPTION(EX_StackUnder);
       goto invalid_operand;
     }
-  else if ( (st0_tag == TW_Infinity) ||
-          (st0_tag == TW_NaN) )
+  else if ( st0_tag == TAG_Special )
     {
-      EXCEPTION(EX_Invalid);
-      goto invalid_operand;
+      st0_tag = FPU_Special(st0_ptr);
+      if ( (st0_tag == TW_Infinity) ||
+          (st0_tag == TW_NaN) )
+       {
+         EXCEPTION(EX_Invalid);
+         goto invalid_operand;
+       }
     }
 
-  reg_move(st0_ptr, &t);
-  precision_loss = round_to_int(&t);
+  reg_copy(st0_ptr, &t);
+  precision_loss = FPU_round_to_int(&t, st0_tag);
   if (t.sigh ||
       ((t.sigl & 0x80000000) &&
-       !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) )
+       !((t.sigl == 0x80000000) && signnegative(&t))) )
     {
       EXCEPTION(EX_Invalid);
       /* This is a special case: see sec 16.2.5.1 of the 80486 book */
@@ -955,13 +955,13 @@ int reg_store_int32(long *d, FPU_REG *st0_ptr)
     {
       if ( precision_loss )
        set_precision_flag(precision_loss);
-      if ( t.sign )
+      if ( signnegative(&t) )
        t.sigl = -(long)t.sigl;
     }
 
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_WRITE,d,4);
-  put_fs_long(t.sigl, (unsigned long *) d);
+  FPU_put_user(t.sigl, (unsigned long *) d);
   RE_ENTRANT_CHECK_ON;
 
   return 1;
@@ -969,30 +969,33 @@ int reg_store_int32(long *d, FPU_REG *st0_ptr)
 
 
 /* Put a short into user memory */
-int reg_store_int16(short *d, FPU_REG *st0_ptr)
+int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short *d)
 {
   FPU_REG t;
   int precision_loss;
-  char st0_tag = st0_ptr->tag;
 
-  if ( st0_tag == TW_Empty )
+  if ( st0_tag == TAG_Empty )
     {
       /* Empty register (stack underflow) */
       EXCEPTION(EX_StackUnder);
       goto invalid_operand;
     }
-  else if ( (st0_tag == TW_Infinity) ||
-          (st0_tag == TW_NaN) )
+  else if ( st0_tag == TAG_Special )
     {
-      EXCEPTION(EX_Invalid);
-      goto invalid_operand;
+      st0_tag = FPU_Special(st0_ptr);
+      if ( (st0_tag == TW_Infinity) ||
+          (st0_tag == TW_NaN) )
+       {
+         EXCEPTION(EX_Invalid);
+         goto invalid_operand;
+       }
     }
 
-  reg_move(st0_ptr, &t);
-  precision_loss = round_to_int(&t);
+  reg_copy(st0_ptr, &t);
+  precision_loss = FPU_round_to_int(&t, st0_tag);
   if (t.sigh ||
       ((t.sigl & 0xffff8000) &&
-       !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) )
+       !((t.sigl == 0x8000) && signnegative(&t))) )
     {
       EXCEPTION(EX_Invalid);
       /* This is a special case: see sec 16.2.5.1 of the 80486 book */
@@ -1009,13 +1012,13 @@ int reg_store_int16(short *d, FPU_REG *st0_ptr)
     {
       if ( precision_loss )
        set_precision_flag(precision_loss);
-      if ( t.sign )
+      if ( signnegative(&t) )
        t.sigl = -t.sigl;
     }
 
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_WRITE,d,2);
-  put_fs_word((short)t.sigl,(short *) d);
+  FPU_put_user((short)t.sigl,(short *) d);
   RE_ENTRANT_CHECK_ON;
 
   return 1;
@@ -1023,24 +1026,33 @@ int reg_store_int16(short *d, FPU_REG *st0_ptr)
 
 
 /* Put a packed bcd array into user memory */
-int reg_store_bcd(char *d, FPU_REG *st0_ptr)
+int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char *d)
 {
   FPU_REG t;
   unsigned long long ll;
-  unsigned char b;
+  u_char b;
   int i, precision_loss;
-  unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
-  char st0_tag = st0_ptr->tag;
+  u_char sign = (getsign(st0_ptr) == SIGN_NEG) ? 0x80 : 0;
 
-  if ( st0_tag == TW_Empty )
+  if ( st0_tag == TAG_Empty )
     {
       /* Empty register (stack underflow) */
       EXCEPTION(EX_StackUnder);
       goto invalid_operand;
     }
+  else if ( st0_tag == TAG_Special )
+    {
+      st0_tag = FPU_Special(st0_ptr);
+      if ( (st0_tag == TW_Infinity) ||
+          (st0_tag == TW_NaN) )
+       {
+         EXCEPTION(EX_Invalid);
+         goto invalid_operand;
+       }
+    }
 
-  reg_move(st0_ptr, &t);
-  precision_loss = round_to_int(&t);
+  reg_copy(st0_ptr, &t);
+  precision_loss = FPU_round_to_int(&t, st0_tag);
   ll = significand(&t);
 
   /* Check for overflow, by comparing with 999999999999999999 decimal. */
@@ -1056,10 +1068,10 @@ int reg_store_bcd(char *d, FPU_REG *st0_ptr)
          RE_ENTRANT_CHECK_OFF;
          FPU_verify_area(VERIFY_WRITE,d,10);
          for ( i = 0; i < 7; i++)
-           put_fs_byte(0, (unsigned char *) d+i); /* These bytes "undefined" */
-         put_fs_byte(0xc0, (unsigned char *) d+7); /* This byte "undefined" */
-         put_fs_byte(0xff, (unsigned char *) d+8);
-         put_fs_byte(0xff, (unsigned char *) d+9);
+           FPU_put_user(0, (u_char *) d+i); /* These bytes "undefined" */
+         FPU_put_user(0xc0, (u_char *) d+7); /* This byte "undefined" */
+         FPU_put_user(0xff, (u_char *) d+8);
+         FPU_put_user(0xff, (u_char *) d+9);
          RE_ENTRANT_CHECK_ON;
          return 1;
        }
@@ -1077,14 +1089,14 @@ int reg_store_bcd(char *d, FPU_REG *st0_ptr)
   RE_ENTRANT_CHECK_ON;
   for ( i = 0; i < 9; i++)
     {
-      b = div_small(&ll, 10);
-      b |= (div_small(&ll, 10)) << 4;
+      b = FPU_div_small(&ll, 10);
+      b |= (FPU_div_small(&ll, 10)) << 4;
       RE_ENTRANT_CHECK_OFF;
-      put_fs_byte(b,(unsigned char *) d+i);
+      FPU_put_user(b,(u_char *) d+i);
       RE_ENTRANT_CHECK_ON;
     }
   RE_ENTRANT_CHECK_OFF;
-  put_fs_byte(sign,(unsigned char *) d+9);
+  FPU_put_user(sign,(u_char *) d+9);
   RE_ENTRANT_CHECK_ON;
 
   return 1;
@@ -1100,25 +1112,25 @@ int reg_store_bcd(char *d, FPU_REG *st0_ptr)
 /* Overflow is signalled by a non-zero return value (in eax).
    In the case of overflow, the returned significand always has the
    largest possible value */
-int round_to_int(FPU_REG *r)
+int FPU_round_to_int(FPU_REG *r, u_char tag)
 {
-  char     very_big;
+  u_char     very_big;
   unsigned eax;
 
-  if (r->tag == TW_Zero)
+  if (tag == TAG_Zero)
     {
       /* Make sure that zero is returned */
       significand(r) = 0;
       return 0;        /* o.k. */
     }
-  
-  if (r->exp > EXP_BIAS + 63)
+
+  if (exponent(r) > 63)
     {
       r->sigl = r->sigh = ~0;      /* The largest representable number */
       return 1;        /* overflow */
     }
 
-  eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
+  eax = FPU_shrxs(&r->sigl, 63 - exponent(r));
   very_big = !(~(r->sigh) | ~(r->sigl));  /* test for 0xfff...fff */
 #define        half_or_more    (eax & 0x80000000)
 #define        frac_part       (eax)
@@ -1135,7 +1147,7 @@ int round_to_int(FPU_REG *r)
        }
       break;
     case RC_DOWN:
-      if (frac_part && r->sign)
+      if (frac_part && getsign(r))
        {
          if ( very_big ) return 1;        /* overflow */
          significand(r) ++;
@@ -1143,7 +1155,7 @@ int round_to_int(FPU_REG *r)
        }
       break;
     case RC_UP:
-      if (frac_part && !r->sign)
+      if (frac_part && !getsign(r))
        {
          if ( very_big ) return 1;        /* overflow */
          significand(r) ++;
@@ -1160,10 +1172,10 @@ int round_to_int(FPU_REG *r)
 
 /*===========================================================================*/
 
-char *fldenv(fpu_addr_modes addr_modes, char *s)
+u_char *fldenv(fpu_addr_modes addr_modes, u_char *s)
 {
   unsigned short tag_word = 0;
-  unsigned char tag;
+  u_char tag;
   int i;
 
   if ( (addr_modes.default_mode == VM86) ||
@@ -1172,13 +1184,13 @@ char *fldenv(fpu_addr_modes addr_modes, char *s)
     {
       RE_ENTRANT_CHECK_OFF;
       FPU_verify_area(VERIFY_READ, s, 0x0e);
-      control_word = get_fs_word((unsigned short *) s);
-      partial_status = get_fs_word((unsigned short *) (s+2));
-      tag_word = get_fs_word((unsigned short *) (s+4));
-      instruction_address.offset = get_fs_word((unsigned short *) (s+6));
-      instruction_address.selector = get_fs_word((unsigned short *) (s+8));
-      operand_address.offset = get_fs_word((unsigned short *) (s+0x0a));
-      operand_address.selector = get_fs_word((unsigned short *) (s+0x0c));
+      FPU_get_user(control_word, (unsigned short *) s);
+      FPU_get_user(partial_status, (unsigned short *) (s+2));
+      FPU_get_user(tag_word, (unsigned short *) (s+4));
+      FPU_get_user(instruction_address.offset, (unsigned short *) (s+6));
+      FPU_get_user(instruction_address.selector, (unsigned short *) (s+8));
+      FPU_get_user(operand_address.offset, (unsigned short *) (s+0x0a));
+      FPU_get_user(operand_address.selector, (unsigned short *) (s+0x0c));
       RE_ENTRANT_CHECK_ON;
       s += 0x0e;
       if ( addr_modes.default_mode == VM86 )
@@ -1192,23 +1204,23 @@ char *fldenv(fpu_addr_modes addr_modes, char *s)
     {
       RE_ENTRANT_CHECK_OFF;
       FPU_verify_area(VERIFY_READ, s, 0x1c);
-      control_word = get_fs_word((unsigned short *) s);
-      partial_status = get_fs_word((unsigned short *) (s+4));
-      tag_word = get_fs_word((unsigned short *) (s+8));
-      instruction_address.offset = get_fs_long((unsigned long *) (s+0x0c));
-      instruction_address.selector = get_fs_word((unsigned short *) (s+0x10));
-      instruction_address.opcode = get_fs_word((unsigned short *) (s+0x12));
-      operand_address.offset = get_fs_long((unsigned long *) (s+0x14));
-      operand_address.selector = get_fs_long((unsigned long *) (s+0x18));
+      FPU_get_user(control_word, (unsigned short *) s);
+      FPU_get_user(partial_status, (unsigned short *) (s+4));
+      FPU_get_user(tag_word, (unsigned short *) (s+8));
+      FPU_get_user(instruction_address.offset, (unsigned long *) (s+0x0c));
+      FPU_get_user(instruction_address.selector, (unsigned short *) (s+0x10));
+      FPU_get_user(instruction_address.opcode, (unsigned short *) (s+0x12));
+      FPU_get_user(operand_address.offset, (unsigned long *) (s+0x14));
+      FPU_get_user(operand_address.selector, (unsigned long *) (s+0x18));
       RE_ENTRANT_CHECK_ON;
       s += 0x1c;
     }
 
 #ifdef PECULIAR_486
   control_word &= ~0xe080;
-#endif /* PECULIAR_486 */
+#endif /* PECULIAR_486 */ 
 
-  top = (partial_status >> SW_Top_Shift) & 7;
+  FPU_top = (partial_status >> SW_Top_Shift) & 7;
 
   if ( partial_status & ~control_word & CW_Exceptions )
     partial_status |= (SW_Summary | SW_Backward);
@@ -1220,29 +1232,28 @@ char *fldenv(fpu_addr_modes addr_modes, char *s)
       tag = tag_word & 3;
       tag_word >>= 2;
 
-      if ( tag == 3 )
+      if ( tag == TAG_Empty )
        /* New tag is empty.  Accept it */
-       regs[i].tag = TW_Empty;
-      else if ( regs[i].tag == TW_Empty )
+       FPU_settag(i, TAG_Empty);
+      else if ( FPU_gettag(i) == TAG_Empty )
        {
          /* Old tag is empty and new tag is not empty.  New tag is determined
             by old reg contents */
-         if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias )
+         if ( exponent(&fpu_register(i)) == - EXTENDED_Ebias )
            {
-             if ( !(regs[i].sigl | regs[i].sigh) )
-               regs[i].tag = TW_Zero;
+             if ( !(fpu_register(i).sigl | fpu_register(i).sigh) )
+               FPU_settag(i, TAG_Zero);
              else
-               regs[i].tag = TW_Valid;
+               FPU_settag(i, TAG_Special);
            }
-         else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias )
+         else if ( exponent(&fpu_register(i)) == 0x7fff - EXTENDED_Ebias )
            {
-             if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) )
-               regs[i].tag = TW_Infinity;
-             else
-               regs[i].tag = TW_NaN;
+             FPU_settag(i, TAG_Special);
            }
+         else if ( fpu_register(i).sigh & 0x80000000 )
+           FPU_settag(i, TAG_Valid);
          else
-           regs[i].tag = TW_Valid;
+           FPU_settag(i, TAG_Special);   /* An Un-normal */
        }
       /* Else old tag is not empty and new tag is not empty.  Old tag
         remains correct */
@@ -1252,56 +1263,32 @@ char *fldenv(fpu_addr_modes addr_modes, char *s)
 }
 
 
-void frstor(fpu_addr_modes addr_modes, char *data_address)
+void frstor(fpu_addr_modes addr_modes, u_char *data_address)
 {
-  int i, stnr;
-  unsigned char tag;
-  char *s = fldenv(addr_modes, data_address);
+  int i, regnr;
+  u_char *s = fldenv(addr_modes, data_address);
+  int offset = (FPU_top & 7) * 10, other = 80 - offset;
+
+  /* Copy all registers in stack order. */
+  RE_ENTRANT_CHECK_OFF;
+  FPU_verify_area(VERIFY_READ,s,80);
+  FPU_copy_from_user(register_base+offset, s, other);
+  if ( offset )
+    FPU_copy_from_user(register_base, s+other, offset);
+  RE_ENTRANT_CHECK_ON;
 
   for ( i = 0; i < 8; i++ )
     {
-      /* Load each register. */
-      stnr = (i+top) & 7;
-      tag = regs[stnr].tag;   /* Derived from the fldenv() loaded tag word. */
-      reg_load_extended((long double *)(s+i*10), &regs[stnr]);
-      if ( tag == TW_Empty )  /* The loaded data over-rides all other cases. */
-       regs[stnr].tag = tag;
+      regnr = (i+FPU_top) & 7;
+      if ( FPU_gettag(regnr) != TAG_Empty )
+       /* The loaded data over-rides all other cases. */
+       FPU_settag(regnr, FPU_tagof(&st(i)));
     }
 
 }
 
 
-unsigned short tag_word(void)
-{
-  unsigned short word = 0;
-  unsigned char tag;
-  int i;
-
-  for ( i = 7; i >= 0; i-- )
-    {
-      switch ( tag = regs[i].tag )
-       {
-       case TW_Valid:
-         if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) )
-           tag = 2;
-         break;
-       case TW_Infinity:
-       case TW_NaN:
-         tag = 2;
-         break;
-       case TW_Empty:
-         tag = 3;
-         break;
-         /* TW_Zero already has the correct value */
-       }
-      word <<= 2;
-      word |= tag;
-    }
-  return word;
-}
-
-
-char *fstenv(fpu_addr_modes addr_modes, char *d)
+u_char *fstenv(fpu_addr_modes addr_modes, u_char *d)
 {
   if ( (addr_modes.default_mode == VM86) ||
       ((addr_modes.default_mode == PM16)
@@ -1310,25 +1297,25 @@ char *fstenv(fpu_addr_modes addr_modes, char *d)
       RE_ENTRANT_CHECK_OFF;
       FPU_verify_area(VERIFY_WRITE,d,14);
 #ifdef PECULIAR_486
-      put_fs_long(control_word & ~0xe080, (unsigned short *) d);
+      FPU_put_user(control_word & ~0xe080, (unsigned long *) d);
 #else
-      put_fs_word(control_word, (unsigned short *) d);
+      FPU_put_user(control_word, (unsigned short *) d);
 #endif /* PECULIAR_486 */
-      put_fs_word(status_word(), (unsigned short *) (d+2));
-      put_fs_word(tag_word(), (unsigned short *) (d+4));
-      put_fs_word(instruction_address.offset, (unsigned short *) (d+6));
-      put_fs_word(operand_address.offset, (unsigned short *) (d+0x0a));
+      FPU_put_user(status_word(), (unsigned short *) (d+2));
+      FPU_put_user(fpu_tag_word, (unsigned short *) (d+4));
+      FPU_put_user(instruction_address.offset, (unsigned short *) (d+6));
+      FPU_put_user(operand_address.offset, (unsigned short *) (d+0x0a));
       if ( addr_modes.default_mode == VM86 )
        {
-         put_fs_word((instruction_address.offset & 0xf0000) >> 4,
+         FPU_put_user((instruction_address.offset & 0xf0000) >> 4,
                      (unsigned short *) (d+8));
-         put_fs_word((operand_address.offset & 0xf0000) >> 4,
+         FPU_put_user((operand_address.offset & 0xf0000) >> 4,
                      (unsigned short *) (d+0x0c));
        }
       else
        {
-         put_fs_word(instruction_address.selector, (unsigned short *) (d+8));
-         put_fs_word(operand_address.selector, (unsigned short *) (d+0x0c));
+         FPU_put_user(instruction_address.selector, (unsigned short *) (d+8));
+         FPU_put_user(operand_address.selector, (unsigned short *) (d+0x0c));
        }
       RE_ENTRANT_CHECK_ON;
       d += 0x0e;
@@ -1336,28 +1323,17 @@ char *fstenv(fpu_addr_modes addr_modes, char *d)
   else
     {
       RE_ENTRANT_CHECK_OFF;
-      FPU_verify_area(VERIFY_WRITE,d,28);
+      FPU_verify_area(VERIFY_WRITE, d, 7*4);
 #ifdef PECULIAR_486
-      /* An 80486 sets all the reserved bits to 1. */
-      put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d);
-      put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4));
-      put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8));
-#else
-      put_fs_word(control_word, (unsigned short *) d);
-      put_fs_word(status_word(), (unsigned short *) (d+4));
-      put_fs_word(tag_word(), (unsigned short *) (d+8));
-#endif /* PECULIAR_486 */
-      put_fs_long(instruction_address.offset, (unsigned long *) (d+0x0c));
-      put_fs_word(instruction_address.selector, (unsigned short *) (d+0x10));
-      put_fs_word(instruction_address.opcode, (unsigned short *) (d+0x12));
-      put_fs_long(operand_address.offset, (unsigned long *) (d+0x14));
-#ifdef PECULIAR_486
-      /* An 80486 sets all the reserved bits to 1. */
-      put_fs_word(operand_address.selector, (unsigned short *) (d+0x18));
-      put_fs_word(0xffff, (unsigned short *) (d+0x1a));
-#else
-      put_fs_long(operand_address.selector, (unsigned long *) (d+0x18));
+      control_word &= ~0xe080;
+      /* An 80486 sets nearly all of the reserved bits to 1. */
+      control_word |= 0xffff0040;
+      partial_status = status_word() | 0xffff0000;
+      fpu_tag_word |= 0xffff0000;
+      I387.soft.fcs &= ~0xf8000000;
+      I387.soft.fos |= 0xffff0000;
 #endif /* PECULIAR_486 */
+      FPU_copy_to_user(d, &control_word, 7*4);
       RE_ENTRANT_CHECK_ON;
       d += 0x1c;
     }
@@ -1369,84 +1345,23 @@ char *fstenv(fpu_addr_modes addr_modes, char *d)
 }
 
 
-void fsave(fpu_addr_modes addr_modes, char *data_address)
+void fsave(fpu_addr_modes addr_modes, u_char *data_address)
 {
-  char *d;
-  int i;
+  u_char *d;
+  int offset = (FPU_top & 7) * 10, other = 80 - offset;
 
   d = fstenv(addr_modes, data_address);
+
   RE_ENTRANT_CHECK_OFF;
   FPU_verify_area(VERIFY_WRITE,d,80);
+
+  /* Copy all registers in stack order. */
+  FPU_copy_to_user(d, register_base+offset, other);
+  if ( offset )
+    FPU_copy_to_user(d+other, register_base, offset);
   RE_ENTRANT_CHECK_ON;
-  for ( i = 0; i < 8; i++ )
-    write_to_extended(&regs[(top + i) & 7], d + 10 * i);
 
   finit();
-
 }
 
 /*===========================================================================*/
-
-/*
-  A call to this function must be preceded by a call to
-  FPU_verify_area() to verify access to the 10 bytes at d
-  */
-static void write_to_extended(FPU_REG *rp, char *d)
-{
-  long e;
-  FPU_REG tmp;
-  
-  e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
-
-#ifdef PARANOID
-  switch ( rp->tag )
-    {
-    case TW_Zero:
-      if ( rp->sigh | rp->sigl | e )
-       EXCEPTION(EX_INTERNAL | 0x160);
-      break;
-    case TW_Infinity:
-    case TW_NaN:
-      if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) )
-       EXCEPTION(EX_INTERNAL | 0x161);
-      break;
-    default:
-      if (e > 0x7fff || e < -63)
-       EXCEPTION(EX_INTERNAL | 0x162);
-    }
-#endif /* PARANOID */
-
-  /*
-    All numbers except denormals are stored internally in a
-    format which is compatible with the extended real number
-    format.
-   */
-  if ( e > 0 )
-    {
-      /* just copy the reg */
-      RE_ENTRANT_CHECK_OFF;
-      put_fs_long(rp->sigl, (unsigned long *) d);
-      put_fs_long(rp->sigh, (unsigned long *) (d + 4));
-      RE_ENTRANT_CHECK_ON;
-    }
-  else
-    {
-      /*
-       The number is a de-normal stored as a normal using our
-       extra exponent range, or is Zero.
-       Convert it back to a de-normal, or leave it as Zero.
-       */
-      reg_move(rp, &tmp);
-      tmp.exp += -EXTENDED_Emin + 63;  /* largest exp to be 63 */
-      round_to_int(&tmp);
-      e = 0;
-      RE_ENTRANT_CHECK_OFF;
-      put_fs_long(tmp.sigl, (unsigned long *) d);
-      put_fs_long(tmp.sigh, (unsigned long *) (d + 4));
-      RE_ENTRANT_CHECK_ON;
-    }
-  e |= rp->sign == SIGN_POS ? 0 : 0x8000;
-  RE_ENTRANT_CHECK_OFF;
-  put_fs_word(e, (unsigned short *) (d + 8));
-  RE_ENTRANT_CHECK_ON;
-}
index 18548b12b1636c85cefdfb6abc7f50aba992d8b9..aacae7c083bb010853d59a3a2186a5c456513c03 100644 (file)
@@ -3,10 +3,11 @@
  |                                                                           |
  | Multiply one FPU_REG by another, put the result in a destination FPU_REG. |
  |                                                                           |
- | Copyright (C) 1992,1993                                                   |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1997                                              |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
+ | Returns the tag of the result if no exceptions or errors occurred.        |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
  | The destination may be any FPU_REG, including one of the source FPU_REGs. |
  +---------------------------------------------------------------------------*/
 
+#include "fpu_emu.h"
 #include "exception.h"
 #include "reg_constant.h"
-#include "fpu_emu.h"
 #include "fpu_system.h"
 
 
+/*
+  Multiply two registers to give a register result.
+  The sources are st(deststnr) and (b,tagb,signb).
+  The destination is st(deststnr).
+  */
 /* This routine must be called with non-empty source registers */
-int reg_mul(FPU_REG const *a, FPU_REG const *b,
-           FPU_REG *dest, unsigned int control_w)
+int FPU_mul(FPU_REG const *b, u_char tagb, int deststnr, int control_w)
 {
-  char saved_sign = dest->sign;
-  char sign = (a->sign ^ b->sign);
+  FPU_REG *a = &st(deststnr);
+  FPU_REG *dest = a;
+  u_char taga = FPU_gettagi(deststnr);
+  u_char saved_sign = getsign(dest);
+  u_char sign = (getsign(a) ^ getsign(b));
+  int tag;
 
-  if (!(a->tag | b->tag))
+
+  if ( !(taga | tagb) )
     {
       /* Both regs Valid, this should be the most common case. */
-      dest->sign = sign;
-      if ( reg_u_mul(a, b, dest, control_w) )
+
+      tag = FPU_u_mul(a, b, dest, control_w, sign, exponent(a) + exponent(b));
+      if ( tag < 0 )
        {
-         dest->sign = saved_sign;
-         return 1;
+         setsign(dest, saved_sign);
+         return tag;
        }
-      return 0;
+      FPU_settagi(deststnr, tag);
+      return tag;
     }
-  else if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero))
+
+  if ( taga == TAG_Special )
+    taga = FPU_Special(a);
+  if ( tagb == TAG_Special )
+    tagb = FPU_Special(b);
+
+  if ( ((taga == TAG_Valid) && (tagb == TW_Denormal))
+           || ((taga == TW_Denormal) && (tagb == TAG_Valid))
+           || ((taga == TW_Denormal) && (tagb == TW_Denormal)) )
     {
-#ifdef DENORM_OPERAND
-      if ( ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ||
-         ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER)) )
+      FPU_REG x, y;
+      if ( denormal_operand() < 0 )
+       return FPU_Exception;
+
+      FPU_to_exp16(a, &x);
+      FPU_to_exp16(b, &y);
+      tag = FPU_u_mul(&x, &y, dest, control_w, sign,
+                     exponent16(&x) + exponent16(&y));
+      if ( tag < 0 )
        {
-         if ( denormal_operand() ) return 1;
+         setsign(dest, saved_sign);
+         return tag;
        }
-#endif /* DENORM_OPERAND */
+      FPU_settagi(deststnr, tag);
+      return tag;
+    }
+  else if ( (taga <= TW_Denormal) && (tagb <= TW_Denormal) )
+    {
+      if ( ((tagb == TW_Denormal) || (taga == TW_Denormal))
+          && (denormal_operand() < 0) )
+       return FPU_Exception;
+
       /* Must have either both arguments == zero, or
         one valid and the other zero.
         The result is therefore zero. */
-      reg_move(&CONST_Z, dest);
+      FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
       /* The 80486 book says that the answer is +0, but a real
         80486 behaves this way.
         IEEE-754 apparently says it should be this way. */
-      dest->sign = sign;
-      return 0;
+      setsign(dest, sign);
+      return TAG_Zero;
     }
-  else
-    {
       /* Must have infinities, NaNs, etc */
-      if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
-       { return real_2op_NaN(a, b, dest); }
-      else if (a->tag == TW_Infinity)
-       {
-         if (b->tag == TW_Zero)
-           { return arith_invalid(dest); }  /* Zero*Infinity is invalid */
-         else
-           {
-#ifdef DENORM_OPERAND
-             if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
-                 denormal_operand() )
-               return 1;
-#endif /* DENORM_OPERAND */
-             reg_move(a, dest);
-             dest->sign = sign;
-           }
-         return 0;
-       }
-      else if (b->tag == TW_Infinity)
-       {
-         if (a->tag == TW_Zero)
-           { return arith_invalid(dest); }  /* Zero*Infinity is invalid */
-         else
-           {
-#ifdef DENORM_OPERAND
-             if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
-                 denormal_operand() )
-               return 1;
-#endif /* DENORM_OPERAND */
-             reg_move(b, dest);
-             dest->sign = sign;
-           }
-         return 0;
-       }
+  else if ( (taga == TW_NaN) || (tagb == TW_NaN) )
+    {
+      return real_2op_NaN(b, tagb, deststnr, &st(0));
+    }
+  else if ( ((taga == TW_Infinity) && (tagb == TAG_Zero))
+           || ((tagb == TW_Infinity) && (taga == TAG_Zero)) )
+    {
+      return arith_invalid(deststnr);  /* Zero*Infinity is invalid */
+    }
+  else if ( ((taga == TW_Denormal) || (tagb == TW_Denormal))
+           && (denormal_operand() < 0) )
+    {
+      return FPU_Exception;
+    }
+  else if (taga == TW_Infinity)
+    {
+      FPU_copy_to_regi(a, TAG_Special, deststnr);
+      setsign(dest, sign);
+      return TAG_Special;
+    }
+  else if (tagb == TW_Infinity)
+    {
+      FPU_copy_to_regi(b, TAG_Special, deststnr);
+      setsign(dest, sign);
+      return TAG_Special;
+    }
+
 #ifdef PARANOID
-      else
-       {
-         EXCEPTION(EX_INTERNAL|0x102);
-         return 1;
-       }
-#endif /* PARANOID */
+  else
+    {
+      EXCEPTION(EX_INTERNAL|0x102);
+      return FPU_Exception;
     }
+#endif /* PARANOID */ 
+
 }
index a44d1a51ac9ec87e8c6706f42f6d5152ec3afe4a..79ea91084d7a73edfb773bcc2c1e734bb0e65c5c 100644 (file)
@@ -1,16 +1,19 @@
 /*---------------------------------------------------------------------------+
  |  reg_norm.S                                                               |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1995                                         |
+ | Copyright (C) 1992,1993,1994,1995,1997                                    |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@jacobi.maths.monash.edu.au |
+ |                       Australia.  E-mail billm@suburbia.net               |
  |                                                                           |
  | Normalize the value in a FPU_REG.                                         |
  |                                                                           |
  | Call from C as:                                                           |
- |   void normalize(FPU_REG *n)                                              |
+ |    int FPU_normalize(FPU_REG *n)                                          |
  |                                                                           |
- |   void normalize_nuo(FPU_REG *n)                                          |
+ |    int FPU_normalize_nuo(FPU_REG *n)                                      |
+ |                                                                           |
+ |    Return value is the tag of the answer, or-ed with FPU_Exception if     |
+ |    one was raised, or -1 on internal error.                               |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
 
 
 .text
-ENTRY(normalize)
+ENTRY(FPU_normalize)
        pushl   %ebp
        movl    %esp,%ebp
        pushl   %ebx
 
        movl    PARAM1,%ebx
 
-#ifdef PARANOID
-       cmpb    TW_Valid,TAG(%ebx)
-       je      L_ok
-
-       pushl   $0x220
-       call    SYMBOL_NAME(exception)
-       addl    $4,%esp
-
-L_ok:
-#endif /* PARANOID */
-
        movl    SIGH(%ebx),%edx
        movl    SIGL(%ebx),%eax
 
@@ -48,7 +40,7 @@ L_ok:
 
        movl    %eax,%edx
        xorl    %eax,%eax
-       subl    $32,EXP(%ebx)   /* This can cause an underflow */
+       subw    $32,EXP(%ebx)   /* This can cause an underflow */
 
 /* We need to shift left by 1 - 31 bits */
 L_shift_1:
@@ -57,18 +49,25 @@ L_shift_1:
        negl    %ecx
        shld    %cl,%eax,%edx
        shl     %cl,%eax
-       subl    %ecx,EXP(%ebx)  /* This can cause an underflow */
+       subw    %cx,EXP(%ebx)   /* This can cause an underflow */
 
        movl    %edx,SIGH(%ebx)
        movl    %eax,SIGL(%ebx)
 
 L_done:
-       cmpl    EXP_OVER,EXP(%ebx)
+       cmpw    EXP_OVER,EXP(%ebx)
        jge     L_overflow
 
-       cmpl    EXP_UNDER,EXP(%ebx)
+       cmpw    EXP_UNDER,EXP(%ebx)
        jle     L_underflow
 
+L_exit_valid:
+       movl    TAG_Valid,%eax
+
+       /* Convert the exponent to 80x87 form. */
+       addw    EXTENDED_Ebias,EXP(%ebx)
+       andw    $0x7fff,EXP(%ebx)
+
 L_exit:
        popl    %ebx
        leave
@@ -76,56 +75,57 @@ L_exit:
 
 
 L_zero:
-       movl    EXP_UNDER,EXP(%ebx)
-       movb    TW_Zero,TAG(%ebx)
+       movw    $0,EXP(%ebx)
+       movl    TAG_Zero,%eax
        jmp     L_exit
 
 L_underflow:
+       /* Convert the exponent to 80x87 form. */
+       addw    EXTENDED_Ebias,EXP(%ebx)
        push    %ebx
        call    SYMBOL_NAME(arith_underflow)
        pop     %ebx
        jmp     L_exit
 
 L_overflow:
+       /* Convert the exponent to 80x87 form. */
+       addw    EXTENDED_Ebias,EXP(%ebx)
+       cmpb    SIGN_POS,PARAM2
+       jne     L_ovfl_neg
+       push    $0
+       jmp     L_ovfl_signed
+L_ovfl_neg:
+       push    $1
+L_ovfl_signed:
        push    %ebx
        call    SYMBOL_NAME(arith_overflow)
        pop     %ebx
+       addl    $4,%esp
        jmp     L_exit
 
 
 
 /* Normalise without reporting underflow or overflow */
-ENTRY(normalize_nuo)
+ENTRY(FPU_normalize_nuo)
        pushl   %ebp
        movl    %esp,%ebp
        pushl   %ebx
 
        movl    PARAM1,%ebx
 
-#ifdef PARANOID
-       cmpb    TW_Valid,TAG(%ebx)
-       je      L_ok_nuo
-
-       pushl   $0x221
-       call    SYMBOL_NAME(exception)
-       addl    $4,%esp
-
-L_ok_nuo:
-#endif /* PARANOID */
-
        movl    SIGH(%ebx),%edx
        movl    SIGL(%ebx),%eax
 
        orl     %edx,%edx       /* ms bits */
-       js      L_exit          /* Already normalized */
+       js      L_exit_nuo_valid        /* Already normalized */
        jnz     L_nuo_shift_1   /* Shift left 1 - 31 bits */
 
        orl     %eax,%eax
-       jz      L_zero          /* The contents are zero */
+       jz      L_exit_nuo_zero         /* The contents are zero */
 
        movl    %eax,%edx
        xorl    %eax,%eax
-       subl    $32,EXP(%ebx)   /* This can cause an underflow */
+       subw    $32,EXP(%ebx)   /* This can cause an underflow */
 
 /* We need to shift left by 1 - 31 bits */
 L_nuo_shift_1:
@@ -134,10 +134,22 @@ L_nuo_shift_1:
        negl    %ecx
        shld    %cl,%eax,%edx
        shl     %cl,%eax
-       subl    %ecx,EXP(%ebx)  /* This can cause an underflow */
+       subw    %cx,EXP(%ebx)   /* This can cause an underflow */
 
        movl    %edx,SIGH(%ebx)
        movl    %eax,SIGL(%ebx)
-       jmp     L_exit
 
+L_exit_nuo_valid:
+       movl    TAG_Valid,%eax
 
+       popl    %ebx
+       leave
+       ret
+
+L_exit_nuo_zero:
+       movl    TAG_Zero,%eax
+       movw    EXP_UNDER,EXP(%ebx)
+
+       popl    %ebx
+       leave
+       ret
index 25626a1cb52eb45d6d418df811d9c6d877bc3d19..d9c7f3cdc3f27d890f30b0ab9d59d5ecd009242b 100644 (file)
@@ -4,17 +4,20 @@
  |                                                                           |
  | Rounding/truncation/etc for FPU basic arithmetic functions.               |
  |                                                                           |
- | Copyright (C) 1993,1995                                                   |
+ | Copyright (C) 1993,1995,1997                                              |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@jacobi.maths.monash.edu.au |
+ |                       Australia.  E-mail billm@suburbia.net               |
  |                                                                           |
- | This code has four possible entry points.                                 |
+ | This code has three possible entry points.                                |
  | The following must be entered by a jmp instruction:                       |
- |   fpu_reg_round, fpu_reg_round_sqrt, and fpu_Arith_exit.                  |
+ |   fpu_reg_round, and fpu_Arith_exit.                                      |
  |                                                                           |
- | The _round_reg entry point is intended to be used by C code.              |
+ | The FPU_round entry point is intended to be used by C code.               |
  | From C, call as:                                                          |
- | void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) |
+ |  int FPU_round(FPU_REG *arg, unsigned int extent, unsigned int control_w) |
+ |                                                                           |
+ |    Return value is the tag of the answer, or-ed with FPU_Exception if     |
+ |    one was raised, or -1 on internal error.                               |
  |                                                                           |
  | For correct "up" and "down" rounding, the argument must have the correct  |
  | sign.                                                                     |
  +---------------------------------------------------------------------------*/
 
 /*---------------------------------------------------------------------------+
- | Four entry points.                                                        |
+ | Three entry points.                                                       |
  |                                                                           |
- | Needed by both the fpu_reg_round and fpu_reg_round_sqrt entry points:     |
+ | Needed by the fpu_reg_round entry point:                                  |
  |  %eax:%ebx  64 bit significand                                            |
  |  %edx       32 bit extension of the significand                           |
  |  %edi       pointer to an FPU_REG for the result to be stored             |
  |  stack      calling function must have set up a C stack frame and         |
  |             pushed %esi, %edi, and %ebx                                   |
  |                                                                           |
- | Needed just for the fpu_reg_round_sqrt entry point:                       |
- |  %cx  A control word in the same format as the FPU control word.          |
- | Otherwise, PARAM4 must give such a value.                                 |
+ | A control word in the same format as the FPU control word must            |
+ | be in PARAM4.                                                             |
  |                                                                           |
  |                                                                           |
  | The significand and its extension are assumed to be exact in the          |
@@ -92,7 +94,7 @@
 /*     Not re-entrant, so we can gain speed by putting
        local storage in a static area: */
 .data
-       .align 2,0
+       .align 4,0
 FPU_bits_lost:
        .byte   0
 FPU_denormal:
@@ -102,11 +104,10 @@ FPU_denormal:
 
 .text
 .globl fpu_reg_round
-.globl fpu_reg_round_sqrt
 .globl fpu_Arith_exit
 
 /* Entry point when called from C */
-ENTRY(round_reg)
+ENTRY(FPU_round)
        pushl   %ebp
        movl    %esp,%ebp
        pushl   %esi
@@ -117,17 +118,13 @@ ENTRY(round_reg)
        movl    SIGH(%edi),%eax
        movl    SIGL(%edi),%ebx
        movl    PARAM2,%edx
-       movl    PARAM3,%ecx
-       jmp     fpu_reg_round_sqrt
 
 fpu_reg_round:                 /* Normal entry point */
        movl    PARAM4,%ecx
 
-fpu_reg_round_sqrt:            /* Entry point from wm_sqrt.S */
-
 #ifndef NON_REENTRANT_FPU
        pushl   %ebx            /* adjust the stack pointer */
-#endif /* NON_REENTRANT_FPU */
+#endif /* NON_REENTRANT_FPU */ 
 
 #ifdef PARANOID
 /* Cannot use this here yet */
@@ -135,12 +132,12 @@ fpu_reg_round_sqrt:               /* Entry point from wm_sqrt.S */
 /*     jns     L_entry_bugged */
 #endif /* PARANOID */
 
-       cmpl    EXP_UNDER,EXP(%edi)
-       jle     xMake_denorm                    /* The number is a de-normal */
+       cmpw    EXP_UNDER,EXP(%edi)
+       jle     L_Make_denorm                   /* The number is a de-normal */
 
        movb    $0,FPU_denormal                 /* 0 -> not a de-normal */
 
-xDenorm_done:
+Denorm_done:
        movb    $0,FPU_bits_lost                /* No bits yet lost in rounding */
 
        movl    %ecx,%esi
@@ -161,11 +158,11 @@ xDenorm_done:
        je      LRound_To_64
 #ifdef PARANOID
        jmp     L_bugged_denorm_486
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 #else
 #ifdef PARANOID
        jmp     L_bugged_denorm /* There is no bug, just a bad control word */
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 #endif /* PECULIAR_486 */
 
 
@@ -187,16 +184,16 @@ LRound_To_24:
 
 #ifdef PARANOID
        jmp     L_bugged_round24
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
 LUp_24:
-       cmpb    SIGN_POS,SIGN(%edi)
+       cmpb    SIGN_POS,PARAM5
        jne     LCheck_truncate_24      /* If negative then  up==truncate */
 
        jmp     LCheck_24_round_up
 
 LDown_24:
-       cmpb    SIGN_POS,SIGN(%edi)
+       cmpb    SIGN_POS,PARAM5
        je      LCheck_truncate_24      /* If positive then  down==truncate */
 
 LCheck_24_round_up:
@@ -205,7 +202,7 @@ LCheck_24_round_up:
        orl     %ebx,%ecx
        orl     %edx,%ecx
        jnz     LDo_24_round_up
-       jmp     LRe_normalise
+       jmp     L_Re_normalise
 
 LRound_nearest_24:
        /* Do rounding of the 24th bit if needed (nearest or even) */
@@ -240,13 +237,13 @@ LCheck_truncate_24:
        andl    $0x000000ff,%ecx
        orl     %ebx,%ecx
        orl     %edx,%ecx
-       jz      LRe_normalise           /* No truncation needed */
+       jz      L_Re_normalise          /* No truncation needed */
 
 LDo_truncate_24:
        andl    $0xffffff00,%eax        /* Truncate to 24 bits */
        xorl    %ebx,%ebx
        movb    LOST_DOWN,FPU_bits_lost
-       jmp     LRe_normalise
+       jmp     L_Re_normalise
 
 
 /* Round etc to 53 bit precision */
@@ -267,16 +264,16 @@ LRound_To_53:
 
 #ifdef PARANOID
        jmp     L_bugged_round53
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
 LUp_53:
-       cmpb    SIGN_POS,SIGN(%edi)
+       cmpb    SIGN_POS,PARAM5
        jne     LCheck_truncate_53      /* If negative then  up==truncate */
 
        jmp     LCheck_53_round_up
 
 LDown_53:
-       cmpb    SIGN_POS,SIGN(%edi)
+       cmpb    SIGN_POS,PARAM5
        je      LCheck_truncate_53      /* If positive then  down==truncate */
 
 LCheck_53_round_up:
@@ -284,7 +281,7 @@ LCheck_53_round_up:
        andl    $0x000007ff,%ecx
        orl     %edx,%ecx
        jnz     LDo_53_round_up
-       jmp     LRe_normalise
+       jmp     L_Re_normalise
 
 LRound_nearest_53:
        /* Do rounding of the 53rd bit if needed (nearest or even) */
@@ -315,12 +312,12 @@ LCheck_truncate_53:
        movl    %ebx,%ecx
        andl    $0x000007ff,%ecx
        orl     %edx,%ecx
-       jz      LRe_normalise
+       jz      L_Re_normalise
 
 LTruncate_53:
        movb    LOST_DOWN,FPU_bits_lost
        andl    $0xfffff800,%ebx        /* Truncate to 53 bits */
-       jmp     LRe_normalise
+       jmp     L_Re_normalise
 
 
 /* Round etc to 64 bit precision */
@@ -341,23 +338,23 @@ LRound_To_64:
 
 #ifdef PARANOID
        jmp     L_bugged_round64
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
 LUp_64:
-       cmpb    SIGN_POS,SIGN(%edi)
+       cmpb    SIGN_POS,PARAM5
        jne     LCheck_truncate_64      /* If negative then  up==truncate */
 
        orl     %edx,%edx
        jnz     LDo_64_round_up
-       jmp     LRe_normalise
+       jmp     L_Re_normalise
 
 LDown_64:
-       cmpb    SIGN_POS,SIGN(%edi)
+       cmpb    SIGN_POS,PARAM5
        je      LCheck_truncate_64      /* If positive then  down==truncate */
 
        orl     %edx,%edx
        jnz     LDo_64_round_up
-       jmp     LRe_normalise
+       jmp     L_Re_normalise
 
 LRound_nearest_64:
        cmpl    $0x80000000,%edx
@@ -366,7 +363,7 @@ LRound_nearest_64:
        jne     LDo_64_round_up
 
        /* Now test for round-to-even */
-       testb   $1,%ebx
+       testb   $1,%bl
        jz      LCheck_truncate_64
 
 LDo_64_round_up:
@@ -375,49 +372,63 @@ LDo_64_round_up:
        adcl    $0,%eax
 
 LCheck_Round_Overflow:
-       jnc     LRe_normalise
+       jnc     L_Re_normalise
 
        /* Overflow, adjust the result (significand to 1.0) */
        rcrl    $1,%eax
        rcrl    $1,%ebx
-       incl    EXP(%edi)
-       jmp     LRe_normalise
+       incw    EXP(%edi)
+       jmp     L_Re_normalise
 
 LCheck_truncate_64:
        orl     %edx,%edx
-       jz      LRe_normalise
+       jz      L_Re_normalise
 
 LTruncate_64:
        movb    LOST_DOWN,FPU_bits_lost
 
-LRe_normalise:
+L_Re_normalise:
        testb   $0xff,FPU_denormal
-       jnz     xNormalise_result
+       jnz     Normalise_result
+
+L_Normalised:
+       movl    TAG_Valid,%edx
 
-xL_Normalised:
+L_deNormalised:
        cmpb    LOST_UP,FPU_bits_lost
-       je      xL_precision_lost_up
+       je      L_precision_lost_up
 
        cmpb    LOST_DOWN,FPU_bits_lost
-       je      xL_precision_lost_down
+       je      L_precision_lost_down
 
-xL_no_precision_loss:
+L_no_precision_loss:
        /* store the result */
-       movb    TW_Valid,TAG(%edi)
 
-xL_Store_significand:
+L_Store_significand:
        movl    %eax,SIGH(%edi)
        movl    %ebx,SIGL(%edi)
 
-       xorl    %eax,%eax       /* No errors detected. */
-
-       cmpl    EXP_OVER,EXP(%edi)
+       cmpw    EXP_OVER,EXP(%edi)
        jge     L_overflow
 
-fpu_reg_round_exit:
+       movl    %edx,%eax
+
+       /* Convert the exponent to 80x87 form. */
+       addw    EXTENDED_Ebias,EXP(%edi)
+       andw    $0x7fff,EXP(%edi)
+
+fpu_reg_round_signed_special_exit:
+
+       cmpb    SIGN_POS,PARAM5
+       je      fpu_reg_round_special_exit
+
+       orw     $0x8000,EXP(%edi)       /* Negative sign for the result. */
+
+fpu_reg_round_special_exit:
+
 #ifndef NON_REENTRANT_FPU
        popl    %ebx            /* adjust the stack pointer */
-#endif /* NON_REENTRANT_FPU */
+#endif /* NON_REENTRANT_FPU */ 
 
 fpu_Arith_exit:
        popl    %ebx
@@ -431,21 +442,25 @@ fpu_Arith_exit:
  * Set the FPU status flags to represent precision loss due to
  * round-up.
  */
-xL_precision_lost_up:
+L_precision_lost_up:
+       push    %edx
        push    %eax
        call    SYMBOL_NAME(set_precision_flag_up)
        popl    %eax
-       jmp     xL_no_precision_loss
+       popl    %edx
+       jmp     L_no_precision_loss
 
 /*
  * Set the FPU status flags to represent precision loss due to
  * truncation.
  */
-xL_precision_lost_down:
+L_precision_lost_down:
+       push    %edx
        push    %eax
        call    SYMBOL_NAME(set_precision_flag_down)
        popl    %eax
-       jmp     xL_no_precision_loss
+       popl    %edx
+       jmp     L_no_precision_loss
 
 
 /*
@@ -453,30 +468,30 @@ xL_precision_lost_down:
  * Shift the number right the required number of bits, which will
  * have to be undone later...
  */
-xMake_denorm:
+L_Make_denorm:
        /* The action to be taken depends upon whether the underflow
           exception is masked */
        testb   CW_Underflow,%cl                /* Underflow mask. */
-       jz      xUnmasked_underflow             /* Do not make a denormal. */
+       jz      Unmasked_underflow              /* Do not make a denormal. */
 
        movb    DENORMAL,FPU_denormal
 
        pushl   %ecx            /* Save */
-       movl    EXP_UNDER+1,%ecx
-       subl    EXP(%edi),%ecx
+       movw    EXP_UNDER+1,%cx
+       subw    EXP(%edi),%cx
 
-       cmpl    $64,%ecx        /* shrd only works for 0..31 bits */
-       jnc     xDenorm_shift_more_than_63
+       cmpw    $64,%cx /* shrd only works for 0..31 bits */
+       jnc     Denorm_shift_more_than_63
 
-       cmpl    $32,%ecx        /* shrd only works for 0..31 bits */
-       jnc     xDenorm_shift_more_than_32
+       cmpw    $32,%cx /* shrd only works for 0..31 bits */
+       jnc     Denorm_shift_more_than_32
 
 /*
  * We got here without jumps by assuming that the most common requirement
  *   is for a small de-normalising shift.
  * Shift by [1..31] bits
  */
-       addl    %ecx,EXP(%edi)
+       addw    %cx,EXP(%edi)
        orl     %edx,%edx       /* extension */
        setne   %ch             /* Save whether %edx is non-zero */
        xorl    %edx,%edx
@@ -485,11 +500,11 @@ xMake_denorm:
        shr     %cl,%eax
        orb     %ch,%dl
        popl    %ecx
-       jmp     xDenorm_done
+       jmp     Denorm_done
 
 /* Shift by [32..63] bits */
-xDenorm_shift_more_than_32:
-       addl    %ecx,EXP(%edi)
+Denorm_shift_more_than_32:
+       addw    %cx,EXP(%edi)
        subb    $32,%cl
        orl     %edx,%edx
        setne   %ch
@@ -506,15 +521,15 @@ xDenorm_shift_more_than_32:
        movl    %eax,%ebx
        xorl    %eax,%eax
        popl    %ecx
-       jmp     xDenorm_done
+       jmp     Denorm_done
 
 /* Shift by [64..) bits */
-xDenorm_shift_more_than_63:
-       cmpl    $64,%ecx
-       jne     xDenorm_shift_more_than_64
+Denorm_shift_more_than_63:
+       cmpw    $64,%cx
+       jne     Denorm_shift_more_than_64
 
 /* Exactly 64 bit shift */
-       addl    %ecx,EXP(%edi)
+       addw    %cx,EXP(%edi)
        xorl    %ecx,%ecx
        orl     %edx,%edx
        setne   %cl
@@ -526,32 +541,32 @@ xDenorm_shift_more_than_63:
        xorl    %eax,%eax
        xorl    %ebx,%ebx
        popl    %ecx
-       jmp     xDenorm_done
+       jmp     Denorm_done
 
-xDenorm_shift_more_than_64:
-       movl    EXP_UNDER+1,EXP(%edi)
+Denorm_shift_more_than_64:
+       movw    EXP_UNDER+1,EXP(%edi)
 /* This is easy, %eax must be non-zero, so.. */
        movl    $1,%edx
        xorl    %eax,%eax
        xorl    %ebx,%ebx
        popl    %ecx
-       jmp     xDenorm_done
+       jmp     Denorm_done
 
 
-xUnmasked_underflow:
+Unmasked_underflow:
        movb    UNMASKED_UNDERFLOW,FPU_denormal
-       jmp     xDenorm_done
+       jmp     Denorm_done
 
 
 /* Undo the de-normalisation. */
-xNormalise_result:
+Normalise_result:
        cmpb    UNMASKED_UNDERFLOW,FPU_denormal
-       je      xSignal_underflow
+       je      Signal_underflow
 
 /* The number must be a denormal if we got here. */
 #ifdef PARANOID
        /* But check it... just in case. */
-       cmpl    EXP_UNDER+1,EXP(%edi)
+       cmpw    EXP_UNDER+1,EXP(%edi)
        jne     L_norm_bugged
 #endif /* PARANOID */
 
@@ -565,41 +580,33 @@ xNormalise_result:
         * Actual 80486 behaviour differs from this in some circumstances.
         */
        orl     %eax,%eax               /* ms bits */
-       js      LNormalise_shift_done   /* Will be masked underflow */
-#endif /* PECULIAR_486 */
-
+       js      LPseudoDenormal         /* Will be masked underflow */
+#else
        orl     %eax,%eax               /* ms bits */
-       js      xL_Normalised           /* No longer a denormal */
+       js      L_Normalised            /* No longer a denormal */
+#endif /* PECULIAR_486 */ 
 
-       jnz     LNormalise_shift_up_to_31       /* Shift left 0 - 31 bits */
+       jnz     LDenormal_adj_exponent
 
        orl     %ebx,%ebx
        jz      L_underflow_to_zero     /* The contents are zero */
 
-/* Shift left 32 - 63 bits */
-       movl    %ebx,%eax
-       xorl    %ebx,%ebx
-       subl    $32,EXP(%edi)
-
-LNormalise_shift_up_to_31:
-       bsrl    %eax,%ecx       /* get the required shift in %ecx */
-       subl    $31,%ecx
-       negl    %ecx
-       shld    %cl,%ebx,%eax
-       shl     %cl,%ebx
-       subl    %ecx,EXP(%edi)
+LDenormal_adj_exponent:
+       decw    EXP(%edi)
 
-LNormalise_shift_done:
+LPseudoDenormal:
        testb   $0xff,FPU_bits_lost     /* bits lost == underflow */
-       jz      xL_Normalised
+       movl    TAG_Special,%edx
+       jz      L_deNormalised
 
        /* There must be a masked underflow */
        push    %eax
        pushl   EX_Underflow
-       call    SYMBOL_NAME(exception)
+       call    EXCEPTION
        popl    %eax
        popl    %eax
-       jmp     xL_Normalised
+       movl    TAG_Special,%edx
+       jmp     L_deNormalised
 
 
 /*
@@ -613,41 +620,50 @@ L_underflow_to_zero:
 
        push    %eax
        pushl   EX_Underflow
-       call    SYMBOL_NAME(exception)
+       call    EXCEPTION
        popl    %eax
        popl    %eax
 
 /* Reduce the exponent to EXP_UNDER */
-       movl    EXP_UNDER,EXP(%edi)
-       movb    TW_Zero,TAG(%edi)
-       jmp     xL_Store_significand
+       movw    EXP_UNDER,EXP(%edi)
+       movl    TAG_Zero,%edx
+       jmp     L_Store_significand
 
 
 /* The operations resulted in a number too large to represent. */
 L_overflow:
+       addw    EXTENDED_Ebias,EXP(%edi)        /* Set for unmasked response. */
+       cmpb    SIGN_POS,PARAM5
+       jne     L_ovfl_neg
+       push    $0
+       jmp     L_ovfl_signed
+L_ovfl_neg:
+       push    $1
+L_ovfl_signed:
        push    %edi
        call    SYMBOL_NAME(arith_overflow)
        pop     %edi
-       jmp     fpu_reg_round_exit
+       addl    $4,%esp
+       jmp     fpu_reg_round_signed_special_exit
 
 
-xSignal_underflow:
+Signal_underflow:
        /* The number may have been changed to a non-denormal */
        /* by the rounding operations. */
-       cmpl    EXP_UNDER,EXP(%edi)
-       jle     xDo_unmasked_underflow
+       cmpw    EXP_UNDER,EXP(%edi)
+       jle     Do_unmasked_underflow
 
-       jmp     xL_Normalised
+       jmp     L_Normalised
 
-xDo_unmasked_underflow:
+Do_unmasked_underflow:
        /* Increase the exponent by the magic number */
-       addl    $(3*(1<<13)),EXP(%edi)
+       addw    $(3*(1<<13)),EXP(%edi)
        push    %eax
        pushl   EX_Underflow
        call    EXCEPTION
        popl    %eax
        popl    %eax
-       jmp     xL_Normalised
+       jmp     L_Normalised
 
 
 #ifdef PARANOID
@@ -663,7 +679,7 @@ L_bugged_denorm:
        call    EXCEPTION
        popl    %ebx
        jmp     L_exception_exit
-#endif /* PECULIAR_486 */
+#endif /* PECULIAR_486 */ 
 
 L_bugged_round24:
        pushl   EX_INTERNAL|0x231
@@ -694,6 +710,6 @@ L_entry_bugged:
        call    EXCEPTION
        popl    %ebx
 L_exception_exit:
-       mov     $1,%eax
-       jmp     fpu_reg_round_exit
-#endif /* PARANOID */
+       mov     $-1,%eax
+       jmp     fpu_reg_round_special_exit
+#endif /* PARANOID */ 
index 77e7b39fa98b3e4e046b5413b0470af005fb8f16..47c4c2434d85efd889d60b809def705f81964948 100644 (file)
@@ -2,24 +2,26 @@
 /*---------------------------------------------------------------------------+
  |  reg_u_add.S                                                              |
  |                                                                           |
- | Add two valid (TW_Valid) FPU_REG numbers, of the same sign, and put the   |
+ | Add two valid (TAG_Valid) FPU_REG numbers, of the same sign, and put the  |
  |   result in a destination FPU_REG.                                        |
  |                                                                           |
- | Copyright (C) 1992,1993,1995                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@jacobi.maths.monash.edu.au |
+ | Copyright (C) 1992,1993,1995,1997                                         |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
  | Call from C as:                                                           |
- |   void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,             |
+ |   int  FPU_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,             |
  |                                                int control_w)             |
+ |    Return value is the tag of the answer, or-ed with FPU_Exception if     |
+ |    one was raised, or -1 on internal error.                               |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
 /*
- |    Kernel addition routine reg_u_add(reg *arg1, reg *arg2, reg *answ).
- |    Takes two valid reg f.p. numbers (TW_Valid), which are
+ |    Kernel addition routine FPU_u_add(reg *arg1, reg *arg2, reg *answ).
+ |    Takes two valid reg f.p. numbers (TAG_Valid), which are
  |    treated as unsigned numbers,
- |    and returns their sum as a TW_Valid or TW_S f.p. number.
+ |    and returns their sum as a TAG_Valid or TAG_Special f.p. number.
  |    The returned number is normalized.
  |    Basic checks are performed if PARANOID is defined.
  */
@@ -29,7 +31,7 @@
 #include "control_w.h"
 
 .text
-ENTRY(reg_u_add)
+ENTRY(FPU_u_add)
        pushl   %ebp
        movl    %esp,%ebp
        pushl   %esi
@@ -39,27 +41,9 @@ ENTRY(reg_u_add)
        movl    PARAM1,%esi             /* source 1 */
        movl    PARAM2,%edi             /* source 2 */
 
-#ifdef DENORM_OPERAND
-       cmpl    EXP_UNDER,EXP(%esi)
-       jg      xOp1_not_denorm
-
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
-
-xOp1_not_denorm:
-       cmpl    EXP_UNDER,EXP(%edi)
-       jg      xOp2_not_denorm
-
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
-
-xOp2_not_denorm:
-#endif /* DENORM_OPERAND */
-
-       movl    EXP(%esi),%ecx
-       subl    EXP(%edi),%ecx          /* exp1 - exp2 */
+       movl    PARAM6,%ecx
+       movl    %ecx,%edx
+       subl    PARAM7,%ecx                     /* exp1 - exp2 */
        jge     L_arg1_larger
 
        /* num1 is smaller */
@@ -67,6 +51,7 @@ xOp2_not_denorm:
        movl    SIGH(%esi),%eax
 
        movl    %edi,%esi
+       movl    PARAM7,%edx
        negw    %cx
        jmp     L_accum_loaded
 
@@ -77,12 +62,7 @@ L_arg1_larger:
 
 L_accum_loaded:
        movl    PARAM3,%edi             /* destination */
-/*     movb    SIGN(%esi),%dl
-       movb    %dl,SIGN(%edi) */       /* Copy the sign from the first arg */
-
-
-       movl    EXP(%esi),%edx
-       movl    %edx,EXP(%edi)          /* Copy exponent to destination */
+       movw    %dx,EXP(%edi)           /* Copy exponent to destination */
 
        xorl    %edx,%edx               /* clear the extension */
 
@@ -162,7 +142,7 @@ L_shift_done:
        orl     $1,%edx
 
 L_no_bit_lost:
-       incl    EXP(%edi)
+       incw    EXP(%edi)
 
 L_round_the_result:
        jmp     fpu_reg_round   /* Round the result */
@@ -175,9 +155,8 @@ L_bugged:
        pushl   EX_INTERNAL|0x201
        call    EXCEPTION
        pop     %ebx
+       movl    $-1,%eax
        jmp     L_exit
-#endif /* PARANOID */
-
 
 L_exit:
        popl    %ebx
@@ -185,3 +164,4 @@ L_exit:
        popl    %esi
        leave
        ret
+#endif /* PARANOID */
index 4599040c25b3c1286c6d429f265802f1f3be1291..cc00654b6f9ada84e99f6eda6c35091b47a7854a 100644 (file)
@@ -2,22 +2,24 @@
 /*---------------------------------------------------------------------------+
  |  reg_u_div.S                                                              |
  |                                                                           |
- | Core division routines                                                    |
+ | Divide one FPU_REG by another and put the result in a destination FPU_REG.|
  |                                                                           |
- | Copyright (C) 1992,1993,1995                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@jacobi.maths.monash.edu.au |
+ | Copyright (C) 1992,1993,1995,1997                                         |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
 /*---------------------------------------------------------------------------+
- |  Kernel for the division routines.                                        |
- |                                                                           |
- |  void reg_u_div(FPU_REG *a, FPU_REG *a,                                   |
- |                 FPU_REG *dest, unsigned int control_word)                 |
+ | Call from C as:                                                           |
+ |    int FPU_u_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest,                   |
+ |                unsigned int control_word, char *sign)                     |
  |                                                                           |
  |  Does not compute the destination exponent, but does adjust it.           |
+ |                                                                           |
+ |    Return value is the tag of the answer, or-ed with FPU_Exception if     |
+ |    one was raised, or -1 on internal error.                               |
  +---------------------------------------------------------------------------*/
 
 #include "exception.h"
@@ -50,7 +52,7 @@
        Result:         FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
        Overflow flag:  ovfl_flag
  */
-       .align 2,0
+       .align 4,0
 FPU_accum_3:
        .long   0
 FPU_accum_2:
@@ -67,9 +69,12 @@ FPU_ovfl_flag:
        .byte   0
 #endif /* NON_REENTRANT_FPU */
 
+#define REGA   PARAM1
+#define REGB   PARAM2
+#define DEST   PARAM3
 
 .text
-ENTRY(reg_u_div)
+ENTRY(FPU_u_div)
        pushl   %ebp
        movl    %esp,%ebp
 #ifndef NON_REENTRANT_FPU
@@ -80,38 +85,32 @@ ENTRY(reg_u_div)
        pushl   %edi
        pushl   %ebx
 
-       movl    PARAM1,%esi     /* pointer to num */
-       movl    PARAM2,%ebx     /* pointer to denom */
-       movl    PARAM3,%edi     /* pointer to answer */
+       movl    REGA,%esi
+       movl    REGB,%ebx
+       movl    DEST,%edi
 
-#ifdef DENORM_OPERAND
-       movl    EXP(%esi),%eax
-       cmpl    EXP_UNDER,%eax
-       jg      xOp1_not_denorm
+       movswl  EXP(%esi),%edx
+       movswl  EXP(%ebx),%eax
+       subl    %eax,%edx
+       addl    EXP_BIAS,%edx
 
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
+       /* A denormal and a large number can cause an exponent underflow */
+       cmpl    EXP_WAY_UNDER,%edx
+       jg      xExp_not_underflow
 
-xOp1_not_denorm:
-       movl    EXP(%ebx),%eax
-       cmpl    EXP_UNDER,%eax
-       jg      xOp2_not_denorm
+       /* Set to a really low value allow correct handling */
+       movl    EXP_WAY_UNDER,%edx
 
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
+xExp_not_underflow:
 
-xOp2_not_denorm:
-#endif /* DENORM_OPERAND */
+       movw    %dx,EXP(%edi)
 
-ENTRY(divide_kernel)
 #ifdef PARANOID
 /*     testl   $0x80000000, SIGH(%esi) // Dividend */
 /*     je      L_bugged */
        testl   $0x80000000, SIGH(%ebx) /* Divisor */
        je      L_bugged
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
 /* Check if the divisor can be treated as having just 32 bits */
        cmpl    $0,SIGL(%ebx)
@@ -147,7 +146,7 @@ L_no_adjust:
 
        /* Do the shifting here */
        /* increase the exponent */
-       incl    EXP(%edi)
+       incw    EXP(%edi)
 
        /* shift the mantissa right one bit */
        stc                     /* To set the ms bit */
@@ -247,7 +246,7 @@ LFirst_div_done:
 
 #ifdef PARANOID
        jb      L_bugged_1
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
        /* need to subtract another once of the denom */
        incl    FPU_result_2    /* Correct the answer */
@@ -260,7 +259,7 @@ LFirst_div_done:
 #ifdef PARANOID
        sbbl    $0,FPU_accum_3
        jne     L_bugged_1      /* Must check for non-zero result here */
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
 /*----------------------------------------------------------------------*/
 /* Half of the main problem is done, there is just a reduced numerator
@@ -290,7 +289,7 @@ LPrevent_2nd_overflow:
 
 #ifdef PARANOID
        je      L_bugged_2      /* Can't bump the result to 1.0 */
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
 LDo_2nd_div:
        cmpl    $0,%ecx         /* augmented denom msw */
@@ -313,7 +312,7 @@ LSecond_div_done:
 
 #ifdef PARANOID
        jc      L_bugged_2
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
        movl    FPU_result_1,%eax       /* Get the result back */
        mull    SIGL(%ebx)      /* now mul the ls dw of the denom */
@@ -324,7 +323,7 @@ LSecond_div_done:
 
 #ifdef PARANOID
        jc      L_bugged_2
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
        jz      LDo_3rd_32_bits
 
@@ -343,7 +342,7 @@ LSecond_div_done:
 #ifdef PARANOID
        jc      L_bugged_2
        jne     L_bugged_2
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
        addl    $1,FPU_result_1 /* Correct the answer */
        adcl    $0,FPU_result_2
@@ -423,7 +422,7 @@ LRound_ovfl:
        testb   $255,FPU_ovfl_flag      /* was the num > denom ? */
        je      LRound_precision
 
-       incl    EXP(%edi)
+       incw    EXP(%edi)
 
        /* shift the mantissa right one bit */
        stc                     /* Will set the ms bit */
@@ -433,7 +432,7 @@ LRound_ovfl:
 
 /* Round the result as required */
 LRound_precision:
-       decl    EXP(%edi)       /* binary point between 1st & 2nd bits */
+       decw    EXP(%edi)       /* binary point between 1st & 2nd bits */
 
        movl    %eax,%edx
        movl    FPU_result_1,%ebx
@@ -462,10 +461,11 @@ L_bugged_2:
        jmp     L_exit
 
 L_exit:
+       movl    $-1,%eax
        popl    %ebx
        popl    %edi
        popl    %esi
 
        leave
        ret
-#endif /* PARANOID */
+#endif /* PARANOID */ 
index 598e0cf54de1b82ccb82201a563bffb5f56c498c..973f12af97df55a837eae72484aeffbabc08d704 100644 (file)
@@ -4,9 +4,9 @@
  |                                                                           |
  | Core multiplication routine                                               |
  |                                                                           |
- | Copyright (C) 1992,1993,1995                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@jacobi.maths.monash.edu.au |
+ | Copyright (C) 1992,1993,1995,1997                                         |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -15,7 +15,7 @@
  |   Basic multiplication routine.                                           |
  |   Does not check the resulting exponent for overflow/underflow            |
  |                                                                           |
- |   reg_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw);         |
+ |   FPU_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw);         |
  |                                                                           |
  |   Internal working is at approx 128 bits.                                 |
  |   Result is rounded to nearest 53 or 64 bits, using "nearest or even".    |
@@ -44,12 +44,12 @@ FPU_accum_1:
 
 
 .text
-ENTRY(reg_u_mul)
+ENTRY(FPU_u_mul)
        pushl   %ebp
        movl    %esp,%ebp
 #ifndef NON_REENTRANT_FPU
        subl    $8,%esp
-#endif /* NON_REENTRANT_FPU */
+#endif /* NON_REENTRANT_FPU */ 
 
        pushl   %esi
        pushl   %edi
@@ -65,27 +65,6 @@ ENTRY(reg_u_mul)
        jz      L_bugged
 #endif /* PARANOID */
 
-#ifdef DENORM_OPERAND
-       movl    EXP(%esi),%eax
-       cmpl    EXP_UNDER,%eax
-       jg      xOp1_not_denorm
-
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
-
-xOp1_not_denorm:
-       movl    EXP(%edi),%eax
-       cmpl    EXP_UNDER,%eax
-       jg      xOp2_not_denorm
-
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
-
-xOp2_not_denorm:
-#endif /* DENORM_OPERAND */
-
        xorl    %ecx,%ecx
        xorl    %ebx,%ebx
 
@@ -111,13 +90,22 @@ xOp2_not_denorm:
        addl    %eax,%ebx
        adcl    %edx,%ecx
 
-       movl    EXP(%esi),%eax  /* Compute the exponent */
-       addl    EXP(%edi),%eax
+       /* Get the sum of the exponents. */
+       movl    PARAM6,%eax
        subl    EXP_BIAS-1,%eax
 
+       /* Two denormals can cause an exponent underflow */
+       cmpl    EXP_WAY_UNDER,%eax
+       jg      Exp_not_underflow
+
+       /* Set to a really low value allow correct handling */
+       movl    EXP_WAY_UNDER,%eax
+
+Exp_not_underflow:
+
 /*  Have now finished with the sources */
        movl    PARAM3,%edi     /* Point to the destination */
-       movl    %eax,EXP(%edi)
+       movw    %ax,EXP(%edi)
 
 /*  Now make sure that the result is normalized */
        testl   $0x80000000,%ecx
@@ -128,7 +116,7 @@ xOp2_not_denorm:
        rcll    $1,FPU_accum_1
        rcll    $1,%ebx
        rcll    $1,%ecx
-       decl    EXP(%edi)
+       decw    EXP(%edi)
 
 LResult_Normalised:
        movl    FPU_accum_0,%eax
@@ -156,5 +144,5 @@ L_exit:
        popl    %esi
        leave
        ret
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
index 114316f7835e3eaf0755699c57a35af8ad98f303..1b6c24801d2231bde8d8149db2c08e781086ffcf 100644 (file)
@@ -4,21 +4,23 @@
  |                                                                           |
  | Core floating point subtraction routine.                                  |
  |                                                                           |
- | Copyright (C) 1992,1993,1995                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@jacobi.maths.monash.edu.au |
+ | Copyright (C) 1992,1993,1995,1997                                         |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@suburbia.net                              |
  |                                                                           |
  | Call from C as:                                                           |
- |   void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,             |
+ |    int FPU_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,             |
  |                                                int control_w)             |
+ |    Return value is the tag of the answer, or-ed with FPU_Exception if     |
+ |    one was raised, or -1 on internal error.                               |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
 /*
- |    Kernel subtraction routine reg_u_sub(reg *arg1, reg *arg2, reg *answ).
- |    Takes two valid reg f.p. numbers (TW_Valid), which are
+ |    Kernel subtraction routine FPU_u_sub(reg *arg1, reg *arg2, reg *answ).
+ |    Takes two valid reg f.p. numbers (TAG_Valid), which are
  |    treated as unsigned numbers,
- |    and returns their difference as a TW_Valid or TW_Zero f.p.
+ |    and returns their difference as a TAG_Valid or TAG_Zero f.p.
  |    number.
  |    The first number (arg1) must be the larger.
  |    The returned number is normalized.
@@ -30,7 +32,7 @@
 #include "control_w.h"
 
 .text
-ENTRY(reg_u_sub)
+ENTRY(FPU_u_sub)
        pushl   %ebp
        movl    %esp,%ebp
        pushl   %esi
@@ -39,34 +41,15 @@ ENTRY(reg_u_sub)
 
        movl    PARAM1,%esi     /* source 1 */
        movl    PARAM2,%edi     /* source 2 */
-
-#ifdef DENORM_OPERAND
-       cmpl    EXP_UNDER,EXP(%esi)
-       jg      xOp1_not_denorm
-
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
-
-xOp1_not_denorm:
-       cmpl    EXP_UNDER,EXP(%edi)
-       jg      xOp2_not_denorm
-
-       call    SYMBOL_NAME(denormal_operand)
-       orl     %eax,%eax
-       jnz     fpu_Arith_exit
-
-xOp2_not_denorm:
-#endif /* DENORM_OPERAND */
-
-       movl    EXP(%esi),%ecx
-       subl    EXP(%edi),%ecx  /* exp1 - exp2 */
+       
+       movl    PARAM6,%ecx
+       subl    PARAM7,%ecx     /* exp1 - exp2 */
 
 #ifdef PARANOID
        /* source 2 is always smaller than source 1 */
        js      L_bugged_1
 
-       testl   $0x80000000,SIGH(%edi)  /* The args are assumed to be normalized */
+       testl   $0x80000000,SIGH(%edi)  /* The args are assumed to be be normalized */
        je      L_bugged_2
 
        testl   $0x80000000,SIGH(%esi)
@@ -81,10 +64,8 @@ xOp2_not_denorm:
        movl    SIGL(%edi),%ebx /* register ls word */
 
        movl    PARAM3,%edi     /* destination */
-       movl    EXP(%esi),%edx
-       movl    %edx,EXP(%edi)  /* Copy exponent to destination */
-/*     movb    SIGN(%esi),%dl
-       movb    %dl,SIGN(%edi) */       /* Copy the sign from the first arg */
+       movl    PARAM6,%edx
+       movw    %dx,EXP(%edi)   /* Copy exponent to destination */
 
        xorl    %edx,%edx       /* register extension */
 
@@ -93,8 +74,8 @@ xOp2_not_denorm:
  |      right the required number of   |
  |     places.                         |
  +--------------------------------------*/
-L_shift_r:
-       cmpl    $32,%ecx                /* shrd only works for 0..31 bits */
+
+       cmpw    $32,%cx         /* shrd only works for 0..31 bits */
        jnc     L_more_than_31
 
 /* less than 32 bits */
@@ -104,7 +85,7 @@ L_shift_r:
        jmp     L_shift_done
 
 L_more_than_31:
-       cmpl    $64,%ecx
+       cmpw    $64,%cx
        jnc     L_more_than_63
 
        subb    $32,%cl
@@ -210,7 +191,7 @@ L_subtr:
        jnz     L_must_be_zero
 
        /* Shift left 64 bits */
-       subl    $64,EXP(%edi)
+       subw    $64,EXP(%edi)
        xchg    %edx,%eax
        jmp     fpu_reg_round
 
@@ -218,20 +199,20 @@ L_must_be_zero:
 #ifdef PARANOID
        orl     %edx,%edx
        jnz     L_bugged_3
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
        /* The result is zero */
-       movb    TW_Zero,TAG(%edi)
-       movl    $0,EXP(%edi)            /* exponent */
+       movw    $0,EXP(%edi)            /* exponent */
        movl    $0,SIGL(%edi)
        movl    $0,SIGH(%edi)
-       jmp     L_exit          /* %eax contains zero */
+       movl    TAG_Zero,%eax
+       jmp     L_exit
 
 L_shift_32:
        movl    %ebx,%eax
        movl    %edx,%ebx
        movl    $0,%edx
-       subl    $32,EXP(%edi)   /* Can get underflow here */
+       subw    $32,EXP(%edi)   /* Can get underflow here */
 
 /* We need to shift left by 1 - 31 bits */
 L_shift_1:
@@ -241,7 +222,7 @@ L_shift_1:
        shld    %cl,%ebx,%eax
        shld    %cl,%edx,%ebx
        shl     %cl,%edx
-       subl    %ecx,EXP(%edi)  /* Can get underflow here */
+       subw    %cx,EXP(%edi)   /* Can get underflow here */
 
 L_round:
        jmp     fpu_reg_round   /* Round the result */
@@ -277,11 +258,12 @@ L_bugged:
        call    EXCEPTION
        pop     %ebx
        jmp     L_error_exit
-#endif /* PARANOID */
-
 
 L_error_exit:
-       movl    $1,%eax
+       movl    $-1,%eax
+
+#endif /* PARANOID */
+
 L_exit:
        popl    %ebx
        popl    %edi
index 78d7b7689dd6b414ae6f25fb7ca61bb3c1c91028..7326861f120ad3c8e61097bc603ab163cd061c24 100644 (file)
@@ -1,9 +1,9 @@
 /*---------------------------------------------------------------------------+
  |  status_w.h                                                               |
  |                                                                           |
- | Copyright (C) 1992,1993                                                   |
+ | Copyright (C) 1992,1993,2001                                              |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
 #define        Const__(x)      x
 #endif
 
+#define CFLAG   1
+#define PFLAG   4
+#define ZFLAG   0x40
+#define SFLAGS (CFLAG | PFLAG | ZFLAG)
+
 #define SW_Backward            Const__(0x8000) /* backward compatibility */
 #define SW_C3          Const__(0x4000) /* condition bit 3 */
 #define SW_Top         Const__(0x3800) /* top of stack */
 #define COMP_SNaN      0x80
 
 #define status_word() \
-  ((partial_status & ~SW_Top & 0xffff) | ((top << SW_Top_Shift) & SW_Top))
+  ((partial_status & ~SW_Top & 0xffff) | ((FPU_top << SW_Top_Shift) & SW_Top))
 #define setcc(cc) ({ \
   partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
   partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
 
+#define setccf(ftype,cc) { if ( ftype ) setflags((cc) >> 8); else setcc(cc); }
+
 #ifdef PECULIAR_486
    /* Default, this conveys no information, but an 80486 does it. */
    /* Clear the SW_C1 bit, "other bits undefined". */
index d966ab19350d0ea6e16aea6c769c937c981bd631..807e1bc47bfd3efbb98b20667cb8dab75ddc70e7 100644 (file)
@@ -2,11 +2,11 @@
  |  version.h                                                                |
  |                                                                           |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1996                                         |
+ | Copyright (C) 1992,1993,1994,1996,1997,1999,2001                          |
  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
- |                  E-mail   billm@jacobi.maths.monash.edu.au                |
+ |                  E-mail   billm@melbpc.org.au                             |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
-#define FPU_VERSION "wm-FPU-emu version 1.22"
+#define FPU_VERSION "wm-FPU-emu version 2.11"
index 1ea7ff7da1963cedbc52c4369f28cff8df31b5be..51842831798599018d62aa13e8553ac559a73827 100644 (file)
@@ -9,9 +9,9 @@
  |                       Australia.  E-mail billm@jacobi.maths.monash.edu.au |
  |                                                                           |
  | Call from C as:                                                           |
- |   unsigned shrx(void *arg1, unsigned arg2)                                |
+ |   unsigned FPU_shrx(void *arg1, unsigned arg2)                            |
  | and                                                                       |
- |   unsigned shrxs(void *arg1, unsigned arg2)                               |
+ |   unsigned FPU_shrxs(void *arg1, unsigned arg2)                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
@@ -19,7 +19,7 @@
 
 .text
 /*---------------------------------------------------------------------------+
- |   unsigned shrx(void *arg1, unsigned arg2)                                |
+ |   unsigned FPU_shrx(void *arg1, unsigned arg2)                            |
  |                                                                           |
  |   Extended shift right function.                                          |
  |   Fastest for small shifts.                                               |
@@ -32,7 +32,7 @@
  |   Results returned in the 64 bit arg and eax.                             |
  +---------------------------------------------------------------------------*/
 
-ENTRY(shrx)
+ENTRY(FPU_shrx)
        push    %ebp
        movl    %esp,%ebp
        pushl   %esi
@@ -95,7 +95,7 @@ L_more_than_95:
 
 
 /*---------------------------------------------------------------------------+
- |   unsigned shrxs(void *arg1, unsigned arg2)                               |
+ |   unsigned FPU_shrxs(void *arg1, unsigned arg2)                           |
  |                                                                           |
  |   Extended shift right function (optimized for small floating point       |
  |   integers).                                                              |
@@ -110,7 +110,7 @@ L_more_than_95:
  |   part which has been shifted out of the arg.                             |
  |   Results returned in the 64 bit arg and eax.                             |
  +---------------------------------------------------------------------------*/
-ENTRY(shrxs)
+ENTRY(FPU_shrxs)
        push    %ebp
        movl    %esp,%ebp
        pushl   %esi
index 33611196bc0b20045982896d3d552ac376528b94..d258f59564e11b71a37148873bbf61b41d3c4b82 100644 (file)
@@ -4,12 +4,12 @@
  |                                                                           |
  | Fixed point arithmetic square root evaluation.                            |
  |                                                                           |
- | Copyright (C) 1992,1993,1995                                              |
+ | Copyright (C) 1992,1993,1995,1997                                         |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@jacobi.maths.monash.edu.au |
+ |                       Australia.  E-mail billm@suburbia.net               |
  |                                                                           |
  | Call from C as:                                                           |
- |   void wm_sqrt(FPU_REG *n, unsigned int control_word)                     |
+ |    int wm_sqrt(FPU_REG *n, unsigned int control_word)                     |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
@@ -70,7 +70,7 @@ FPU_fsqrt_arg_1:
        .long   0
 FPU_fsqrt_arg_0:
        .long   0               /* ls word, at most the ms bit is set */
-#endif /* NON_REENTRANT_FPU */
+#endif /* NON_REENTRANT_FPU */ 
 
 
 .text
@@ -92,7 +92,7 @@ ENTRY(wm_sqrt)
 
 /* We use a rough linear estimate for the first guess.. */
 
-       cmpl    EXP_BIAS,EXP(%esi)
+       cmpw    EXP_BIAS,EXP(%esi)
        jnz     sqrt_arg_ge_2
 
        shrl    $1,%eax                 /* arg is in the range  [1.0 .. 2.0) */
@@ -224,7 +224,7 @@ sqrt_stage_2_finish:
 sqrt_stage_2_error:
        pushl   EX_INTERNAL|0x213
        call    EXCEPTION
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
 sqrt_stage_2_done:
 
@@ -347,9 +347,8 @@ sqrt_round_result:
        movl    %esi,%eax
        movl    %edi,%ebx
        movl    PARAM1,%edi
-       movl    EXP_BIAS,EXP(%edi)      /* Result is in  [1.0 .. 2.0) */
-       movl    PARAM2,%ecx
-       jmp     fpu_reg_round_sqrt
+       movw    EXP_BIAS,EXP(%edi)      /* Result is in  [1.0 .. 2.0) */
+       jmp     fpu_reg_round
 
 
 sqrt_near_exact_x:
@@ -386,7 +385,7 @@ sqrt_near_exact:
        call    EXCEPTION
 
 sqrt_near_exact_ok:
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
        or      %ebx,%ebx
        js      sqrt_near_exact_small
@@ -446,7 +445,7 @@ sqrt_get_more_precision:
        call    EXCEPTION
 
 sqrt_more_prec_ok:
-#endif /* PARANOID */
+#endif /* PARANOID */ 
 
        or      %ebx,%ebx
        js      sqrt_more_prec_small
index 88058bfdbe141d0f85b60459762fa0cbe910fedc..30ec639f7ef023041f032a6ee6d461f0e7337e94 100644 (file)
@@ -178,4 +178,9 @@ bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
    int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
+bool 'Memory flooding' CONFIG_SADISTIC_KMALLOC
+bool 'Socket-buffer consistency checking' CONFIG_SKB_CHECK
+if [ "$CONFIG_SKB_CHECK" = "y" ]; then
+   bool '  Whole-queue checking' CONFIG_SKB_CHECK_WHOLE_QUEUE
+fi
 endmenu
index 9f33f7321f174c6c7a0bd6ba9a5b1e1f7453b446..55717673f2a3f030c65dd66fac1a092ebe084aec 100644 (file)
@@ -115,4 +115,9 @@ bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
    int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
+bool 'Memory flooding' CONFIG_SADISTIC_KMALLOC
+bool 'Socket-buffer consistency checking' CONFIG_SKB_CHECK
+if [ "$CONFIG_SKB_CHECK" = "y" ]; then
+   bool '  Whole-queue checking' CONFIG_SKB_CHECK_WHOLE_QUEUE
+fi
 endmenu
index 5d2e05cf638feb68a41598b6a65ee863bf2e5378..632d78bee93a38afedf825d2fa912e55479501f1 100644 (file)
@@ -104,4 +104,9 @@ bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
    int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
+bool 'Memory flooding' CONFIG_SADISTIC_KMALLOC
+bool 'Socket-buffer consistency checking' CONFIG_SKB_CHECK
+if [ "$CONFIG_SKB_CHECK" = "y" ]; then
+   bool '  Whole-queue checking' CONFIG_SKB_CHECK_WHOLE_QUEUE
+fi
 endmenu
index c5f8cdb8ce93e2f236d2389025f41d9e86720da1..04d16fdd0ede903b4dc19e40fba869b1aff60ce7 100644 (file)
@@ -111,3 +111,8 @@ bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
    int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
+bool 'Memory flooding' CONFIG_SADISTIC_KMALLOC
+bool 'Socket-buffer consistency checking' CONFIG_SKB_CHECK
+if [ "$CONFIG_SKB_CHECK" = "y" ]; then
+   bool '  Whole-queue checking' CONFIG_SKB_CHECK_WHOLE_QUEUE
+fi
index 82bc7b1ca3496c5e8ab4e80596076c9d6b817086..85b47b9d9a5053123522c5dfd84f88f8fc9c6d92 100644 (file)
 
     The idea of using the setup timeout to filter out bogus IRQs came from
     the serial driver.
+
+Update 2000-09-04, by Michael Deutschmann <michael@talamasca.ocis.net>:
+    
+    The code used to rely upon freeing an IRQ handler, within the same 
+    handler.  This is not correct -- it causes the kernel to free a 
+    structure that is still in use.  
+
+    Because the read-after-free takes place with interrupts off, it's
+    probably impossible for anything else to reallocate the memory and 
+    trash the structure -- but in SADISTIC_KMALLOC mode it will crash 
+    every time.
+
+    I've fixed the code so it doesn't do that.
+
 */
 
 
@@ -52,13 +66,14 @@ static volatile int irq_number;                     /* The latest irq number we actually found. */
 
 static void autoirq_probe(int irq, void *dev_id, struct pt_regs * regs)
 {
+       /* Only act on first instance of this IRQ.  We can't free the 
+        * handler from here, so we just make sure second trips do nothing.
+        */
+       if (test_bit(irq, (void *)&irq_bitmap)) 
+            return; 
+
        irq_number = irq;
        set_bit(irq, (void *)&irq_bitmap);      /* irq_bitmap |= 1 << irq; */
-       /* This code used to disable the irq. However, the interrupt stub
-        * would then re-enable the interrupt with (potentially) disastrous
-        * consequences
-        */
-       free_irq(irq, dev_id);
        return;
 }
 
@@ -102,8 +117,6 @@ int autoirq_report(int waittime)
                if (irq_number)
                        break;
 
-       irq_handled &= ~irq_bitmap;     /* This eliminates the already reset handlers */
-
        /* Retract the irq handlers that we installed. */
        for (i = 0; i < 16; i++) {
                if (test_bit(i, (void *)&irq_handled))
index 0d4d4b949df4af2405f268706b3fc5402e969845..48042b927f58ad4c68debd76a39b1a8e771ad832 100644 (file)
@@ -53,7 +53,9 @@
 #include "pci2000.h"
 #include "psi_roy.h"
 
-#include<linux/stat.h>
+#include "pci2220i.h"
+
+#include <linux/stat.h>
 
 struct proc_dir_entry Proc_Scsi_Pci2000 =
        { PROC_SCSI_PCI2000, 7, "pci2000", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
index d3dacea4127730c81d7a06114d1ef814fc925602..9c44d625ab1f11479bc9603ecf6d1495eb1b7f8b 100644 (file)
@@ -145,7 +145,7 @@ static int ext2_readdir (struct inode * inode, struct file * filp,
                                        brelse (bha[i]);
                        }
                }
-               
+
 revalidate:
                /* If the dir block has changed since the last call to
                 * readdir(2), then we might be pointing to an invalid
@@ -153,7 +153,7 @@ revalidate:
                 * to make sure. */
                if (filp->f_version != inode->i_version) {
                        for (i = 0; i < sb->s_blocksize && i < offset; ) {
-                               de = (struct ext2_dir_entry *) 
+                               de = (struct ext2_dir_entry_2 *)
                                        (bh->b_data + i);
                                /* It's too expensive to do a full
                                 * dirent test each time round this
@@ -170,8 +170,8 @@ revalidate:
                                | offset;
                        filp->f_version = inode->i_version;
                }
-               
-               while (!error && filp->f_pos < inode->i_size 
+
+               while (!error && filp->f_pos < inode->i_size
                       && offset < sb->s_blocksize) {
                        de = (struct ext2_dir_entry_2 *) (bh->b_data + offset);
                        if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
index 2e95d0a61d39aa5012fd10c90fe9e8f4e9255c9b..6e13f1c429dd6f993f15577ab48fce4f51c2bf49 100644 (file)
@@ -711,7 +711,6 @@ void cleanup_module(void)
 void ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
 {
        unsigned long overhead;
-       unsigned long overhead_per_group;
        struct statfs tmp;
        int ngroups, i;
 
index 92083a5d61acabbb397f1a849a6a4181e7c65b5e..90c26c6464f3f08e5276aa5b8c135260c67f7199 100644 (file)
@@ -3,17 +3,8 @@
 
 #include <asm/sigcontext.h>
 
-void restore_i387_soft(struct _fpstate *buf);
-struct _fpstate * save_i387_soft(struct _fpstate * buf);
-
-struct fpu_reg {
-       char sign;
-       char tag;
-       long exp;
-       unsigned sigl;
-       unsigned sigh;
-};
-
+int restore_i387_soft(void *s387, struct _fpstate *buf);
+int save_i387_soft(void *s387, struct _fpstate * buf);
 
 /* This structure matches the layout of the data saved to the stack
    following a device-not-present interrupt, part of it saved
@@ -45,13 +36,4 @@ struct info {
        long ___vm86_gs;
 };
 
-/* Interface for converting data between the emulator format
- * and the hardware format. Used for core dumping and for
- * ptrace(2) */
-void hardreg_to_softreg(const char hardreg[10],
-                             struct fpu_reg *soft_reg);
-
-void softreg_to_hardreg(const struct fpu_reg *rp, char d[10],
-                       long int control_word);
-
 #endif
index eee66a2ab1fa62bbe83bee722626b98c1c17350f..ea5073365b9c095372833b582923ce494f948655 100644 (file)
@@ -114,9 +114,8 @@ struct i387_soft_struct {
        long    fcs;
        long    foo;
        long    fos;
-       long    top;
-       struct fpu_reg  regs[8];        /* 8*16 bytes for each FP-reg = 128 bytes */
-       unsigned char   lookahead;
+       long    st_space[20];   /* 8*10 bytes for each FP-reg = 80 bytes */
+       unsigned char   ftop, changed, lookahead, no_update, rm, alimit;
        struct info     *info;
        unsigned long   entry_eip;
 };
index 7b77fbfbca646220bde30f8decec491da9c21bed..8547de4e12e05d8406eef425e14e54435a4410f8 100644 (file)
@@ -20,8 +20,6 @@
 #include <asm/atomic.h>
 #include <asm/types.h>
 
-#define CONFIG_SKB_CHECK 0
-
 #define HAVE_ALLOC_SKB         /* For the drivers to know */
 #define HAVE_ALIGNABLE_SKB     /* Ditto 8)                */
 
@@ -209,9 +207,24 @@ extern __inline__ __u32 skb_queue_len(struct sk_buff_head *list_)
 extern int                     skb_check(struct sk_buff *skb,int,int, char *);
 #define IS_SKB(skb)            skb_check((skb), 0, __LINE__,__FILE__)
 #define IS_SKB_HEAD(skb)       skb_check((skb), 1, __LINE__,__FILE__)
+#define IS_SKB_LINKED(skb)     skb_check((skb), 2, __LINE__,__FILE__)
+#define IS_SKB_UNLINKED(skb)   skb_check((skb), 3, __LINE__,__FILE__)
+/* Note: IS_SKB_LINKED will accept skb_heads in addition to linked-in 
+ * data skbs */
+
+extern void __skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk);
+extern void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk);
+extern struct sk_buff *__skb_dequeue(struct sk_buff_head *list);
+extern void __skb_insert(struct sk_buff *newsk,
+       struct sk_buff * prev, struct sk_buff *next,
+       struct sk_buff_head * list);
+extern void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list);
+
 #else
 #define IS_SKB(skb)            
 #define IS_SKB_HEAD(skb)       
+#define IS_SKB_LINKED(skb)     
+#define IS_SKB_UNLINKED(skb)   
 
 extern __inline__ void skb_queue_head_init(struct sk_buff_head *list)
 {
index d477003121309869e83e69edba1f15bec95b9e76..bbe1e9e27d6c1b663ddce1fe89ef3c1444f6adce 100644 (file)
@@ -241,15 +241,15 @@ static inline int goodness(struct task_struct * p, struct task_struct * prev, in
 {
        int weight;
 
-#ifdef __SMP__
+#ifdef __SMP__ 
        /* We are not permitted to run a task someone else is running */
        if (p->processor != NO_PROC_ID)
                return -1000;
-#ifdef PAST_2_0
+#ifdef PAST_2_0                
        /* This process is locked to a processor group */
        if (p->processor_mask && !(p->processor_mask & (1<<this_cpu))
                return -1000;
-#endif
+#endif         
 #endif
 
        /*
index dbf204c27f346df47e6167e98ffafc0f31238218..2366ec192ab8de0dc27d10411a13528e23d1db12 100644 (file)
@@ -21,9 +21,6 @@
 #include <asm/system.h>
 #include <asm/dma.h>
 
-/* Define this if you want slow routines that try to trip errors */
-#undef SADISTIC_KMALLOC
-
 /* Private flags. */
 
 #define MF_USED 0xffaa0055
@@ -295,7 +292,7 @@ found_it:
        bucket->nbytesmalloced += size;
        p->bh_flags = type;     /* As of now this block is officially in use */
        p->bh_length = size;
-#ifdef SADISTIC_KMALLOC
+#if CONFIG_SADISTIC_KMALLOC
        memset(p+1, 0xf0, size);
 #endif
        return p + 1;           /* Pointer arithmetic: increments past header */
@@ -405,7 +402,7 @@ void kfree(void *__ptr)
        if (ptr->bh_flags != MF_USED)
                goto bad_order;
        ptr->bh_flags = MF_FREE;        /* As of now this block is officially free */
-#ifdef SADISTIC_KMALLOC
+#if CONFIG_SADISTIC_KMALLOC
        memset(ptr+1, 0xe0, ptr->bh_length);
 #endif
        save_flags(flags);
index 1f74aedba5dcf2dcdff86a8abe501ac030fd8beb..b5a18fbadeddf2d5c394857a6102ab5359428e82 100644 (file)
@@ -15,6 +15,8 @@
  *             Alan Cox        :       Added all the changed routines Linus
  *                                     only put in the headers
  *             Ray VanTassle   :       Fixed --skb->lock in free
+ *     Michael Deutschmann     :       Corrected and expanded
+ *                                     CONFIG_SKB_CHECK system.
  *
  *     TO FIX:
  *             The __skb_ routines ought to check interrupts are disabled
@@ -81,79 +83,166 @@ void show_net_buffers(void)
 
 #if CONFIG_SKB_CHECK
 
-/*
- *     Debugging paranoia. Can go later when this crud stack works
+/* Verify that an SKB has an appropriate magic number, and note the list 
+ * to which it belongs.
+ *
+ * If (*list) is NULL, it will be overwritten with the list membership of 
+ * this SKB (which might also be null).  Otherwise, an error will be 
+ * reported if the SKB does not belong to the same list.
  */
+static __inline__ int skb_magiccheck(struct sk_buff *skb,
+                                    struct sk_buff_head **list,
+                                    int line,
+                                    char *file, 
+                                    char *direct)
+{
+       struct sk_buff_head *list2;
+
+       if (!skb) {
+               printk("File: %s Line %d, found a null skb pointer (%s)\n",
+                       file,line,direct);
+               return -1;
+       }
+
+       if (skb->magic_debug_cookie == SK_HEAD_SKB) { 
+               list2 = (struct sk_buff_head *) skb;
+               goto match_list;
+       }
+
+       if (skb->magic_debug_cookie == SK_GOOD_SKB) {
+               list2 = skb->list;
+       match_list:
+               if (!*list) *list = list2;
+
+               if (*list == list2) 
+                       return 0;
+
+               printk("File %s: Line %d, list mismatch on linked skb (%s)\n",
+                      file,line,direct);
+               return -1;
+       }
+
+       if (skb->magic_debug_cookie == SK_FREED_SKB) {
+               printk("File: %s Line %d, found a freed skb (%s)\n",
+                      file,line,direct);
+               printk("skb=%p, real size=%d, free=%d\n",
+                      skb,skb->truesize,skb->free);
+               return -1;
+       }
+
+       printk("File: %s Line %d, found an invalid skb (%s)\n",
+              file,line,direct);
+
+       return -1;
+}
 
-int skb_check(struct sk_buff *skb, int head, int line, char *file)
+/* Check that the links on an SKB both point to valid SKBs in the same 
+ * list, and are reciprocal (s->p->n == s == s->n->p) 
+ */
+static __inline__ int skb_linkscheck(struct sk_buff *skb,
+                                    struct sk_buff_head *list,
+                                    int line, 
+                                    char *file)
+{
+       if (list->magic_debug_cookie != SK_HEAD_SKB) {
+               printk("File %s: Line %d, skb list is not skb-head\n",
+                      file,line);
+               return -1;
+       }
+
+       if (skb_magiccheck(skb->next,&list,line,file,"next"))
+               return -1;
+
+       if (skb_magiccheck(skb->prev,&list,line,file,"prev"))
+               return -1;
+
+       if (skb->next->prev != skb ||
+           skb->prev->next != skb) {
+               printk("File %s: Line %d, skb links not reciprocal\n",
+                      file,line);
+               return -1;
+       }
+       return 0;
+}
+
+/* Checks specific to sk_buff_head objects.  This actually devolves to 
+ * linkscheck unless whole-queue paranoia is enabled.
+ */
+static __inline__ int skb_headcheck(struct sk_buff_head *list,
+                                   int line, char *file)
 {
-       if (head) {
-               if (skb->magic_debug_cookie != SK_HEAD_SKB) {
-                       printk("File: %s Line %d, found a bad skb-head\n",
-                               file,line);
-                       return -1;
-               }
-               if (!skb->next || !skb->prev) {
-                       printk("skb_check: head without next or prev\n");
-                       return -1;
-               }
-               if (skb->next->magic_debug_cookie != SK_HEAD_SKB
-                       && skb->next->magic_debug_cookie != SK_GOOD_SKB) {
-                       printk("File: %s Line %d, bad next head-skb member\n",
-                               file,line);
+#if CONFIG_SKB_CHECK_WHOLE_QUEUE       
+       struct sk_buff *skb = (struct sk_buff *) list;
+
+       int qlen = 0;                   
+
+       while (skb->next != (struct sk_buff *) list) {  
+               if (skb_magiccheck(skb->next,&list,line,file,"scan")); 
                        return -1;
-               }
-               if (skb->prev->magic_debug_cookie != SK_HEAD_SKB
-                       && skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
-                       printk("File: %s Line %d, bad prev head-skb member\n",
-                               file,line);
+
+               if (skb->next->prev != skb) {
+                       printk("File %s: Line %d, nonreciprocal links"
+                              " in skb queue\n",file,line);
                        return -1;
                }
-#if 0
-               {
-               struct sk_buff *skb2 = skb->next;
-               int i = 0;
-               while (skb2 != skb && i < 5) {
-                       if (skb_check(skb2, 0, line, file) < 0) {
-                               printk("bad queue element in whole queue\n");
-                               return -1;
-                       }
-                       i++;
-                       skb2 = skb2->next;
-               }
-               }
-#endif
-               return 0;
-       }
-       if (skb->next != NULL && skb->next->magic_debug_cookie != SK_HEAD_SKB
-               && skb->next->magic_debug_cookie != SK_GOOD_SKB) {
-               printk("File: %s Line %d, bad next skb member\n",
-                       file,line);
+
+               qlen++;
+               skb = skb->next;
+       } 
+
+       if (list->prev != skb)
+       {
+               printk("File %s: Line %d, skb-head prev link not"
+                      " consistent\n",file,line);
                return -1;
        }
-       if (skb->prev != NULL && skb->prev->magic_debug_cookie != SK_HEAD_SKB
-               && skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
-               printk("File: %s Line %d, bad prev skb member\n",
-                       file,line);
+
+       if (qlen != list->qlen) {
+               printk("File %s: Line %d, skb-head queue length wrong\n",
+                      file,line);
                return -1;
        }
+       return 0;
+#else  /* ! CONFIG_SKB_CHECK_WHOLE_QUEUE */
+       return skb_linkscheck( (struct sk_buff *) list, list, line, file);
+#endif /* ! CONFIG_SKB_CHECK_WHOLE_QUEUE */
+}      
 
 
-       if(skb->magic_debug_cookie==SK_FREED_SKB)
-       {
-               printk("File: %s Line %d, found a freed skb lurking in the undergrowth!\n",
-                       file,line);
-               printk("skb=%p, real size=%d, free=%d\n",
-                       skb,skb->truesize,skb->free);
+/*
+ *     Debugging paranoia.
+ */
+int skb_check(struct sk_buff *skb, int type, int line, char *file)
+{
+       struct sk_buff_head *list = NULL;
+
+       if (skb_magiccheck(skb,&list,line,file,"self"))
+               return -1;
+
+       if (skb->magic_debug_cookie == SK_HEAD_SKB) {
+               if (type != 0) 
+                       return skb_headcheck(list,line,file);
+
+               printk("File %s: Line %d, found skb-head instead of skb\n",
+                      file,line);
                return -1;
        }
-       if(skb->magic_debug_cookie!=SK_GOOD_SKB)
-       {
-               printk("File: %s Line %d, passed a non skb!\n", file,line);
-               printk("skb=%p, real size=%d, free=%d\n",
-                       skb,skb->truesize,skb->free);
+
+       if (list) {
+               if (type == 3) {
+                       printk("File %s: Line %d, skb is already linked\n",
+                              file,line);
+                       return -1;
+               }
+               if (skb_linkscheck(skb,list,line,file))
+                       return -1;
+       }
+       else if (skb->prev || skb->next) {
+               printk("File %s: Line %d, skb has prev/next field,"
+                      " but no list\n",file,line);
                return -1;
        }
+
        if(skb->head>skb->data)
        {
                printk("File: %s Line %d, head > data !\n", file,line);
@@ -182,7 +271,8 @@ int skb_check(struct sk_buff *skb, int head, int line, char *file)
                        skb,skb->data,skb->end,skb->len);
                return -1;
        }
-       if((unsigned long) skb->end > (unsigned long) skb)
+       if((unsigned long) skb->end > 
+           (unsigned long) (skb->data_skb ? skb->data_skb : skb))
        {
                printk("File: %s Line %d, control overrun\n", file,line);
                printk("skb=%p, end=%p\n",
@@ -217,7 +307,7 @@ void skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
        save_flags(flags);
        cli();
 
-       IS_SKB(newsk);
+       IS_SKB_UNLINKED(newsk);
        IS_SKB_HEAD(list);
        if (newsk->next || newsk->prev)
                printk("Suspicious queue head: sk_buff on list!\n");
@@ -238,7 +328,7 @@ void __skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
        struct sk_buff *list = (struct sk_buff *)list_;
 
 
-       IS_SKB(newsk);
+       IS_SKB_UNLINKED(newsk);
        IS_SKB_HEAD(list);
        if (newsk->next || newsk->prev)
                printk("Suspicious queue head: sk_buff on list!\n");
@@ -266,7 +356,7 @@ void skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
 
        if (newsk->next || newsk->prev)
                printk("Suspicious queue tail: sk_buff on list!\n");
-       IS_SKB(newsk);
+       IS_SKB_UNLINKED(newsk);
        IS_SKB_HEAD(list);
 
        newsk->next = list;
@@ -287,7 +377,7 @@ void __skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
 
        if (newsk->next || newsk->prev)
                printk("Suspicious queue tail: sk_buff on list!\n");
-       IS_SKB(newsk);
+       IS_SKB_UNLINKED(newsk);
        IS_SKB_HEAD(list);
 
        newsk->next = list;
@@ -332,7 +422,7 @@ struct sk_buff *skb_dequeue(struct sk_buff_head *list_)
        
        restore_flags(flags);
 
-       IS_SKB(result);
+       IS_SKB_UNLINKED(result);
        return result;
 }
 
@@ -356,7 +446,7 @@ struct sk_buff *__skb_dequeue(struct sk_buff_head *list_)
        list_->qlen--;
        result->list = NULL;
        
-       IS_SKB(result);
+       IS_SKB_UNLINKED(result);
        return result;
 }
 
@@ -367,8 +457,8 @@ void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
 {
        unsigned long flags;
 
-       IS_SKB(old);
-       IS_SKB(newsk);
+       IS_SKB_LINKED(old);
+       IS_SKB_UNLINKED(newsk);
 
        if(!old->next || !old->prev)
                printk("insert before unlisted item!\n");
@@ -395,9 +485,9 @@ void __skb_insert(struct sk_buff *newsk,
        struct sk_buff * prev, struct sk_buff *next,
        struct sk_buff_head * list)
 {
-       IS_SKB(prev);
-       IS_SKB(newsk);
-       IS_SKB(next);
+       IS_SKB_LINKED(prev);
+       IS_SKB_UNLINKED(newsk);
+       IS_SKB_LINKED(next);
 
        if(!prev->next || !prev->prev)
                printk("insert after unlisted item!\n");
@@ -422,8 +512,8 @@ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
 {
        unsigned long flags;
 
-       IS_SKB(old);
-       IS_SKB(newsk);
+       IS_SKB_LINKED(old);
+       IS_SKB_UNLINKED(newsk);
 
        if(!old->next || !old->prev)
                printk("append before unlisted item!\n");
@@ -474,10 +564,13 @@ void skb_unlink(struct sk_buff *skb)
        restore_flags(flags);
 }
 
-void __skb_unlink(struct sk_buff *skb)
+void __skb_unlink(struct sk_buff *skb,struct sk_buff_head *list)
 {
        IS_SKB(skb);
 
+        if(skb->list != list)
+               printk("__skb_unlink called with wrong list argument\n");
+
        if(skb->list)
        {
                skb->list->qlen--;
@@ -581,9 +674,9 @@ void kfree_skb(struct sk_buff *skb, int rw)
                        __builtin_return_address(0));
                return;
        }
-#if CONFIG_SKB_CHECK
-       IS_SKB(skb);
-#endif
+
+       IS_SKB_UNLINKED(skb);
+
        /* Check it twice, this is such a rare event and only occurs under
         * extremely high load, normal code path should not suffer from the
         * overhead of the cli.
index e52a15c6cff621ab76eeb221b3b53e2f124e8510..6cf1c71e6b30b0f1f6f567b048a034f41baa6fce 100644 (file)
  *             David S. Miller :       New socket lookup architecture for ISS.
  *                                     This code is dedicated to John Dyson.
  *             Elliot Poger    :       Added support for SO_BINDTODEVICE.
+ *     Michael Deutschmann     :       Added some IS_SKBs in tcp_recvmsg.
  *                                     
  * To Fix:
  *             Fast path the code. Two things here - fix the window calculation
@@ -1652,9 +1653,11 @@ static int tcp_recvmsg(struct sock *sk, struct msghdr *msg,
 
                current->state = TASK_INTERRUPTIBLE;
 
+               IS_SKB_HEAD((struct sk_buff *)&sk->receive_queue);
                skb = sk->receive_queue.next;
                while (skb != (struct sk_buff *)&sk->receive_queue)
                {
+                       IS_SKB_LINKED(skb);
                        if (before(*seq, skb->seq))
                                break;
                        offset = *seq - skb->seq;
index 3f3f4b3e8f9c681933aa1ec921a54755075de211..e2f49f0ffdda56f9ac86c720c71998a73b150179 100644 (file)
@@ -46,6 +46,7 @@
  *                                     </RANT>
  *     George Baeslack         :       SIGIO delivery on accept() bug that
  *                                     affected sun jdk.
+ *     Michael Deutschmann     :       Added IS_SKBs to tcp_insert_skb
  */
 
 #include <linux/config.h>
@@ -1897,6 +1898,8 @@ static inline void tcp_insert_skb(struct sk_buff * skb, struct sk_buff_head * li
        struct sk_buff * prev, * next;
        u32 seq;
 
+       IS_SKB_UNLINKED(skb);
+
        /*
         * Find where the new skb goes.. (This goes backwards,
         * on the assumption that we get the packets in order)
@@ -1904,7 +1907,9 @@ static inline void tcp_insert_skb(struct sk_buff * skb, struct sk_buff_head * li
        seq = skb->seq;
        prev = list->prev;
        next = (struct sk_buff *) list;
+       IS_SKB_HEAD(next);
        for (;;) {
+               IS_SKB_LINKED(prev);
                if (prev == (struct sk_buff *) list || !after(prev->seq, seq))
                        break;
                next = prev;
index fc2608113cb1fbc183af069fa62e06f824da3839..7ebdf1b76347f337012c267683e87bcfef4cca4a 100644 (file)
@@ -65,17 +65,17 @@ static void handle_config(void)
 #endif
 
 #define GETNEXT { \
+if (next == end) \
+       break; \
 next_byte(__buf); \
 if (!__nrbuf) { \
        __buf = *(unsigned long *) next; \
        __nrbuf = sizeof(unsigned long); \
-       if (!__buf) \
-               break; \
 } next++; __nrbuf--; }
 #define CASE(c,label) if (current == c) goto label
 #define NOTCASE(c,label) if (current != c) goto label
 
-static void state_machine(register char *next)
+static void state_machine(register char *next, register char *end)
 {
        for(;;) {
        register unsigned long __buf = 0;
@@ -244,7 +244,7 @@ static void do_depend(void)
                return;
        }
        close(fd);
-       state_machine(map);
+       state_machine(map, map + st.st_size);
        munmap(map, mapsize);
        if (hasdep)
                puts(command);