Commit 97ccc379 authored by Linus Torvalds's avatar Linus Torvalds

Import 2.1.73

parent 62e3b9df
...@@ -248,11 +248,13 @@ CONFIG_BLK_DEV_IDETAPE ...@@ -248,11 +248,13 @@ CONFIG_BLK_DEV_IDETAPE
Include IDE/ATAPI FLOPPY support Include IDE/ATAPI FLOPPY support
CONFIG_BLK_DEV_IDEFLOPPY CONFIG_BLK_DEV_IDEFLOPPY
If you have an IDE floppy drive which uses the ATAPI protocol, say If you have an IDE floppy drive which uses the ATAPI protocol, say Y.
Y. Chances are that you don't, because these animals are rare.
ATAPI is a new protocol used by IDE CDROM/tape/floppy drives, ATAPI is a new protocol used by IDE CDROM/tape/floppy drives,
similar to the SCSI protocol. At boot time, the FLOPPY drive will similar to the SCSI protocol. IDE floppy drives include the
be identified along with other IDE devices, as "hdb" or "hdc", or LS-120 and the ATAPI ZIP (ATAPI PD-CD/CDR drives are not supported
by this driver; support for PD-CD/CDR drives is available through
the SCSI emulation). At boot time, the FLOPPY drive will be
identified along with other IDE devices, as "hdb" or "hdc", or
something similar. If you want to compile the driver as a module ( = something similar. If you want to compile the driver as a module ( =
code which can be inserted in and removed from the running kernel code which can be inserted in and removed from the running kernel
whenever you want), say M here and read Documentation/modules.txt. whenever you want), say M here and read Documentation/modules.txt.
...@@ -263,10 +265,11 @@ CONFIG_BLK_DEV_IDESCSI ...@@ -263,10 +265,11 @@ CONFIG_BLK_DEV_IDESCSI
This will provide SCSI host adapter emulation for IDE ATAPI devices, This will provide SCSI host adapter emulation for IDE ATAPI devices,
and will allow you to use a SCSI device driver instead of a native and will allow you to use a SCSI device driver instead of a native
ATAPI driver. This is useful if you have an ATAPI device for which ATAPI driver. This is useful if you have an ATAPI device for which
no native driver has been written; you can then use this emulation no native driver has been written (for example, an ATAPI PD-CD or
together with an appropriate SCSI device driver. If both this SCSI CDR drive); you can then use this emulation together with an
emulation and native ATAPI support are compiled into the kernel, the appropriate SCSI device driver. If both this SCSI emulation and
native support will be used. Normally, say N. native ATAPI support are compiled into the kernel, the native
support will be used. Normally, say N.
CMD640 chipset bugfix/support CMD640 chipset bugfix/support
CONFIG_BLK_DEV_CMD640 CONFIG_BLK_DEV_CMD640
...@@ -303,17 +306,24 @@ CONFIG_BLK_DEV_RZ1000 ...@@ -303,17 +306,24 @@ CONFIG_BLK_DEV_RZ1000
Linux. This may slow disk throughput by a few percent, but at least Linux. This may slow disk throughput by a few percent, but at least
things will operate 100% reliably. If unsure, say Y. things will operate 100% reliably. If unsure, say Y.
Intel 82371 PIIX (Triton I/II), VIA VP-1 DMA support Generic PCI IDE chipset support
CONFIG_BLK_DEV_IDEPCI
Enable this for PCI systems which use IDE drive(s).
This option helps the IDE driver to automatically detect and
configure all PCI-based IDE interfaces in your system.
It is safe to say Y to this question.
Generic PCI bus-master DMA support
CONFIG_BLK_DEV_IDEDMA CONFIG_BLK_DEV_IDEDMA
If your PCI system uses IDE drive(s) (as opposed to SCSI, say) If your PCI IDE controller is capable of bus-master DMA
and is capable of bus-master DMA operation (most Pentium PCI (Direct Memory Access) transfers (most newer systems),
systems), you will want to enable this option to allow use of then you will want to enable this option to reduce CPU overhead.
bus-mastering DMA data transfers. Read the comments at the With this option, Linux will automatically enable DMA transfers
beginning of drivers/block/idedma.c and Documentation/ide.txt. in most cases, noting this with "DMA" appended to the drive
You can get the latest version of the hdparm utility via identification info. You can also use the "hdparm" utility to
ftp (user: anonymous) from enable DMA for drives which were not enabled automatically.
sunsite.unc.edu/pub/Linux/kernel/patches/diskdrives/; it is You can get the latest version of the hdparm utility via anonymous
used to tune your harddisk. FTP from sunsite.unc.edu/pub/Linux/system/hardware/
It is safe to say Y to this question. It is safe to say Y to this question.
Other IDE chipset support Other IDE chipset support
...@@ -371,6 +381,12 @@ CONFIG_BLK_DEV_OPTI621 ...@@ -371,6 +381,12 @@ CONFIG_BLK_DEV_OPTI621
for drives attached to an OPTi MIDE controller. for drives attached to an OPTi MIDE controller.
Please read the comments at the top of drivers/block/opti621.c. Please read the comments at the top of drivers/block/opti621.c.
NS87415 support (EXPERIMENTAL)
CONFIG_BLK_DEV_NS87415
This driver adds detection and support for the NS87415 chip
(used in SPARC64, among others).
Please read the comments at the top of drivers/block/ns87415.c.
QDI QD6580 support QDI QD6580 support
CONFIG_BLK_DEV_QD6580 CONFIG_BLK_DEV_QD6580
This driver is enabled at runtime using the "ide0=qd6580" kernel This driver is enabled at runtime using the "ide0=qd6580" kernel
...@@ -875,31 +891,8 @@ CONFIG_BINFMT_AOUT ...@@ -875,31 +891,8 @@ CONFIG_BINFMT_AOUT
Kernel support for JAVA binaries Kernel support for JAVA binaries
CONFIG_BINFMT_JAVA CONFIG_BINFMT_JAVA
JAVA(tm) is an object oriented programming language developed by This option is obsolete. Use binfmt_misc instead. It is more
SUN; JAVA programs are compiled into "JAVA bytecode" binaries which flexible.
can then be interpreted by run time systems on many different
operating systems. These JAVA binaries are becoming a universal
executable format. If you want to execute JAVA binaries, read the
Java on Linux HOWTO, available via ftp (user: anonymous) at
sunsite.unc.edu:/pub/Linux/docs/HOWTO. You will then need to install
the run time system contained in the Java Developers Kit (JDK) as
described in the HOWTO. This is completely independent of the Linux
kernel and you do NOT need to say Y here for this to work.
Saying Y here allows you to execute a JAVA bytecode binary just like
any other Linux program: by simply typing in its name. (You also
need to have the JDK installed for this to work). As more and more
Java programs become available, the use for this will gradually
increase. You can even execute HTML files containing JAVA applets (=
JAVA binaries) if those files start with the string
"<!--applet-->". If you want to use this, say Y here and read
Documentation/java.txt. If you disable this option it will reduce
your kernel by about 4kB. This is not much and by itself does not
warrant removing support. However its removal is a good idea if you
do not have the JDK installed. You may answer M for module support
and later load the module when you install the JDK or find an
interesting Java program that you can't live without. The module
will be called binfmt_java.o. If you don't know what to answer at
this point then answer Y.
Kernel support for Linux/Intel ELF binaries Kernel support for Linux/Intel ELF binaries
CONFIG_BINFMT_EM86 CONFIG_BINFMT_EM86
...@@ -1332,6 +1325,37 @@ CONFIG_IP_MASQUERADE ...@@ -1332,6 +1325,37 @@ CONFIG_IP_MASQUERADE
inserted in and removed from the running kernel whenever you want; inserted in and removed from the running kernel whenever you want;
read Documentation/modules.txt for details. read Documentation/modules.txt for details.
IP: ICMP masquerading
CONFIG_IP_MASQUERADE_ICMP
The basic masquerade code described for CONFIG_IP_MASQUERADE only
handles TCP or UDP packets (and ICMP errors for existing
connections). This option adds additional support for masquerading
ICMP packets, such as ping or the probes used by the Windows 95
tracert program.
If you want this, say Y.
IP: ipautofw masquerade support
CONFIG_IP_MASQUERADE_IPAUTOFW (Experimental)
ipautofw is a program by Richard Lynch allowing additional
support for masquerading protocols which do not (as yet)
have additional protocol helpers.
Information and source for ipautofw is available from
ftp://ftp.netis.com/pub/members/rlynch/
The ipautofw code is still under development and so is currently
marked EXPERIMENTAL.
If you want this, say Y.
IP: ipportfw masquerade support
CONFIG_IP_MASQUERADE_IPPORTFW
ipportfw is an addition to IP Masquerading written by Steven Clarke
to allow some forwarding of packets from outside to inside a
firewall on given ports. Information and source for ipportfw is
available from
http://www.monmouth.demon.co.uk/ipsubs/portforwarding.html
The portfw code is still under development and so is currently
marked EXPERIMENTAL.
If you want this, say Y.
IP: always defragment IP: always defragment
CONFIG_IP_ALWAYS_DEFRAG CONFIG_IP_ALWAYS_DEFRAG
This option means that all incoming fragments (= parts of IP packets This option means that all incoming fragments (= parts of IP packets
......
...@@ -28,9 +28,9 @@ Bugs in the present implementation: ...@@ -28,9 +28,9 @@ Bugs in the present implementation:
Please report any bugs and suggestions to Please report any bugs and suggestions to
Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de> or Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de>
Pascal Haible <haible@izfm.uni-stuttgart.de> . Pascal Haible <haible@izfm.uni-stuttgart.de>
Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
Bruno Haible Bruno Haible
<haible@ma2s2.mathematik.uni-karlsruhe.de> <haible@ma2s2.mathematik.uni-karlsruhe.de>
......
...@@ -279,6 +279,12 @@ M: andersee@debian.org ...@@ -279,6 +279,12 @@ M: andersee@debian.org
L: linux-kernel@vger.rutgers.edu L: linux-kernel@vger.rutgers.edu
S: Maintained S: Maintained
IDE/ATAPI TAPE/FLOPPY DRIVERS
P: Gadi Oxman
M: Gadi Oxman <gadio@netvision.net.il>
L: linux-kernel@vger.rutgers.edu
S: Maintained
ISDN SUBSYSTEM ISDN SUBSYSTEM
P: Fritz Elfert P: Fritz Elfert
M: fritz@wuemaus.franken.de M: fritz@wuemaus.franken.de
...@@ -551,6 +557,11 @@ P: Pavel Machek ...@@ -551,6 +557,11 @@ P: Pavel Machek
M: pavel@atrey.karlin.mff.cuni.cz M: pavel@atrey.karlin.mff.cuni.cz
S: Maintained S: Maintained
SYSV FILESYSTEM
P: Krzysztof G. Baranowski
M: kgb@manjak.knm.org.pl
S: Maintained
REST: REST:
P: Linus Torvalds P: Linus Torvalds
S: Buried alive in diapers S: Buried alive in diapers
VERSION = 2 VERSION = 2
PATCHLEVEL = 1 PATCHLEVEL = 1
SUBLEVEL = 72 SUBLEVEL = 73
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
......
...@@ -58,6 +58,7 @@ CONFIG_BLK_DEV_IDECD=y ...@@ -58,6 +58,7 @@ CONFIG_BLK_DEV_IDECD=y
CONFIG_BLK_DEV_CMD640=y CONFIG_BLK_DEV_CMD640=y
# CONFIG_BLK_DEV_CMD640_ENHANCED is not set # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
CONFIG_BLK_DEV_RZ1000=y CONFIG_BLK_DEV_RZ1000=y
CONFIG_BLK_DEV_IDEPCI=y
CONFIG_BLK_DEV_IDEDMA=y CONFIG_BLK_DEV_IDEDMA=y
# CONFIG_IDE_CHIPSETS is not set # CONFIG_IDE_CHIPSETS is not set
...@@ -221,7 +222,6 @@ CONFIG_ISO9660_FS=y ...@@ -221,7 +222,6 @@ CONFIG_ISO9660_FS=y
# CONFIG_VFAT_FS is not set # CONFIG_VFAT_FS is not set
CONFIG_PROC_FS=y CONFIG_PROC_FS=y
CONFIG_NFS_FS=y CONFIG_NFS_FS=y
# CONFIG_ROOT_NFS is not set
CONFIG_NFSD=y CONFIG_NFSD=y
CONFIG_SUNRPC=y CONFIG_SUNRPC=y
CONFIG_LOCKD=y CONFIG_LOCKD=y
......
...@@ -40,6 +40,10 @@ ...@@ -40,6 +40,10 @@
#include <asm/system.h> #include <asm/system.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/ldt.h> #include <asm/ldt.h>
#include <asm/processor.h>
#ifdef CONFIG_MATH_EMULATION
#include <asm/math_emu.h>
#endif
#ifdef __SMP__ #ifdef __SMP__
asmlinkage void ret_from_smpfork(void) __asm__("ret_from_smpfork"); asmlinkage void ret_from_smpfork(void) __asm__("ret_from_smpfork");
...@@ -525,19 +529,16 @@ int dump_fpu (struct pt_regs * regs, struct user_i387_struct* fpu) ...@@ -525,19 +529,16 @@ int dump_fpu (struct pt_regs * regs, struct user_i387_struct* fpu)
{ {
int fpvalid; int fpvalid;
/* Flag indicating the math stuff is valid. We don't support this for the if ((fpvalid = current->used_math) != 0) {
soft-float routines yet */ if (hard_math) {
if (hard_math) { if (last_task_used_math == current) {
if ((fpvalid = current->used_math) != 0) { __asm__("clts ; fsave %0; fwait": :"m" (*fpu));
if (last_task_used_math == current) }
__asm__("clts ; fnsave %0": :"m" (*fpu));
else else
memcpy(fpu,&current->tss.i387.hard,sizeof(*fpu)); memcpy(fpu,&current->tss.i387.hard,sizeof(*fpu));
} else {
memcpy(fpu,&current->tss.i387.hard,sizeof(*fpu));
} }
} else {
/* we should dump the emulator state here, but we need to
convert it into standard 387 format first.. */
fpvalid = 0;
} }
return fpvalid; return fpvalid;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/processor.h>
/* /*
* does not yet catch signals sent when the child dies. * does not yet catch signals sent when the child dies.
...@@ -561,6 +562,102 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) ...@@ -561,6 +562,102 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
goto out; goto out;
} }
case PTRACE_GETREGS: { /* Get all gp regs from the child. */
if (!access_ok(VERIFY_WRITE, (unsigned *)data,
17*sizeof(long)))
{
ret = -EIO;
goto out;
}
for ( i = 0; i < 17*sizeof(long); i += sizeof(long) )
{
__put_user(getreg(child, i),(unsigned long *) data);
data += sizeof(long);
}
ret = 0;
goto out;
};
case PTRACE_SETREGS: { /* Set all gp regs in the child. */
unsigned long tmp;
if (!access_ok(VERIFY_READ, (unsigned *)data,
17*sizeof(long)))
{
ret = -EIO;
goto out;
}
for ( i = 0; i < 17*sizeof(long); i += sizeof(long) )
{
__get_user(tmp, (unsigned long *) data);
putreg(child, i, tmp);
data += sizeof(long);
}
ret = 0;
goto out;
};
case PTRACE_GETFPREGS: { /* Get the child FPU state. */
if (!access_ok(VERIFY_WRITE, (unsigned *)data,
sizeof(struct user_i387_struct)))
{
ret = -EIO;
goto out;
}
ret = 0;
if ( !child->used_math ) {
/* Simulate an empty FPU. */
child->tss.i387.hard.cwd = 0xffff037f;
child->tss.i387.hard.swd = 0xffff0000;
child->tss.i387.hard.twd = 0xffffffff;
}
#ifdef CONFIG_MATH_EMULATION
if ( hard_math ) {
#endif
if (last_task_used_math == child) {
clts();
__asm__("fnsave %0; fwait":"=m" (child->tss.i387.hard));
last_task_used_math = NULL;
stts();
}
__copy_to_user((void *)data, &child->tss.i387.hard,
sizeof(struct user_i387_struct));
#ifdef CONFIG_MATH_EMULATION
} else {
save_i387_soft(&child->tss.i387.soft,
(struct _fpstate *)data);
}
#endif
goto out;
};
case PTRACE_SETFPREGS: { /* Set the child FPU state. */
if (!access_ok(VERIFY_READ, (unsigned *)data,
sizeof(struct user_i387_struct)))
{
ret = -EIO;
goto out;
}
child->used_math = 1;
#ifdef CONFIG_MATH_EMULATION
if ( hard_math ) {
#endif
if (last_task_used_math == child) {
/* Discard the state of the FPU */
last_task_used_math = NULL;
}
__copy_from_user(&child->tss.i387.hard, (void *)data,
sizeof(struct user_i387_struct));
child->flags &= ~PF_USEDFPU;
#ifdef CONFIG_MATH_EMULATION
} else {
restore_i387_soft(&child->tss.i387.soft,
(struct _fpstate *)data);
}
#endif
ret = 0;
goto out;
};
default: default:
ret = -EIO; ret = -EIO;
goto out; goto out;
......
...@@ -156,7 +156,6 @@ static inline void restore_i387_hard(struct _fpstate *buf) ...@@ -156,7 +156,6 @@ static inline void restore_i387_hard(struct _fpstate *buf)
stts(); stts();
} }
#endif #endif
current->used_math = 1;
current->flags &= ~PF_USEDFPU; current->flags &= ~PF_USEDFPU;
__copy_from_user(&current->tss.i387.hard, buf, sizeof(*buf)); __copy_from_user(&current->tss.i387.hard, buf, sizeof(*buf));
} }
...@@ -169,8 +168,9 @@ static inline void restore_i387(struct _fpstate *buf) ...@@ -169,8 +168,9 @@ static inline void restore_i387(struct _fpstate *buf)
if (hard_math) if (hard_math)
restore_i387_hard(buf); restore_i387_hard(buf);
else else
restore_i387_soft(buf); restore_i387_soft(&current->tss.i387.soft, buf);
#endif #endif
current->used_math = 1;
} }
static int static int
...@@ -309,7 +309,6 @@ static inline struct _fpstate * save_i387_hard(struct _fpstate * buf) ...@@ -309,7 +309,6 @@ static inline struct _fpstate * save_i387_hard(struct _fpstate * buf)
#endif #endif
current->tss.i387.hard.status = current->tss.i387.hard.swd; current->tss.i387.hard.status = current->tss.i387.hard.swd;
copy_to_user(buf, &current->tss.i387.hard, sizeof(*buf)); copy_to_user(buf, &current->tss.i387.hard, sizeof(*buf));
current->used_math = 0;
return buf; return buf;
} }
...@@ -318,10 +317,16 @@ static struct _fpstate * save_i387(struct _fpstate *buf) ...@@ -318,10 +317,16 @@ static struct _fpstate * save_i387(struct _fpstate *buf)
if (!current->used_math) if (!current->used_math)
return NULL; return NULL;
/* This will cause a "finit" to be triggered by the next
attempted FPU operation by the 'current' process.
*/
current->used_math = 0;
#ifndef CONFIG_MATH_EMULATION #ifndef CONFIG_MATH_EMULATION
return save_i387_hard(buf); return save_i387_hard(buf);
#else #else
return hard_math ? save_i387_hard(buf) : save_i387_soft(buf); return hard_math ? save_i387_hard(buf)
: save_i387_soft(&current->tss.i387.soft, buf);
#endif #endif
} }
......
...@@ -12,18 +12,23 @@ CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin $(MATH_EMULATION) ...@@ -12,18 +12,23 @@ CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin $(MATH_EMULATION)
.S.o: .S.o:
$(CC) -D__ASSEMBLY__ $(PARANOID) -c $< $(CC) -D__ASSEMBLY__ $(PARANOID) -c $<
L_OBJS =fpu_entry.o div_small.o errors.o \ # From 'C' language sources:
fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \ C_OBJS =fpu_entry.o errors.o \
fpu_arith.o fpu_aux.o fpu_etc.o fpu_tags.o fpu_trig.o \
load_store.o get_address.o \ load_store.o get_address.o \
poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.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_add_sub.o reg_compare.o reg_constant.o reg_convert.o \
reg_div.o reg_mul.o reg_norm.o \ reg_ld_str.o reg_divide.o reg_mul.o
reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \
reg_round.o \ # From 80x86 assembler sources:
A_OBJS =reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \
div_small.o reg_norm.o reg_round.o \
wm_shrx.o wm_sqrt.o \ wm_shrx.o wm_sqrt.o \
div_Xsig.o polynom_Xsig.o round_Xsig.o \ div_Xsig.o polynom_Xsig.o round_Xsig.o \
shr_Xsig.o mul_Xsig.o shr_Xsig.o mul_Xsig.o
L_OBJS =$(C_OBJS) $(A_OBJS)
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
proto: proto:
......
+---------------------------------------------------------------------------+ +---------------------------------------------------------------------------+
| wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. | | 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 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@suburbia.net | | Australia. E-mail billm@suburbia.net |
| | | |
...@@ -44,9 +44,12 @@ some differences. ...@@ -44,9 +44,12 @@ some differences.
Please report bugs, etc to me at: Please report bugs, etc to me at:
billm@suburbia.net billm@suburbia.net
For more information on the emulator and on floating point topics, see
my web pages, currently at http://www.suburbia.net/~billm/
--Bill Metzenthen --Bill Metzenthen
October 1996 December 1997
----------------------- Internals of wm-FPU-emu ----------------------- ----------------------- Internals of wm-FPU-emu -----------------------
...@@ -95,8 +98,9 @@ form of re-entrancy which is required by the Linux kernel. ...@@ -95,8 +98,9 @@ form of re-entrancy which is required by the Linux kernel.
----------------------- Limitations of wm-FPU-emu ----------------------- ----------------------- Limitations of wm-FPU-emu -----------------------
There are a number of differences between the current 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 (version 2.00) and the 80486 FPU (apart from bugs). The differences
important differences are listed below: 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 The Roundup flag does not have much meaning for the transcendental
functions and its 80486 value with these functions is likely to differ functions and its 80486 value with these functions is likely to differ
...@@ -122,18 +126,6 @@ and Unnormals. None of these will be generated by an 80486 or by the ...@@ -122,18 +126,6 @@ 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 emulator. Do not use them. The emulator treats them differently in
detail from the way an 80486 does. 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 Self modifying code can cause the emulator to fail. An example of such
code is: code is:
movl %esp,[%ebx] movl %esp,[%ebx]
......
...@@ -12,13 +12,13 @@ ...@@ -12,13 +12,13 @@
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+ /*---------------------------------------------------------------------------+
| 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" #include "fpu_emu.h"
.text .text
ENTRY(div_small) ENTRY(FPU_div_small)
pushl %ebp pushl %ebp
movl %esp,%ebp movl %esp,%ebp
......
This diff is collapsed.
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
| | | |
| Code to implement the FPU register/register arithmetic instructions | | Code to implement the FPU register/register arithmetic instructions |
| | | |
| Copyright (C) 1992,1993 | | Copyright (C) 1992,1993,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -19,16 +19,18 @@ ...@@ -19,16 +19,18 @@
void fadd__() void fadd__()
{ {
/* fadd st,st(i) */ /* fadd st,st(i) */
int i = FPU_rm;
clear_C1(); 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__() void fmul__()
{ {
/* fmul st,st(i) */ /* fmul st,st(i) */
int i = FPU_rm;
clear_C1(); 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__() ...@@ -37,7 +39,7 @@ void fsub__()
{ {
/* fsub st,st(i) */ /* fsub st,st(i) */
clear_C1(); 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_() ...@@ -45,7 +47,7 @@ void fsubr_()
{ {
/* fsubr st,st(i) */ /* fsubr st,st(i) */
clear_C1(); 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__() ...@@ -53,7 +55,7 @@ void fdiv__()
{ {
/* fdiv st,st(i) */ /* fdiv st,st(i) */
clear_C1(); 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_() ...@@ -61,7 +63,7 @@ void fdivr_()
{ {
/* fdivr st,st(i) */ /* fdivr st,st(i) */
clear_C1(); 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_() ...@@ -69,8 +71,9 @@ void fdivr_()
void fadd_i() void fadd_i()
{ {
/* fadd st(i),st */ /* fadd st(i),st */
int i = FPU_rm;
clear_C1(); 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() ...@@ -78,27 +81,23 @@ void fmul_i()
{ {
/* fmul st(i),st */ /* fmul st(i),st */
clear_C1(); 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() void fsubri()
{ {
/* fsubr st(i),st */ /* 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(); 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() void fsub_i()
{ {
/* fsub st(i),st */ /* 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(); 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() ...@@ -106,7 +105,7 @@ void fdivri()
{ {
/* fdivr st(i),st */ /* fdivr st(i),st */
clear_C1(); 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() ...@@ -114,7 +113,7 @@ void fdiv_i()
{ {
/* fdiv st(i),st */ /* fdiv st(i),st */
clear_C1(); 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() ...@@ -122,9 +121,10 @@ void fdiv_i()
void faddp_() void faddp_()
{ {
/* faddp st(i),st */ /* faddp st(i),st */
int i = FPU_rm;
clear_C1(); clear_C1();
if ( !reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) if ( FPU_add(&st(i), FPU_gettagi(i), i, control_word) >= 0 )
pop(); FPU_pop();
} }
...@@ -132,8 +132,8 @@ void fmulp_() ...@@ -132,8 +132,8 @@ void fmulp_()
{ {
/* fmulp st(i),st */ /* fmulp st(i),st */
clear_C1(); clear_C1();
if ( !reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) if ( FPU_mul(&st(0), FPU_gettag0(), FPU_rm, control_word) >= 0 )
pop(); FPU_pop();
} }
...@@ -141,22 +141,18 @@ void fmulp_() ...@@ -141,22 +141,18 @@ void fmulp_()
void fsubrp() void fsubrp()
{ {
/* fsubrp st(i),st */ /* 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(); clear_C1();
if ( !reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) if ( FPU_sub(DEST_RM, FPU_rm, control_word) >= 0 )
pop(); FPU_pop();
} }
void fsubp_() void fsubp_()
{ {
/* fsubp st(i),st */ /* 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(); clear_C1();
if ( !reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) if ( FPU_sub(REV|DEST_RM, FPU_rm, control_word) >= 0 )
pop(); FPU_pop();
} }
...@@ -164,8 +160,8 @@ void fdivrp() ...@@ -164,8 +160,8 @@ void fdivrp()
{ {
/* fdivrp st(i),st */ /* fdivrp st(i),st */
clear_C1(); clear_C1();
if ( !reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) if ( FPU_div(DEST_RM, FPU_rm, control_word) >= 0 )
pop(); FPU_pop();
} }
...@@ -173,7 +169,6 @@ void fdivp_() ...@@ -173,7 +169,6 @@ void fdivp_()
{ {
/* fdivp st(i),st */ /* fdivp st(i),st */
clear_C1(); clear_C1();
if ( !reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) if ( FPU_div(REV|DEST_RM, FPU_rm, control_word) >= 0 )
pop(); FPU_pop();
} }
/*---------------------------------------------------------------------------+ /*---------------------------------------------------------------------------+
| fpu_asm.h | | fpu_asm.h |
| | | |
| Copyright (C) 1992,1995 | | Copyright (C) 1992,1995,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@jacobi.maths.monash.edu.au | | Australia. E-mail billm@suburbia.net |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -19,13 +19,14 @@ ...@@ -19,13 +19,14 @@
#define PARAM2 12(%ebp) #define PARAM2 12(%ebp)
#define PARAM3 16(%ebp) #define PARAM3 16(%ebp)
#define PARAM4 20(%ebp) #define PARAM4 20(%ebp)
#define PARAM5 24(%ebp)
#define PARAM6 28(%ebp)
#define PARAM7 32(%ebp)
#define SIGL_OFFSET 8 #define SIGL_OFFSET 0
#define SIGN(x) (x) #define EXP(x) 8(x)
#define TAG(x) 1(x)
#define EXP(x) 4(x)
#define SIG(x) SIGL_OFFSET##(x) #define SIG(x) SIGL_OFFSET##(x)
#define SIGL(x) SIGL_OFFSET##(x) #define SIGL(x) SIGL_OFFSET##(x)
#define SIGH(x) 12(x) #define SIGH(x) 4(x)
#endif _FPU_ASM_H_ #endif _FPU_ASM_H_
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
| | | |
| Code to implement some of the FPU auxiliary instructions. | | Code to implement some of the FPU auxiliary instructions. |
| | | |
| Copyright (C) 1992,1993,1994 | | Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -32,15 +32,11 @@ void fclex(void) ...@@ -32,15 +32,11 @@ void fclex(void)
/* Needs to be externally visible */ /* Needs to be externally visible */
void finit() void finit()
{ {
int r;
control_word = 0x037f; control_word = 0x037f;
partial_status = 0; partial_status = 0;
top = 0; /* We don't keep top in the status word internally. */ top = 0; /* We don't keep top in the status word internally. */
for (r = 0; r < 8; r++) fpu_tag_word = 0xffff;
{ /* The behaviour is different from that detailed in
regs[r].tag = TW_Empty;
}
/* The behaviour is different to that detailed in
Section 15.1.6 of the Intel manual */ Section 15.1.6 of the Intel manual */
operand_address.offset = 0; operand_address.offset = 0;
operand_address.selector = 0; operand_address.selector = 0;
...@@ -99,19 +95,27 @@ void fp_nop() ...@@ -99,19 +95,27 @@ void fp_nop()
void fld_i_() void fld_i_()
{ {
FPU_REG *st_new_ptr; FPU_REG *st_new_ptr;
int i;
u_char tag;
if ( STACK_OVERFLOW ) if ( STACK_OVERFLOW )
{ stack_overflow(); return; } { FPU_stack_overflow(); return; }
/* fld st(i) */ /* fld st(i) */
if ( NOT_EMPTY(FPU_rm) ) i = FPU_rm;
{ reg_move(&st(FPU_rm), st_new_ptr); push(); } if ( NOT_EMPTY(i) )
{
reg_copy(&st(i), st_new_ptr);
tag = FPU_gettagi(i);
push();
FPU_settag0(tag);
}
else else
{ {
if ( control_word & CW_Invalid ) if ( control_word & CW_Invalid )
{ {
/* The masked response */ /* The masked response */
stack_underflow(); FPU_stack_underflow();
} }
else else
EXCEPTION(EX_StackUnder); EXCEPTION(EX_StackUnder);
...@@ -124,61 +128,77 @@ void fxch_i() ...@@ -124,61 +128,77 @@ void fxch_i()
{ {
/* fxch st(i) */ /* fxch st(i) */
FPU_REG t; FPU_REG t;
register FPU_REG *sti_ptr = &st(FPU_rm), *st0_ptr = &st(0); int i = FPU_rm;
FPU_REG *st0_ptr = &st(0), *sti_ptr = &st(i);
if ( st0_ptr->tag == TW_Empty ) long tag_word = fpu_tag_word;
int regnr = 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(); FPU_stack_underflow();
stack_underflow_i(FPU_rm); FPU_stack_underflow_i(i);
return; return;
} }
if ( control_word & CW_Invalid ) 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; return;
} }
if ( sti_ptr->tag == TW_Empty ) if ( sti_tag == TAG_Empty )
{ {
if ( control_word & CW_Invalid ) 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; return;
} }
clear_C1(); clear_C1();
reg_move(st0_ptr, &t);
reg_move(sti_ptr, st0_ptr); reg_copy(st0_ptr, &t);
reg_move(&t, sti_ptr); 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_() void ffree_()
{ {
/* ffree st(i) */ /* ffree st(i) */
st(FPU_rm).tag = TW_Empty; FPU_settagi(FPU_rm, TAG_Empty);
} }
void ffreep() void ffreep()
{ {
/* ffree st(i) + pop - unofficial code */ /* ffree st(i) + pop - unofficial code */
st(FPU_rm).tag = TW_Empty; FPU_settagi(FPU_rm, TAG_Empty);
pop(); FPU_pop();
} }
void fst_i_() void fst_i_()
{ {
/* fst st(i) */ /* fst st(i) */
reg_move(&st(0), &st(FPU_rm)); FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm);
} }
void fstp_i() void fstp_i()
{ {
/* fstp st(i) */ /* fstp st(i) */
reg_move(&st(0), &st(FPU_rm)); FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm);
pop(); FPU_pop();
} }
/*---------------------------------------------------------------------------+ /*---------------------------------------------------------------------------+
| fpu_emu.h | | fpu_emu.h |
| | | |
| Copyright (C) 1992,1993,1994 | | Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | Australia. E-mail billm@suburbia.net |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -11,14 +11,6 @@ ...@@ -11,14 +11,6 @@
#ifndef _FPU_EMU_H_ #ifndef _FPU_EMU_H_
#define _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, * Define PECULIAR_486 to get a closer approximation to 80486 behaviour,
* rather than behaviour which appears to be cleaner. * rather than behaviour which appears to be cleaner.
...@@ -38,28 +30,51 @@ ...@@ -38,28 +30,51 @@
#define EXP_BIAS Const(0) #define EXP_BIAS Const(0)
#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */ #define EXP_OVER Const(0x4000) /* smallest invalid large exponent */
#define EXP_UNDER Const(-0x3fff) /* largest invalid small 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_Infinity EXP_OVER
#define EXP_NaN 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_POS Const(0)
#define SIGN_NEG Const(1) #define SIGN_NEG Const(0x80)
#define SIGN_Positive Const(0)
#define SIGN_Negative Const(0x8000)
/* Keep the order TW_Valid, TW_Zero, TW_Denormal */
#define TW_Valid Const(0) /* valid */ /* Keep the order TAG_Valid, TAG_Zero, TW_Denormal */
#define TW_Zero Const(1) /* zero */
/* The following fold to 2 (Special) in the Tag Word */ /* 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_Infinity Const(5) /* + or - infinity */
#define TW_NaN Const(6) /* Not a Number */ #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). */
/* A few flags (must be >= 0x10). */
#define REV 0x10
#define DEST_RM 0x20
#define LOADED 0x40
#define TW_Empty Const(7) /* empty */ #define FPU_Exception Const(0x80000000) /* Added to tag returns. */
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <asm/sigcontext.h> /* for struct _fpstate */ #include "fpu_system.h"
#include <asm/math_emu.h>
#include <asm/sigcontext.h> /* for struct _fpstate */
#include <asm/math_emu.h>
#include <linux/linkage.h> #include <linux/linkage.h>
/* /*
...@@ -67,7 +82,7 @@ ...@@ -67,7 +82,7 @@
*/ */
#ifdef RE_ENTRANT_CHECKING #ifdef RE_ENTRANT_CHECKING
extern char emulating; extern u_char emulating;
# define RE_ENTRANT_CHECK_OFF emulating = 0 # define RE_ENTRANT_CHECK_OFF emulating = 0
# define RE_ENTRANT_CHECK_ON emulating = 1 # define RE_ENTRANT_CHECK_ON emulating = 1
#else #else
...@@ -97,18 +112,24 @@ extern char emulating; ...@@ -97,18 +112,24 @@ extern char emulating;
struct address { struct address {
unsigned int offset; unsigned int offset;
unsigned short selector; unsigned int selector:16;
unsigned short opcode:11, unsigned int opcode:11;
empty:5; unsigned int empty:5;
};
struct fpu__reg {
unsigned sigl;
unsigned sigh;
short exp;
}; };
typedef void (*FUNC)(void); typedef void (*FUNC)(void);
typedef struct fpu_reg FPU_REG; typedef struct fpu__reg FPU_REG;
typedef void (*FUNC_ST0)(FPU_REG *st0_ptr); typedef void (*FUNC_ST0)(FPU_REG *st0_ptr, u_char st0_tag);
typedef struct { unsigned char address_size, operand_size, segment; } typedef struct { u_char address_size, operand_size, segment; }
overrides; overrides;
/* This structure is 32 bits: */ /* This structure is 32 bits: */
typedef struct { overrides override; 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 /* PROTECTED has a restricted meaning in the emulator; it is used
to signal that the emulator needs to do special things to ensure to signal that the emulator needs to do special things to ensure
that protection is respected in a segmented model. */ that protection is respected in a segmented model. */
...@@ -117,27 +138,50 @@ typedef struct { overrides override; ...@@ -117,27 +138,50 @@ typedef struct { overrides override;
#define VM86 SIXTEEN #define VM86 SIXTEEN
#define PM16 (SIXTEEN | PROTECTED) #define PM16 (SIXTEEN | PROTECTED)
#define SEG32 PROTECTED #define SEG32 PROTECTED
extern unsigned char const data_sizes_16[32]; extern u_char const data_sizes_16[32];
#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 * ((top+x) & 7) )) )
#define st(x) ( regs[((top+x) &7 )] ) #define STACK_OVERFLOW (FPU_stackoverflow(&st_new_ptr))
#define NOT_EMPTY(i) (!FPU_empty_i(i))
#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty) #define NOT_EMPTY_ST0 (st0_tag ^ TAG_Empty)
#define NOT_EMPTY(i) (st(i).tag != TW_Empty)
#define NOT_EMPTY_ST0 (st0_tag ^ TW_Empty)
#define pop() { regs[(top++ & 7 )].tag = TW_Empty; } #define poppop() { FPU_pop(); FPU_pop(); }
#define poppop() { regs[((top + 1) & 7 )].tag \
= regs[(top & 7 )].tag = TW_Empty; \
top += 2; }
/* push() does not affect the tags */ /* push() does not affect the tags */
#define push() { top--; } #define push() { 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)
#include "fpu_proto.h"
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 reg_move(x, y) { \ #define isdenormal(ptr) (exponent(ptr) == EXP_BIAS+EXP_UNDER)
*(short *)&((y)->sign) = *(const short *)&((x)->sign); \
*(long *)&((y)->exp) = *(const long *)&((x)->exp); \
*(long long *)&((y)->sigl) = *(const long long *)&((x)->sigl); }
#define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] ) #define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] )
...@@ -145,24 +189,26 @@ extern unsigned char const data_sizes_16[32]; ...@@ -145,24 +189,26 @@ extern unsigned char const data_sizes_16[32];
/*----- Prototypes for functions written in assembler -----*/ /*----- Prototypes for functions written in assembler -----*/
/* extern void reg_move(FPU_REG *a, FPU_REG *b); */ /* extern void reg_move(FPU_REG *a, FPU_REG *b); */
asmlinkage void normalize(FPU_REG *x); asmlinkage int FPU_normalize(FPU_REG *x);
asmlinkage void normalize_nuo(FPU_REG *x); asmlinkage int FPU_normalize_nuo(FPU_REG *x);
asmlinkage int reg_div(FPU_REG const *arg1, FPU_REG const *arg2, asmlinkage int FPU_u_sub(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w); FPU_REG *answ, unsigned int control_w, u_char sign,
asmlinkage int reg_u_sub(FPU_REG const *arg1, FPU_REG const *arg2, int expa, int expb);
FPU_REG *answ, unsigned int control_w); asmlinkage int FPU_u_mul(FPU_REG const *arg1, FPU_REG const *arg2,
asmlinkage int reg_u_mul(FPU_REG const *arg1, FPU_REG const *arg2, FPU_REG *answ, unsigned int control_w, u_char sign,
FPU_REG *answ, unsigned int control_w); int expon);
asmlinkage int reg_u_div(FPU_REG const *arg1, FPU_REG const *arg2, asmlinkage int FPU_u_div(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w); FPU_REG *answ, unsigned int control_w, u_char sign);
asmlinkage int reg_u_add(FPU_REG const *arg1, FPU_REG const *arg2, asmlinkage int FPU_u_add(FPU_REG const *arg1, FPU_REG const *arg2,
FPU_REG *answ, unsigned int control_w); FPU_REG *answ, unsigned int control_w, u_char sign,
asmlinkage int wm_sqrt(FPU_REG *n, unsigned int control_w); int expa, int expb);
asmlinkage unsigned shrx(void *l, unsigned x); asmlinkage int wm_sqrt(FPU_REG *n, int dummy1, int dummy2,
asmlinkage unsigned shrxs(void *v, unsigned x); unsigned int control_w, u_char sign);
asmlinkage unsigned long div_small(unsigned long long *x, unsigned long y); asmlinkage unsigned FPU_shrx(void *l, unsigned x);
asmlinkage void round_reg(FPU_REG *arg, unsigned int extent, asmlinkage unsigned FPU_shrxs(void *v, unsigned x);
unsigned int control_w); 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 #ifndef MAKING_PROTO
#include "fpu_proto.h" #include "fpu_proto.h"
......
This diff is collapsed.
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
| | | |
| Implement a few FPU instructions. | | Implement a few FPU instructions. |
| | | |
| Copyright (C) 1992,1993,1994 | | Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | Australia. E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -17,102 +17,116 @@ ...@@ -17,102 +17,116 @@
#include "reg_constant.h" #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(); clear_C1();
} }
else 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(); clear_C1();
} }
else 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); setcc(SW_C3);
break; break;
case TW_Valid: case TAG_Valid:
if (st0_ptr->sign == SIGN_POS) if (getsign(st0_ptr) == SIGN_POS)
setcc(0); setcc(0);
else else
setcc(SW_C0); setcc(SW_C0);
break;
#ifdef DENORM_OPERAND case TAG_Special:
if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) 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 #ifdef PECULIAR_486
/* This is weird! */ /* This is weird! */
if (st0_ptr->sign == SIGN_POS) if (getsign(st0_ptr) == SIGN_POS)
setcc(SW_C3); setcc(SW_C3);
#endif PECULIAR_486 #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; break;
case TW_Empty: case TAG_Empty:
setcc(SW_C0|SW_C2|SW_C3); setcc(SW_C0|SW_C2|SW_C3);
EXCEPTION(EX_StackUnder); EXCEPTION(EX_StackUnder);
break; 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; int c = 0;
switch (st0_ptr->tag) switch (st0tag)
{ {
case TW_Empty: case TAG_Empty:
c = SW_C3|SW_C0; c = SW_C3|SW_C0;
break; break;
case TW_Zero: case TAG_Zero:
c = SW_C3; c = SW_C3;
break; break;
case TW_Valid: case TAG_Valid:
/* This will need to be changed if TW_Denormal is ever used. */ c = SW_C2;
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;
break; 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; c |= SW_C1;
setcc(c); setcc(c);
} }
...@@ -123,7 +137,7 @@ static FUNC_ST0 const fp_etc_table[] = { ...@@ -123,7 +137,7 @@ static FUNC_ST0 const fp_etc_table[] = {
ftst_, fxam, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal 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());
} }
#ifndef _FPU_PROTO_H
#define _FPU_PROTO_H
/* errors.c */ /* errors.c */
extern void Un_impl(void); extern void Un_impl(void);
extern void FPU_illegal(void); extern void FPU_illegal(void);
extern void emu_printall(void); extern void FPU_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 int set_precision_flag(int flags);
asmlinkage void FPU_exception(int n); asmlinkage void FPU_exception(int n);
asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest); extern int real_1op_NaN(FPU_REG *a);
asmlinkage int arith_invalid(FPU_REG *dest); extern int real_2op_NaN(FPU_REG const *b, u_char tagb, int deststnr,
asmlinkage int divide_by_zero(int sign, FPU_REG *dest); FPU_REG const *defaultNaN);
asmlinkage void set_precision_flag_up(void); extern int arith_invalid(int deststnr);
asmlinkage void set_precision_flag_down(void); extern int FPU_divide_by_zero(int deststnr, u_char sign);
asmlinkage int denormal_operand(void); extern int set_precision_flag(int flags);
asmlinkage int arith_overflow(FPU_REG *dest); extern void set_precision_flag_up(void);
asmlinkage int arith_underflow(FPU_REG *dest); extern void set_precision_flag_down(void);
extern int denormal_operand(void);
extern int arith_overflow(FPU_REG *dest);
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 */ /* fpu_arith.c */
extern void fadd__(void); extern void fadd__(void);
extern void fmul__(void); extern void fmul__(void);
...@@ -36,7 +40,6 @@ extern void fsubrp(void); ...@@ -36,7 +40,6 @@ extern void fsubrp(void);
extern void fsubp_(void); extern void fsubp_(void);
extern void fdivrp(void); extern void fdivrp(void);
extern void fdivp_(void); extern void fdivp_(void);
/* fpu_aux.c */ /* fpu_aux.c */
extern void fclex(void); extern void fclex(void);
extern void finit(void); extern void finit(void);
...@@ -49,89 +52,92 @@ extern void ffree_(void); ...@@ -49,89 +52,92 @@ extern void ffree_(void);
extern void ffreep(void); extern void ffreep(void);
extern void fst_i_(void); extern void fst_i_(void);
extern void fstp_i(void); extern void fstp_i(void);
/* fpu_entry.c */ /* fpu_entry.c */
asmlinkage void math_emulate(long arg); extern void math_emulate(long arg);
extern void math_abort(struct info *info, unsigned int signal); extern void math_abort(struct info *info, unsigned int signal);
/* fpu_etc.c */ /* 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 */ /* fpu_trig.c */
extern void convert_l2reg(long const *arg, FPU_REG *dest); extern void FPU_triga(void);
extern void trig_a(void); extern void FPU_trigb(void);
extern void trig_b(void);
/* get_address.c */ /* get_address.c */
extern void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, extern void *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
struct address *addr, struct address *addr, fpu_addr_modes addr_modes);
fpu_addr_modes); extern void *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
extern void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, struct address *addr, fpu_addr_modes addr_modes);
struct address *addr,
fpu_addr_modes);
/* load_store.c */ /* load_store.c */
extern int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, extern int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
void *address); void *data_address);
/* poly_2xm1.c */ /* 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 */ /* 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 */ /* poly_l2.c */
extern void poly_l2(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(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result); extern int poly_l2p1(u_char s0, u_char s1, FPU_REG *r0, FPU_REG *r1,
FPU_REG *d);
/* poly_sin.c */ /* poly_sin.c */
extern void poly_sine(FPU_REG const *arg, FPU_REG *result); extern void poly_sine(FPU_REG *st0_ptr);
extern void poly_cos(FPU_REG const *arg, FPU_REG *result); extern void poly_cos(FPU_REG *st0_ptr);
/* poly_tan.c */ /* 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 */ /* reg_add_sub.c */
extern int reg_add(FPU_REG const *a, FPU_REG const *b, extern int FPU_add(FPU_REG const *b, u_char tagb, int destrnr, int control_w);
FPU_REG *dest, int control_w); extern int FPU_sub(int flags, int rm, int control_w);
extern int reg_sub(FPU_REG const *a, FPU_REG const *b,
FPU_REG *dest, int control_w);
/* reg_compare.c */ /* reg_compare.c */
extern int compare(FPU_REG const *b); extern int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag);
extern int compare_st_data(FPU_REG const *b);
extern void fcom_st(void); extern void fcom_st(void);
extern void fcompst(void); extern void fcompst(void);
extern void fcompp(void); extern void fcompp(void);
extern void fucom_(void); extern void fucom_(void);
extern void fucomp(void); extern void fucomp(void);
extern void fucompp(void); extern void fucompp(void);
/* reg_constant.c */ /* reg_constant.c */
extern void fconst(void); extern void fconst(void);
/* reg_ld_str.c */ /* reg_ld_str.c */
extern int reg_load_extended(long double *addr, FPU_REG *loaded_data); extern int FPU_load_extended(long double *s, int stnr);
extern int reg_load_double(double *dfloat, FPU_REG *loaded_data); extern int FPU_load_double(double *dfloat, FPU_REG *loaded_data);
extern int reg_load_single(float *single, FPU_REG *loaded_data); extern int FPU_load_single(float *single, FPU_REG *loaded_data);
extern void reg_load_int64(long long *_s, FPU_REG *loaded_data); extern int FPU_load_int64(long long *_s);
extern void reg_load_int32(long *_s, FPU_REG *loaded_data); extern int FPU_load_int32(long *_s, FPU_REG *loaded_data);
extern void reg_load_int16(short *_s, FPU_REG *loaded_data); extern int FPU_load_int16(short *_s, FPU_REG *loaded_data);
extern void reg_load_bcd(char *s, FPU_REG *loaded_data); extern int FPU_load_bcd(u_char *s);
extern int reg_store_extended(long double *d, FPU_REG *st0_ptr); extern int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag,
extern int reg_store_double(double *dfloat, FPU_REG *st0_ptr); long double *d);
extern int reg_store_single(float *single, FPU_REG *st0_ptr); extern int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double *dfloat);
extern int reg_store_int64(long long *d, FPU_REG *st0_ptr); extern int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float *single);
extern int reg_store_int32(long *d, FPU_REG *st0_ptr); extern int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag, long long *d);
extern int reg_store_int16(short *d, FPU_REG *st0_ptr); extern int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long *d);
extern int reg_store_bcd(char *d, FPU_REG *st0_ptr); extern int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short *d);
extern int round_to_int(FPU_REG *r); extern int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char *d);
extern char *fldenv(fpu_addr_modes addr_modes, char *address); extern int FPU_round_to_int(FPU_REG *r, u_char tag);
extern void frstor(fpu_addr_modes addr_modes, char *address); extern u_char *fldenv(fpu_addr_modes addr_modes, u_char *s);
extern unsigned short tag_word(void); extern void frstor(fpu_addr_modes addr_modes, u_char *data_address);
extern char *fstenv(fpu_addr_modes addr_modes, char *address); extern u_char *fstenv(fpu_addr_modes addr_modes, u_char *d);
extern void fsave(fpu_addr_modes addr_modes, char *address); extern void fsave(fpu_addr_modes addr_modes, u_char *data_address);
extern int FPU_tagof(FPU_REG *ptr);
/* reg_mul.c */ /* reg_mul.c */
extern int reg_mul(FPU_REG const *a, FPU_REG const *b, extern int FPU_mul(FPU_REG const *b, u_char tagb, int deststnr, int control_w);
FPU_REG *dest, unsigned 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 */
/*---------------------------------------------------------------------------+ /*---------------------------------------------------------------------------+
| fpu_system.h | | fpu_system.h |
| | | |
| Copyright (C) 1992,1994 | | Copyright (C) 1992,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | Australia. E-mail billm@suburbia.net |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -18,19 +18,19 @@ ...@@ -18,19 +18,19 @@
/* This sets the pointer FPU_info to point to the argument part /* This sets the pointer FPU_info to point to the argument part
of the stack frame of math_emulate() */ of the stack frame of math_emulate() */
#define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg #define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg
#define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3]) #define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3])
#define SEG_D_SIZE(x) ((x).b & (3 << 21)) #define SEG_D_SIZE(x) ((x).b & (3 << 21))
#define SEG_G_BIT(x) ((x).b & (1 << 23)) #define SEG_G_BIT(x) ((x).b & (1 << 23))
#define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1) #define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1)
#define SEG_286_MODE(x) ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23))) #define SEG_286_MODE(x) ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23)))
#define SEG_BASE_ADDR(s) (((s).b & 0xff000000) \ #define SEG_BASE_ADDR(s) (((s).b & 0xff000000) \
| (((s).b & 0xff) << 16) | ((s).a >> 16)) | (((s).b & 0xff) << 16) | ((s).a >> 16))
#define SEG_LIMIT(s) (((s).b & 0xff0000) | ((s).a & 0xffff)) #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_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_WRITE_PERM(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 9))
#define SEG_EXPAND_DOWN(s) (((s).b & ((1 << 11) | (1 << 10))) \ #define SEG_EXPAND_DOWN(s) (((s).b & ((1 << 11) | (1 << 10))) \
== (1 << 10)) == (1 << 10))
#define I387 (current->tss.i387) #define I387 (current->tss.i387)
...@@ -48,23 +48,24 @@ ...@@ -48,23 +48,24 @@
/* nz if ip_offset and cs_selector are not to be set for the current /* nz if ip_offset and cs_selector are not to be set for the current
instruction. */ instruction. */
#define no_ip_update (((char *)&(I387.soft.twd))[0]) #define no_ip_update (*(u_char *)&(I387.soft.no_update))
#define FPU_rm (((unsigned char *)&(I387.soft.twd))[1]) #define FPU_rm (*(u_char *)&(I387.soft.rm))
/* Number of bytes of data which can be legally accessed by the current /* 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. */ 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 control_word (I387.soft.cwd)
#define regs (I387.soft.regs) #define fpu_tag_word (I387.soft.twd)
#define top (I387.soft.top) #define registers (I387.soft.st_space)
#define top (I387.soft.ftop)
#define instruction_address (*(struct address *)&I387.soft.fip) #define instruction_address (*(struct address *)&I387.soft.fip)
#define operand_address (*(struct address *)&I387.soft.foo) #define operand_address (*(struct address *)&I387.soft.foo)
#define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \ #define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \
math_abort(FPU_info,SIGSEGV) math_abort(FPU_info,SIGSEGV)
#undef FPU_IGNORE_CODE_SEGV #undef FPU_IGNORE_CODE_SEGV
#ifdef FPU_IGNORE_CODE_SEGV #ifdef FPU_IGNORE_CODE_SEGV
...@@ -80,4 +81,7 @@ ...@@ -80,4 +81,7 @@
#define FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z) #define FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z)
#endif #endif
#define FPU_get_user(x,y) get_user((x),(y))
#define FPU_put_user(x,y) put_user((x),(y))
#endif #endif
/*---------------------------------------------------------------------------+
| fpu_tags.c |
| |
| Set FPU register tags. |
| |
| Copyright (C) 1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| E-mail billm@jacobi.maths.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#include "fpu_emu.h"
#include "fpu_system.h"
#include "exception.h"
void FPU_pop(void)
{
fpu_tag_word |= 3 << ((top & 7)*2);
top++;
}
int FPU_gettag0(void)
{
return (fpu_tag_word >> ((top & 7)*2)) & 3;
}
int FPU_gettagi(int stnr)
{
return (fpu_tag_word >> (((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 = 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+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 = (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 >> (((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 = top;
regnr &= 7;
reg_copy(r, &st(0));
fpu_tag_word &= ~(3 << (regnr*2));
fpu_tag_word |= (tag & 3) << (regnr*2);
}
This diff is collapsed.
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
| | | |
| Get the effective address from an FPU instruction. | | 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, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | Australia. E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -41,7 +41,7 @@ static int reg_offset[] = { ...@@ -41,7 +41,7 @@ static int reg_offset[] = {
offsetof(struct info,___edi) 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[] = { static int reg_offset_vm86[] = {
offsetof(struct info,___cs), offsetof(struct info,___cs),
...@@ -54,7 +54,7 @@ static int reg_offset_vm86[] = { ...@@ -54,7 +54,7 @@ static int reg_offset_vm86[] = {
}; };
#define VM86_REG_(x) (*(unsigned short *) \ #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. */ /* These are dummy, fs and gs are not saved on the stack. */
#define ___FS ___ds #define ___FS ___ds
...@@ -71,18 +71,18 @@ static int reg_offset_pm[] = { ...@@ -71,18 +71,18 @@ static int reg_offset_pm[] = {
}; };
#define PM_REG_(x) (*(unsigned short *) \ #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 */ /* Decode the SIB byte. This function assumes mod != 0 */
static int sib(int mod, unsigned long *fpu_eip) static int sib(int mod, unsigned long *fpu_eip)
{ {
unsigned char ss,index,base; u_char ss,index,base;
long offset; long offset;
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1); FPU_code_verify_area(1);
get_user(base, (unsigned char *) (*fpu_eip)); /* The SIB byte */ FPU_get_user(base, (u_char *) (*fpu_eip)); /* The SIB byte */
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
(*fpu_eip)++; (*fpu_eip)++;
ss = base >> 6; ss = base >> 6;
...@@ -112,7 +112,7 @@ static int sib(int mod, unsigned long *fpu_eip) ...@@ -112,7 +112,7 @@ static int sib(int mod, unsigned long *fpu_eip)
long displacement; long displacement;
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1); FPU_code_verify_area(1);
get_user(displacement, (signed char *) (*fpu_eip)); FPU_get_user(displacement, (signed char *) (*fpu_eip));
offset += displacement; offset += displacement;
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
(*fpu_eip)++; (*fpu_eip)++;
...@@ -123,7 +123,7 @@ static int sib(int mod, unsigned long *fpu_eip) ...@@ -123,7 +123,7 @@ static int sib(int mod, unsigned long *fpu_eip)
long displacement; long displacement;
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(4); FPU_code_verify_area(4);
get_user(displacement, (signed long *) (*fpu_eip)); FPU_get_user(displacement, (long *) (*fpu_eip));
offset += displacement; offset += displacement;
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
(*fpu_eip) += 4; (*fpu_eip) += 4;
...@@ -133,7 +133,7 @@ static int sib(int mod, unsigned long *fpu_eip) ...@@ -133,7 +133,7 @@ static int sib(int mod, unsigned long *fpu_eip)
} }
static unsigned long vm86_segment(unsigned char segment, static unsigned long vm86_segment(u_char segment,
unsigned short *selector) unsigned short *selector)
{ {
segment--; segment--;
...@@ -150,7 +150,7 @@ static unsigned long vm86_segment(unsigned char segment, ...@@ -150,7 +150,7 @@ static unsigned long vm86_segment(unsigned char segment,
/* This should work for 16 and 32 bit protected mode. */ /* This should work for 16 and 32 bit protected mode. */
static long pm_address(unsigned char FPU_modrm, unsigned char segment, static long pm_address(u_char FPU_modrm, u_char segment,
unsigned short *selector, long offset) unsigned short *selector, long offset)
{ {
struct desc_struct descriptor; struct desc_struct descriptor;
...@@ -233,12 +233,11 @@ static long pm_address(unsigned char FPU_modrm, unsigned char segment, ...@@ -233,12 +233,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, struct address *addr,
/* unsigned short *selector, unsigned long *offset, */
fpu_addr_modes addr_modes) fpu_addr_modes addr_modes)
{ {
unsigned char mod; u_char mod;
unsigned rm = FPU_modrm & 7; unsigned rm = FPU_modrm & 7;
long *cpu_reg_ptr; long *cpu_reg_ptr;
int address = 0; /* Initialized just to stop compiler warnings. */ int address = 0; /* Initialized just to stop compiler warnings. */
...@@ -270,7 +269,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, ...@@ -270,7 +269,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
/* Special case: disp32 */ /* Special case: disp32 */
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(4); FPU_code_verify_area(4);
get_user(address, (unsigned long *) (*fpu_eip)); FPU_get_user(address, (unsigned long *) (*fpu_eip));
(*fpu_eip) += 4; (*fpu_eip) += 4;
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
addr->offset = address; addr->offset = address;
...@@ -287,7 +286,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, ...@@ -287,7 +286,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
/* 8 bit signed displacement */ /* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1); FPU_code_verify_area(1);
get_user(address, (signed char *) (*fpu_eip)); FPU_get_user(address, (signed char *) (*fpu_eip));
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
(*fpu_eip)++; (*fpu_eip)++;
break; break;
...@@ -295,7 +294,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, ...@@ -295,7 +294,7 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
/* 32 bit displacement */ /* 32 bit displacement */
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(4); FPU_code_verify_area(4);
get_user(address, (long *) (*fpu_eip)); FPU_get_user(address, (long *) (*fpu_eip));
(*fpu_eip) += 4; (*fpu_eip) += 4;
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
break; break;
...@@ -329,12 +328,11 @@ void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, ...@@ -329,12 +328,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, struct address *addr,
/* unsigned short *selector, unsigned long *offset, */
fpu_addr_modes addr_modes) fpu_addr_modes addr_modes)
{ {
unsigned char mod; u_char mod;
unsigned rm = FPU_modrm & 7; unsigned rm = FPU_modrm & 7;
int address = 0; /* Default used for mod == 0 */ int address = 0; /* Default used for mod == 0 */
...@@ -358,7 +356,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, ...@@ -358,7 +356,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
/* Special case: disp16 */ /* Special case: disp16 */
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(2); FPU_code_verify_area(2);
get_user(address, (unsigned short *) (*fpu_eip)); FPU_get_user(address, (unsigned short *) (*fpu_eip));
(*fpu_eip) += 2; (*fpu_eip) += 2;
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
goto add_segment; goto add_segment;
...@@ -368,7 +366,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, ...@@ -368,7 +366,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
/* 8 bit signed displacement */ /* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1); FPU_code_verify_area(1);
get_user(address, (signed char *) (*fpu_eip)); FPU_get_user(address, (signed char *) (*fpu_eip));
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
(*fpu_eip)++; (*fpu_eip)++;
break; break;
...@@ -376,7 +374,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, ...@@ -376,7 +374,7 @@ void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
/* 16 bit displacement */ /* 16 bit displacement */
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(2); FPU_code_verify_area(2);
get_user(address, (unsigned short *) (*fpu_eip)); FPU_get_user(address, (unsigned short *) (*fpu_eip));
(*fpu_eip) += 2; (*fpu_eip) += 2;
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
break; break;
......
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
| This file contains most of the code to interpret the FPU instructions | | This file contains most of the code to interpret the FPU instructions |
| which load and store from user memory. | | which load and store from user memory. |
| | | |
| Copyright (C) 1992,1993,1994 | | Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | Australia. E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -32,10 +32,10 @@ ...@@ -32,10 +32,10 @@
#define _PUSH_ 3 /* Need to check for space to push onto stack */ #define _PUSH_ 3 /* Need to check for space to push onto stack */
#define _null_ 4 /* Function illegal or not implemented */ #define _null_ 4 /* Function illegal or not implemented */
#define pop_0() { st0_ptr->tag = TW_Empty; top++; } #define pop_0() { FPU_settag0(TAG_Empty); top++; }
static unsigned char const type_table[32] = { static u_char const type_table[32] = {
_PUSH_, _PUSH_, _PUSH_, _PUSH_, _PUSH_, _PUSH_, _PUSH_, _PUSH_,
_null_, _null_, _null_, _null_, _null_, _null_, _null_, _null_,
_REG0_, _REG0_, _REG0_, _REG0_, _REG0_, _REG0_, _REG0_, _REG0_,
...@@ -46,25 +46,27 @@ static unsigned char const type_table[32] = { ...@@ -46,25 +46,27 @@ static unsigned char const type_table[32] = {
_NONE_, _REG0_, _NONE_, _REG0_ _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, 0, 0, 0, 0,
4, 4, 8, 2, 4, 4, 8, 2, 4, 4, 8, 2, 4, 4, 8, 2,
14, 0, 94, 10, 2, 10, 0, 8, 14, 0, 94, 10, 2, 10, 0, 8,
14, 0, 94, 10, 2, 10, 2, 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, 0, 0, 0, 0,
4, 4, 8, 2, 4, 4, 8, 2, 4, 4, 8, 2, 4, 4, 8, 2,
28, 0,108, 10, 2, 10, 0, 8, 28, 0,108, 10, 2, 10, 0, 8,
28, 0,108, 10, 2, 10, 2, 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) void *data_address)
{ {
FPU_REG loaded_data; FPU_REG loaded_data;
FPU_REG *st0_ptr; 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. */ st0_ptr = NULL; /* Initialized just to stop compiler warnings. */
...@@ -93,13 +95,14 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, ...@@ -93,13 +95,14 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
case _REG0_: case _REG0_:
st0_ptr = &st(0); /* Some of these instructions pop after st0_ptr = &st(0); /* Some of these instructions pop after
storing */ storing */
st0_tag = FPU_gettag0();
break; break;
case _PUSH_: case _PUSH_:
{ {
st0_ptr = &st(-1); if ( FPU_gettagi(-1) != TAG_Empty )
if ( st0_ptr->tag != TW_Empty ) { FPU_stack_overflow(); return 0; }
{ stack_overflow(); return 0; }
top--; top--;
st0_ptr = &st(0);
} }
break; break;
case _null_: case _null_:
...@@ -116,92 +119,97 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, ...@@ -116,92 +119,97 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
{ {
case 000: /* fld m32real */ case 000: /* fld m32real */
clear_C1(); clear_C1();
reg_load_single((float *)data_address, &loaded_data); loaded_tag = FPU_load_single((float *)data_address, &loaded_data);
if ( (loaded_data.tag == TW_NaN) && if ( (loaded_tag == TAG_Special)
real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) && isNaN(&loaded_data)
&& (real_1op_NaN(&loaded_data) < 0) )
{ {
top++; top++;
break; break;
} }
reg_move(&loaded_data, st0_ptr); FPU_copy_to_reg0(&loaded_data, loaded_tag);
break; break;
case 001: /* fild m32int */ case 001: /* fild m32int */
clear_C1(); 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; break;
case 002: /* fld m64real */ case 002: /* fld m64real */
clear_C1(); clear_C1();
reg_load_double((double *)data_address, &loaded_data); loaded_tag = FPU_load_double((double *)data_address, &loaded_data);
if ( (loaded_data.tag == TW_NaN) && if ( (loaded_tag == TAG_Special)
real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) && isNaN(&loaded_data)
&& (real_1op_NaN(&loaded_data) < 0) )
{ {
top++; top++;
break; break;
} }
reg_move(&loaded_data, st0_ptr); FPU_copy_to_reg0(&loaded_data, loaded_tag);
break; break;
case 003: /* fild m16int */ case 003: /* fild m16int */
clear_C1(); 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; break;
case 010: /* fst m32real */ case 010: /* fst m32real */
clear_C1(); clear_C1();
reg_store_single((float *)data_address, st0_ptr); FPU_store_single(st0_ptr, st0_tag, (float *)data_address);
break; break;
case 011: /* fist m32int */ case 011: /* fist m32int */
clear_C1(); clear_C1();
reg_store_int32((long *)data_address, st0_ptr); FPU_store_int32(st0_ptr, st0_tag, (long *)data_address);
break; break;
case 012: /* fst m64real */ case 012: /* fst m64real */
clear_C1(); clear_C1();
reg_store_double((double *)data_address, st0_ptr); FPU_store_double(st0_ptr, st0_tag, (double *)data_address);
break; break;
case 013: /* fist m16int */ case 013: /* fist m16int */
clear_C1(); clear_C1();
reg_store_int16((short *)data_address, st0_ptr); FPU_store_int16(st0_ptr, st0_tag, (short *)data_address);
break; break;
case 014: /* fstp m32real */ case 014: /* fstp m32real */
clear_C1(); 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 pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */ (see the 80486 manual p16-28) */
break; break;
case 015: /* fistp m32int */ case 015: /* fistp m32int */
clear_C1(); 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 pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */ (see the 80486 manual p16-28) */
break; break;
case 016: /* fstp m64real */ case 016: /* fstp m64real */
clear_C1(); 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 pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */ (see the 80486 manual p16-28) */
break; break;
case 017: /* fistp m16int */ case 017: /* fistp m16int */
clear_C1(); 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 pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */ (see the 80486 manual p16-28) */
break; break;
case 020: /* fldenv m14/28byte */ 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 /* Ensure that the values just loaded are not changed by
fix-up operations. */ fix-up operations. */
return 1; return 1;
case 022: /* frstor m94/108byte */ 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 /* Ensure that the values just loaded are not changed by
fix-up operations. */ fix-up operations. */
return 1; return 1;
case 023: /* fbld m80dec */ case 023: /* fbld m80dec */
clear_C1(); clear_C1();
reg_load_bcd((char *)data_address, st0_ptr); loaded_tag = FPU_load_bcd((u_char *)data_address);
FPU_settag0(loaded_tag);
break; break;
case 024: /* fldcw */ case 024: /* fldcw */
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_verify_area(VERIFY_READ, data_address, 2); FPU_verify_area(VERIFY_READ, data_address, 2);
get_user(control_word, (unsigned short *) data_address); FPU_get_user(control_word, (unsigned short *) data_address);
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
if ( partial_status & ~control_word & CW_Exceptions ) if ( partial_status & ~control_word & CW_Exceptions )
partial_status |= (SW_Summary | SW_Backward); partial_status |= (SW_Summary | SW_Backward);
...@@ -213,45 +221,47 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, ...@@ -213,45 +221,47 @@ int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
return 1; return 1;
case 025: /* fld m80real */ case 025: /* fld m80real */
clear_C1(); 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; break;
case 027: /* fild m64int */ case 027: /* fild m64int */
clear_C1(); 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; break;
case 030: /* fstenv m14/28byte */ case 030: /* fstenv m14/28byte */
fstenv(addr_modes, (char *)data_address); fstenv(addr_modes, (u_char *)data_address);
return 1; return 1;
case 032: /* fsave */ case 032: /* fsave */
fsave(addr_modes, (char *)data_address); fsave(addr_modes, (u_char *)data_address);
return 1; return 1;
case 033: /* fbstp m80dec */ case 033: /* fbstp m80dec */
clear_C1(); 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 pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */ (see the 80486 manual p16-28) */
break; break;
case 034: /* fstcw m16int */ case 034: /* fstcw m16int */
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_verify_area(VERIFY_WRITE,data_address,2); FPU_verify_area(VERIFY_WRITE,data_address,2);
put_user(control_word, (unsigned short *) data_address); FPU_put_user(control_word, (unsigned short *) data_address);
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
return 1; return 1;
case 035: /* fstp m80real */ case 035: /* fstp m80real */
clear_C1(); 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 pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */ (see the 80486 manual p16-28) */
break; break;
case 036: /* fstsw m2byte */ case 036: /* fstsw m2byte */
RE_ENTRANT_CHECK_OFF; RE_ENTRANT_CHECK_OFF;
FPU_verify_area(VERIFY_WRITE,data_address,2); FPU_verify_area(VERIFY_WRITE,data_address,2);
put_user(status_word(),(unsigned short *) data_address); FPU_put_user(status_word(),(unsigned short *) data_address);
RE_ENTRANT_CHECK_ON; RE_ENTRANT_CHECK_ON;
return 1; return 1;
case 037: /* fistp m64int */ case 037: /* fistp m64int */
clear_C1(); 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 pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */ (see the 80486 manual p16-28) */
break; break;
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
| | | |
| Function to compute 2^x-1 by a polynomial approximation. | | Function to compute 2^x-1 by a polynomial approximation. |
| | | |
| Copyright (C) 1992,1993,1994 | | Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "exception.h" #include "exception.h"
#include "reg_constant.h" #include "reg_constant.h"
#include "fpu_emu.h" #include "fpu_emu.h"
#include "fpu_system.h"
#include "control_w.h" #include "control_w.h"
#include "poly.h" #include "poly.h"
...@@ -48,20 +49,19 @@ static const Xsig *shiftterm[] = { &shiftterm0, &shiftterm1, ...@@ -48,20 +49,19 @@ static const Xsig *shiftterm[] = { &shiftterm0, &shiftterm1,
/*--- poly_2xm1() -----------------------------------------------------------+ /*--- 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; long int exponent, shift;
unsigned long long Xll; unsigned long long Xll;
Xsig accumulator, Denom, argSignif; Xsig accumulator, Denom, argSignif;
u_char tag;
exponent = exponent16(arg);
exponent = arg->exp - EXP_BIAS;
#ifdef PARANOID #ifdef PARANOID
if ( (exponent >= 0) /* Don't want a |number| >= 1.0 */ if ( exponent >= 0 ) /* Don't want a |number| >= 1.0 */
|| (arg->tag != TW_Valid) )
{ {
/* Number negative, too large, or not Valid. */ /* Number negative, too large, or not Valid. */
EXCEPTION(EX_INTERNAL|0x127); EXCEPTION(EX_INTERNAL|0x127);
...@@ -94,7 +94,7 @@ int poly_2xm1(FPU_REG const *arg, FPU_REG *result) ...@@ -94,7 +94,7 @@ int poly_2xm1(FPU_REG const *arg, FPU_REG *result)
if ( exponent < -2 ) if ( exponent < -2 )
{ {
/* Shift the argument right by the required places. */ /* Shift the argument right by the required places. */
if ( shrx(&Xll, -2-exponent) >= 0x80000000U ) if ( FPU_shrx(&Xll, -2-exponent) >= 0x80000000U )
Xll++; /* round up */ Xll++; /* round up */
} }
...@@ -118,7 +118,7 @@ int poly_2xm1(FPU_REG const *arg, FPU_REG *result) ...@@ -118,7 +118,7 @@ int poly_2xm1(FPU_REG const *arg, FPU_REG *result)
exponent = 1; exponent = 1;
} }
if ( arg->sign != SIGN_POS ) if ( sign != SIGN_POS )
{ {
/* The argument is negative, use the identity: /* The argument is negative, use the identity:
f(-x) = -f(x) / (1 + f(x)) f(-x) = -f(x) / (1 + f(x))
...@@ -142,10 +142,14 @@ int poly_2xm1(FPU_REG const *arg, FPU_REG *result) ...@@ -142,10 +142,14 @@ int poly_2xm1(FPU_REG const *arg, FPU_REG *result)
/* Convert to 64 bit signed-compatible */ /* Convert to 64 bit signed-compatible */
exponent += round_Xsig(&accumulator); exponent += round_Xsig(&accumulator);
result = &st(0);
significand(result) = XSIG_LL(accumulator); significand(result) = XSIG_LL(accumulator);
result->tag = TW_Valid; setexponent16(result, exponent);
result->exp = exponent + EXP_BIAS;
result->sign = arg->sign; tag = FPU_round(result, 1, 0, FULL_PRECISION, sign);
setsign(result, sign);
FPU_settag0(tag);
return 0; return 0;
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
| | | |
| Compute the arctan of a FPU_REG, using a polynomial approximation. | | Compute the arctan of a FPU_REG, using a polynomial approximation. |
| | | |
| Copyright (C) 1992,1993,1994 | | Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "exception.h" #include "exception.h"
#include "reg_constant.h" #include "reg_constant.h"
#include "fpu_emu.h" #include "fpu_emu.h"
#include "fpu_system.h"
#include "status_w.h" #include "status_w.h"
#include "control_w.h" #include "control_w.h"
#include "poly.h" #include "poly.h"
...@@ -51,31 +52,57 @@ static const Xsig pi_signif = MK_XSIG(0xc90fdaa2, 0x2168c234, 0xc4c6628b); ...@@ -51,31 +52,57 @@ static const Xsig pi_signif = MK_XSIG(0xc90fdaa2, 0x2168c234, 0xc4c6628b);
/*--- poly_atan() -----------------------------------------------------------+ /*--- 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, u_char transformed, inverted,
sign1 = arg1->sign, sign2 = arg2->sign; sign1, sign2;
long int exponent, dummy_exp; int exponent;
Xsig accumulator, Numer, Denom, accumulatore, argSignif, long int dummy_exp;
argSq, argSqSq; 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 ( (exponent < 0) || ((exponent == 0) &&
if ( (compare(arg2) & ~COMP_Denormal) == COMP_A_lt_B ) ((st0_ptr->sigh < st1_ptr->sigh) ||
((st0_ptr->sigh == st1_ptr->sigh) &&
(st0_ptr->sigl < st1_ptr->sigl))) ) )
{ {
inverted = 1; inverted = 1;
exponent = arg1->exp - arg2->exp;
Numer.lsw = Denom.lsw = 0; Numer.lsw = Denom.lsw = 0;
XSIG_LL(Numer) = significand(arg1); XSIG_LL(Numer) = significand(st0_ptr);
XSIG_LL(Denom) = significand(arg2); XSIG_LL(Denom) = significand(st1_ptr);
} }
else else
{ {
inverted = 0; inverted = 0;
exponent = arg2->exp - arg1->exp; exponent = -exponent;
Numer.lsw = Denom.lsw = 0; Numer.lsw = Denom.lsw = 0;
XSIG_LL(Numer) = significand(arg2); XSIG_LL(Numer) = significand(st1_ptr);
XSIG_LL(Denom) = significand(arg1); XSIG_LL(Denom) = significand(st0_ptr);
} }
div_Xsig(&Numer, &Denom, &argSignif); div_Xsig(&Numer, &Denom, &argSignif);
exponent += norm_Xsig(&argSignif); exponent += norm_Xsig(&argSignif);
...@@ -189,9 +216,14 @@ void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result) ...@@ -189,9 +216,14 @@ void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result)
} }
exponent += round_Xsig(&accumulator); exponent += round_Xsig(&accumulator);
significand(result) = XSIG_LL(accumulator);
result->exp = exponent + EXP_BIAS; significand(st1_ptr) = XSIG_LL(accumulator);
result->tag = TW_Valid; setexponent16(st1_ptr, exponent);
result->sign = sign2;
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. */
} }
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
| | | |
| Compute the base 2 log of a FPU_REG, using a polynomial approximation. | | Compute the base 2 log of a FPU_REG, using a polynomial approximation. |
| | | |
| Copyright (C) 1992,1993,1994 | | Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -14,96 +14,101 @@ ...@@ -14,96 +14,101 @@
#include "exception.h" #include "exception.h"
#include "reg_constant.h" #include "reg_constant.h"
#include "fpu_emu.h" #include "fpu_emu.h"
#include "fpu_system.h"
#include "control_w.h" #include "control_w.h"
#include "poly.h" #include "poly.h"
static void log2_kernel(FPU_REG const *arg, u_char argsign,
static void log2_kernel(FPU_REG const *arg,
Xsig *accum_result, long int *expon); Xsig *accum_result, long int *expon);
/*--- poly_l2() -------------------------------------------------------------+ /*--- poly_l2() -------------------------------------------------------------+
| Base 2 logarithm by a polynomial approximation. | | 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; long int exponent, expon, expon_expon;
Xsig accumulator, expon_accum, yaccum; Xsig accumulator, expon_accum, yaccum;
char sign; u_char sign, argsign;
FPU_REG x; FPU_REG x;
int tag;
exponent = exponent16(st0_ptr);
exponent = arg->exp - EXP_BIAS; /* From st0_ptr, make a number > sqrt(2)/2 and < sqrt(2) */
if ( st0_ptr->sigh > (unsigned)0xb504f334 )
/* From arg, make a number > sqrt(2)/2 and < sqrt(2) */
if ( arg->sigh > (unsigned)0xb504f334 )
{ {
/* Treat as sqrt(2)/2 < arg < 1 */ /* Treat as sqrt(2)/2 < st0_ptr < 1 */
significand(&x) = - significand(arg); significand(&x) = - significand(st0_ptr);
x.sign = SIGN_NEG; setexponent16(&x, -1);
x.tag = TW_Valid;
x.exp = EXP_BIAS-1;
exponent++; exponent++;
normalize(&x); argsign = SIGN_NEG;
} }
else else
{ {
/* Treat as 1 <= arg < sqrt(2) */ /* Treat as 1 <= st0_ptr < sqrt(2) */
x.sigh = arg->sigh - 0x80000000; x.sigh = st0_ptr->sigh - 0x80000000;
x.sigl = arg->sigl; x.sigl = st0_ptr->sigl;
x.sign = SIGN_POS; setexponent16(&x, 0);
x.tag = TW_Valid; argsign = SIGN_POS;
x.exp = EXP_BIAS;
normalize(&x);
} }
tag = FPU_normalize_nuo(&x);
if ( x.tag == TW_Zero ) if ( tag == TAG_Zero )
{ {
expon = 0; expon = 0;
accumulator.msw = accumulator.midw = accumulator.lsw = 0; accumulator.msw = accumulator.midw = accumulator.lsw = 0;
} }
else else
{ {
log2_kernel(&x, &accumulator, &expon); log2_kernel(&x, argsign, &accumulator, &expon);
} }
sign = exponent < 0; if ( exponent < 0 )
if ( sign ) exponent = -exponent; {
sign = SIGN_NEG;
exponent = -exponent;
}
else
sign = SIGN_POS;
expon_accum.msw = exponent; expon_accum.midw = expon_accum.lsw = 0; expon_accum.msw = exponent; expon_accum.midw = expon_accum.lsw = 0;
if ( exponent ) if ( exponent )
{ {
expon_expon = 31 + norm_Xsig(&expon_accum); expon_expon = 31 + norm_Xsig(&expon_accum);
shr_Xsig(&accumulator, expon_expon - expon); shr_Xsig(&accumulator, expon_expon - expon);
if ( sign ^ (x.sign == SIGN_NEG) ) if ( sign ^ argsign )
negate_Xsig(&accumulator); negate_Xsig(&accumulator);
add_Xsig_Xsig(&accumulator, &expon_accum); add_Xsig_Xsig(&accumulator, &expon_accum);
} }
else else
{ {
expon_expon = expon; 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); mul_Xsig_Xsig(&accumulator, &yaccum);
expon_expon += round_Xsig(&accumulator); expon_expon += round_Xsig(&accumulator);
if ( accumulator.msw == 0 ) if ( accumulator.msw == 0 )
{ {
reg_move(&CONST_Z, y); FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
} return;
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;
} }
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; return;
} }
...@@ -111,47 +116,62 @@ void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result) ...@@ -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. | | Base 2 logarithm by a polynomial approximation. |
| log2(x+1) | | 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; u_char tag;
long int exponent; long int exponent;
Xsig accumulator, yaccum; Xsig accumulator, yaccum;
if ( exponent16(st0_ptr) < 0 )
sign = arg->sign;
if ( arg->exp < EXP_BIAS )
{ {
log2_kernel(arg, &accumulator, &exponent); log2_kernel(st0_ptr, sign0, &accumulator, &exponent);
yaccum.lsw = 0; yaccum.lsw = 0;
XSIG_LL(yaccum) = significand(y); XSIG_LL(yaccum) = significand(st1_ptr);
mul_Xsig_Xsig(&accumulator, &yaccum); mul_Xsig_Xsig(&accumulator, &yaccum);
exponent += round_Xsig(&accumulator); exponent += round_Xsig(&accumulator);
result->exp = exponent + y->exp + 1; exponent += exponent16(st1_ptr) + 1;
significand(result) = XSIG_LL(accumulator); if ( exponent < EXP_WAY_UNDER ) exponent = EXP_WAY_UNDER;
result->tag = TW_Valid; /* set the tags to Valid */
result->sign = sign ^ y->sign; 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 else
{ {
/* The magnitude of arg is far too large. */ /* The magnitude of st0_ptr is far too large. */
reg_move(y, result);
if ( sign != SIGN_POS ) if ( sign0 != SIGN_POS )
{ {
/* Trying to get the log of a negative number. */ /* 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 else
{ set_precision_flag_up();
return 0;
}
} }
if ( exponent(dest) <= EXP_UNDER )
EXCEPTION(EX_Underflow);
return 0;
} }
...@@ -180,20 +200,17 @@ static const unsigned long leadterm = 0xb8000000; ...@@ -180,20 +200,17 @@ static const unsigned long leadterm = 0xb8000000;
| Base 2 logarithm by a polynomial approximation. | | Base 2 logarithm by a polynomial approximation. |
| log2(x+1) | | 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) long int *expon)
{ {
char sign;
long int exponent, adj; long int exponent, adj;
unsigned long long Xsq; unsigned long long Xsq;
Xsig accumulator, Numer, Denom, argSignif, arg_signif; Xsig accumulator, Numer, Denom, argSignif, arg_signif;
sign = arg->sign; exponent = exponent16(arg);
exponent = arg->exp - EXP_BIAS;
Numer.lsw = Denom.lsw = 0; Numer.lsw = Denom.lsw = 0;
XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg); XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg);
if ( sign == SIGN_POS ) if ( argsign == SIGN_POS )
{ {
shr_Xsig(&Denom, 2 - (1 + exponent)); shr_Xsig(&Denom, 2 - (1 + exponent));
Denom.msw |= 0x80000000; Denom.msw |= 0x80000000;
......
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
| Computation of an approximation of the sin function and the cosine | | Computation of an approximation of the sin function and the cosine |
| function by a polynomial. | | function by a polynomial. |
| | | |
| Copyright (C) 1992,1993,1994 | | Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "exception.h" #include "exception.h"
#include "reg_constant.h" #include "reg_constant.h"
#include "fpu_emu.h" #include "fpu_emu.h"
#include "fpu_system.h"
#include "control_w.h" #include "control_w.h"
#include "poly.h" #include "poly.h"
...@@ -62,35 +63,26 @@ static const unsigned long long neg_terms_h[N_COEFF_NH] = ...@@ -62,35 +63,26 @@ static const unsigned long long neg_terms_h[N_COEFF_NH] =
/*--- poly_sine() -----------------------------------------------------------+ /*--- poly_sine() -----------------------------------------------------------+
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
void poly_sine(FPU_REG const *arg, FPU_REG *result) void poly_sine(FPU_REG *st0_ptr)
{ {
int exponent, echange; int exponent, echange;
Xsig accumulator, argSqrd, argTo4; Xsig accumulator, argSqrd, argTo4;
unsigned long fix_up, adj; unsigned long fix_up, adj;
unsigned long long fixed_arg; unsigned long long fixed_arg;
FPU_REG result;
exponent = exponent(st0_ptr);
#ifdef PARANOID
if ( arg->tag == TW_Zero )
{
/* Return 0.0 */
reg_move(&CONST_Z, result);
return;
}
#endif PARANOID
exponent = arg->exp - EXP_BIAS;
accumulator.lsw = accumulator.midw = accumulator.msw = 0; accumulator.lsw = accumulator.midw = accumulator.msw = 0;
/* Split into two ranges, for arguments below and above 1.0 */ /* Split into two ranges, for arguments below and above 1.0 */
/* The boundary between upper and lower is approx 0.88309101259 */ /* 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 */ /* The argument is <= 0.88309101259 */
argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; argSqrd.msw = st0_ptr->sigh; argSqrd.midw = st0_ptr->sigl; argSqrd.lsw = 0;
mul64_Xsig(&argSqrd, &significand(arg)); mul64_Xsig(&argSqrd, &significand(st0_ptr));
shr_Xsig(&argSqrd, 2*(-1-exponent)); shr_Xsig(&argSqrd, 2*(-1-exponent));
argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw;
argTo4.lsw = argSqrd.lsw; argTo4.lsw = argSqrd.lsw;
...@@ -107,29 +99,29 @@ void poly_sine(FPU_REG const *arg, FPU_REG *result) ...@@ -107,29 +99,29 @@ void poly_sine(FPU_REG const *arg, FPU_REG *result)
shr_Xsig(&accumulator, 2); /* Divide by four */ shr_Xsig(&accumulator, 2); /* Divide by four */
accumulator.msw |= 0x80000000; /* Add 1.0 */ accumulator.msw |= 0x80000000; /* Add 1.0 */
mul64_Xsig(&accumulator, &significand(arg)); mul64_Xsig(&accumulator, &significand(st0_ptr));
mul64_Xsig(&accumulator, &significand(arg)); mul64_Xsig(&accumulator, &significand(st0_ptr));
mul64_Xsig(&accumulator, &significand(arg)); mul64_Xsig(&accumulator, &significand(st0_ptr));
/* Divide by four, FPU_REG compatible, etc */ /* Divide by four, FPU_REG compatible, etc */
exponent = 3*exponent + EXP_BIAS; exponent = 3*exponent;
/* The minimum exponent difference is 3 */ /* The minimum exponent difference is 3 */
shr_Xsig(&accumulator, arg->exp - exponent); shr_Xsig(&accumulator, exponent(st0_ptr) - exponent);
negate_Xsig(&accumulator); negate_Xsig(&accumulator);
XSIG_LL(accumulator) += significand(arg); XSIG_LL(accumulator) += significand(st0_ptr);
echange = round_Xsig(&accumulator); echange = round_Xsig(&accumulator);
result->exp = arg->exp + echange; setexponentpos(&result, exponent(st0_ptr) + echange);
} }
else else
{ {
/* The argument is > 0.88309101259 */ /* 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 ) if ( exponent == 0 )
{ {
...@@ -192,16 +184,16 @@ void poly_sine(FPU_REG const *arg, FPU_REG *result) ...@@ -192,16 +184,16 @@ void poly_sine(FPU_REG const *arg, FPU_REG *result)
echange = round_Xsig(&accumulator); echange = round_Xsig(&accumulator);
result->exp = EXP_BIAS - 1 + echange; setexponentpos(&result, echange - 1);
} }
significand(result) = XSIG_LL(accumulator); significand(&result) = XSIG_LL(accumulator);
result->tag = TW_Valid; setsign(&result, getsign(st0_ptr));
result->sign = arg->sign; FPU_copy_to_reg0(&result, TAG_Valid);
#ifdef PARANOID #ifdef PARANOID
if ( (result->exp >= EXP_BIAS) if ( (exponent(&result) >= 0)
&& (significand(result) > 0x8000000000000000LL) ) && (significand(&result) > 0x8000000000000000LL) )
{ {
EXCEPTION(EX_INTERNAL|0x150); EXCEPTION(EX_INTERNAL|0x150);
} }
...@@ -214,42 +206,36 @@ void poly_sine(FPU_REG const *arg, FPU_REG *result) ...@@ -214,42 +206,36 @@ void poly_sine(FPU_REG const *arg, FPU_REG *result)
/*--- poly_cos() ------------------------------------------------------------+ /*--- 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; long int exponent, exp2, echange;
Xsig accumulator, argSqrd, fix_up, argTo4; Xsig accumulator, argSqrd, fix_up, argTo4;
unsigned long adj; unsigned long adj;
unsigned long long fixed_arg; unsigned long long fixed_arg;
#ifdef PARANOID #ifdef PARANOID
if ( arg->tag == TW_Zero ) if ( (exponent(st0_ptr) > 0)
{ || ((exponent(st0_ptr) == 0)
/* Return 1.0 */ && (significand(st0_ptr) > 0xc90fdaa22168c234LL)) )
reg_move(&CONST_1, result);
return;
}
if ( (arg->exp > EXP_BIAS)
|| ((arg->exp == EXP_BIAS)
&& (significand(arg) > 0xc90fdaa22168c234LL)) )
{ {
EXCEPTION(EX_Invalid); EXCEPTION(EX_Invalid);
reg_move(&CONST_QNaN, result); FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
return; return;
} }
#endif PARANOID #endif PARANOID
exponent = arg->exp - EXP_BIAS; exponent = exponent(st0_ptr);
accumulator.lsw = accumulator.midw = accumulator.msw = 0; 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 */ /* arg is < 0.687705 */
argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; argSqrd.msw = st0_ptr->sigh; argSqrd.midw = st0_ptr->sigl;
mul64_Xsig(&argSqrd, &significand(arg)); argSqrd.lsw = 0;
mul64_Xsig(&argSqrd, &significand(st0_ptr));
if ( exponent < -1 ) if ( exponent < -1 )
{ {
...@@ -270,8 +256,8 @@ void poly_cos(FPU_REG const *arg, FPU_REG *result) ...@@ -270,8 +256,8 @@ void poly_cos(FPU_REG const *arg, FPU_REG *result)
N_COEFF_PH-1); N_COEFF_PH-1);
negate_Xsig(&accumulator); negate_Xsig(&accumulator);
mul64_Xsig(&accumulator, &significand(arg)); mul64_Xsig(&accumulator, &significand(st0_ptr));
mul64_Xsig(&accumulator, &significand(arg)); mul64_Xsig(&accumulator, &significand(st0_ptr));
shr_Xsig(&accumulator, -2*(1+exponent)); shr_Xsig(&accumulator, -2*(1+exponent));
shr_Xsig(&accumulator, 3); shr_Xsig(&accumulator, 3);
...@@ -290,20 +276,20 @@ void poly_cos(FPU_REG const *arg, FPU_REG *result) ...@@ -290,20 +276,20 @@ void poly_cos(FPU_REG const *arg, FPU_REG *result)
if ( accumulator.msw == 0 ) if ( accumulator.msw == 0 )
{ {
/* The result is 1.0 */ /* The result is 1.0 */
reg_move(&CONST_1, result); FPU_copy_to_reg0(&CONST_1, TAG_Valid);
return;
} }
else else
{ {
significand(result) = XSIG_LL(accumulator); significand(&result) = XSIG_LL(accumulator);
/* will be a valid positive nr with expon = -1 */ /* will be a valid positive nr with expon = -1 */
*(short *)&(result->sign) = 0; setexponentpos(&result, -1);
result->exp = EXP_BIAS - 1;
} }
} }
else else
{ {
fixed_arg = significand(arg); fixed_arg = significand(st0_ptr);
if ( exponent == 0 ) if ( exponent == 0 )
{ {
...@@ -392,14 +378,15 @@ void poly_cos(FPU_REG const *arg, FPU_REG *result) ...@@ -392,14 +378,15 @@ void poly_cos(FPU_REG const *arg, FPU_REG *result)
echange = round_Xsig(&accumulator); echange = round_Xsig(&accumulator);
result->exp = exp2 + EXP_BIAS + echange; setexponentpos(&result, exp2 + echange);
*(short *)&(result->sign) = 0; /* Is a valid positive nr */ significand(&result) = XSIG_LL(accumulator);
significand(result) = XSIG_LL(accumulator);
} }
FPU_copy_to_reg0(&result, TAG_Valid);
#ifdef PARANOID #ifdef PARANOID
if ( (result->exp >= EXP_BIAS) if ( (exponent(&result) >= 0)
&& (significand(result) > 0x8000000000000000LL) ) && (significand(&result) > 0x8000000000000000LL) )
{ {
EXCEPTION(EX_INTERNAL|0x151); EXCEPTION(EX_INTERNAL|0x151);
} }
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
| | | |
| Compute the tan of a FPU_REG, using a polynomial approximation. | | Compute the tan of a FPU_REG, using a polynomial approximation. |
| | | |
| Copyright (C) 1992,1993,1994 | | Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | Australia. E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "exception.h" #include "exception.h"
#include "reg_constant.h" #include "reg_constant.h"
#include "fpu_emu.h" #include "fpu_emu.h"
#include "fpu_system.h"
#include "control_w.h" #include "control_w.h"
#include "poly.h" #include "poly.h"
...@@ -52,7 +53,7 @@ static const unsigned long long twothirds = 0xaaaaaaaaaaaaaaabLL; ...@@ -52,7 +53,7 @@ static const unsigned long long twothirds = 0xaaaaaaaaaaaaaaabLL;
/*--- poly_tan() ------------------------------------------------------------+ /*--- poly_tan() ------------------------------------------------------------+
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
void poly_tan(FPU_REG const *arg, FPU_REG *result) void poly_tan(FPU_REG *st0_ptr)
{ {
long int exponent; long int exponent;
int invert; int invert;
...@@ -60,20 +61,20 @@ void poly_tan(FPU_REG const *arg, FPU_REG *result) ...@@ -60,20 +61,20 @@ void poly_tan(FPU_REG const *arg, FPU_REG *result)
argSignif, fix_up; argSignif, fix_up;
unsigned long adj; unsigned long adj;
exponent = arg->exp - EXP_BIAS; exponent = exponent(st0_ptr);
#ifdef PARANOID #ifdef PARANOID
if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */ if ( signnegative(st0_ptr) ) /* Can't hack a number < 0.0 */
{ arith_invalid(result); return; } /* Need a positive number */ { arith_invalid(0); return; } /* Need a positive number */
#endif PARANOID #endif PARANOID
/* Split the problem into two domains, smaller and larger than pi/4 */ /* 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 */ /* The argument is greater than (approx) pi/4 */
invert = 1; invert = 1;
accum.lsw = 0; accum.lsw = 0;
XSIG_LL(accum) = significand(arg); XSIG_LL(accum) = significand(st0_ptr);
if ( exponent == 0 ) if ( exponent == 0 )
{ {
...@@ -92,12 +93,12 @@ void poly_tan(FPU_REG const *arg, FPU_REG *result) ...@@ -92,12 +93,12 @@ void poly_tan(FPU_REG const *arg, FPU_REG *result)
{ {
invert = 0; invert = 0;
argSignif.lsw = 0; argSignif.lsw = 0;
XSIG_LL(accum) = XSIG_LL(argSignif) = significand(arg); XSIG_LL(accum) = XSIG_LL(argSignif) = significand(st0_ptr);
if ( exponent < -1 ) if ( exponent < -1 )
{ {
/* shift the argument right by the required places */ /* 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 */ XSIG_LL(accum) ++; /* round up */
} }
} }
...@@ -206,8 +207,8 @@ void poly_tan(FPU_REG const *arg, FPU_REG *result) ...@@ -206,8 +207,8 @@ void poly_tan(FPU_REG const *arg, FPU_REG *result)
/* Transfer the result */ /* Transfer the result */
round_Xsig(&accum); round_Xsig(&accum);
*(short *)&(result->sign) = 0; FPU_settag0(TAG_Valid);
significand(result) = XSIG_LL(accum); significand(st0_ptr) = XSIG_LL(accum);
result->exp = EXP_BIAS + exponent; setexponent16(st0_ptr, exponent + EXTENDED_Ebias); /* Result is positive. */
} }
This diff is collapsed.
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
| | | |
| Compare two floating point registers | | Compare two floating point registers |
| | | |
| Copyright (C) 1992,1993,1994 | | Copyright (C) 1992,1993,1994,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| Australia. E-mail billm@vaxc.cc.monash.edu.au | | E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -21,86 +21,87 @@ ...@@ -21,86 +21,87 @@
#include "status_w.h" #include "status_w.h"
int compare(FPU_REG const *b) static int compare(FPU_REG const *b, int tagb)
{ {
int diff; int diff, exp0, expb;
char st0_tag; u_char st0_tag;
FPU_REG *st0_ptr; FPU_REG *st0_ptr;
FPU_REG x, y;
u_char st0_sign, signb = getsign(b);
st0_ptr = &st(0); st0_ptr = &st(0);
st0_tag = st0_ptr->tag; st0_tag = FPU_gettag0();
st0_sign = getsign(st0_ptr);
if ( st0_tag | b->tag ) if ( tagb == TAG_Special )
tagb = FPU_Special(b);
if ( st0_tag == TAG_Special )
st0_tag = FPU_Special(st0_ptr);
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 ( tagb == TAG_Zero ) return COMP_A_eq_B;
if ( b->tag == TW_Valid ) if ( tagb == TAG_Valid )
{ return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) if ( tagb == TW_Denormal )
#ifdef DENORM_OPERAND return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
| ((b->exp <= EXP_UNDER) ? | COMP_Denormal;
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
} }
else if ( b->tag == TW_Zero ) else if ( tagb == TAG_Zero )
{ {
if ( st0_tag == TW_Valid ) if ( st0_tag == TAG_Valid )
{ return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B if ( st0_tag == TW_Denormal )
: COMP_A_lt_B) return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
#ifdef DENORM_OPERAND | COMP_Denormal;
| ((st0_ptr->exp <= EXP_UNDER )
? COMP_Denormal : 0 )
#endif DENORM_OPERAND
;
}
} }
if ( st0_tag == TW_Infinity ) if ( st0_tag == TW_Infinity )
{ {
if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) ) if ( (tagb == TAG_Valid) || (tagb == TAG_Zero) )
{ return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B else if ( tagb == TW_Denormal )
: COMP_A_lt_B) return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
#ifdef DENORM_OPERAND | COMP_Denormal;
| (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ? else if ( tagb == TW_Infinity )
COMP_Denormal : 0 )
#endif DENORM_OPERAND
;
}
else if ( b->tag == TW_Infinity )
{ {
/* The 80486 book says that infinities can be equal! */ /* The 80486 book says that infinities can be equal! */
return (st0_ptr->sign == b->sign) ? COMP_A_eq_B : return (st0_sign == signb) ? COMP_A_eq_B :
((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
} }
/* Fall through to the NaN code */ /* 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) ) if ( (st0_tag == TAG_Valid) || (st0_tag == TAG_Zero) )
{ return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) if ( st0_tag == TW_Denormal )
#ifdef DENORM_OPERAND return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
| (((st0_tag == TW_Valid) | COMP_Denormal;
&& (st0_ptr->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
}
/* Fall through to the NaN code */ /* Fall through to the NaN code */
} }
/* The only possibility now should be that one of the arguments /* The only possibility now should be that one of the arguments
is a NaN */ 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)) int signalling = 0, unsupported = 0;
|| ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) ) if ( st0_tag == TW_NaN )
/* At least one arg is a signaling 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; return COMP_No_Comp | COMP_SNaN | COMP_NaN;
else else
/* Neither is a signaling NaN */ /* Neither is a signaling NaN */
...@@ -110,24 +111,34 @@ int compare(FPU_REG const *b) ...@@ -110,24 +111,34 @@ int compare(FPU_REG const *b)
EXCEPTION(EX_Invalid); 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 #ifdef PARANOID
if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
#endif PARANOID #endif PARANOID
diff = exp0 - expb;
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;
if ( diff == 0 ) if ( diff == 0 )
{ {
diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are
...@@ -142,42 +153,30 @@ int compare(FPU_REG const *b) ...@@ -142,42 +153,30 @@ int compare(FPU_REG const *b)
if ( diff > 0 ) if ( diff > 0 )
{ {
return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
#ifdef DENORM_OPERAND | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
| COMP_Denormal : 0);
( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
} }
if ( diff < 0 ) if ( diff < 0 )
{ {
return ((st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
#ifdef DENORM_OPERAND | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
| COMP_Denormal : 0);
( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
} }
return COMP_A_eq_B return COMP_A_eq_B
#ifdef DENORM_OPERAND | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
| COMP_Denormal : 0);
( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
COMP_Denormal : 0)
#endif DENORM_OPERAND
;
} }
/* This function requires that st(0) is not empty */ /* 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; int f, c;
c = compare(loaded_data); c = compare(loaded_data, loaded_tag);
if (c & COMP_NaN) if (c & COMP_NaN)
{ {
...@@ -209,7 +208,7 @@ int compare_st_data(FPU_REG const *loaded_data) ...@@ -209,7 +208,7 @@ int compare_st_data(FPU_REG const *loaded_data)
setcc(f); setcc(f);
if (c & COMP_Denormal) if (c & COMP_Denormal)
{ {
return denormal_operand(); return denormal_operand() < 0;
} }
return 0; return 0;
} }
...@@ -218,6 +217,7 @@ int compare_st_data(FPU_REG const *loaded_data) ...@@ -218,6 +217,7 @@ int compare_st_data(FPU_REG const *loaded_data)
static int compare_st_st(int nr) static int compare_st_st(int nr)
{ {
int f, c; int f, c;
FPU_REG *st_ptr;
if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) )
{ {
...@@ -227,7 +227,8 @@ static int compare_st_st(int nr) ...@@ -227,7 +227,8 @@ static int compare_st_st(int nr)
return !(control_word & CW_Invalid); return !(control_word & CW_Invalid);
} }
c = compare(&st(nr)); st_ptr = &st(nr);
c = compare(st_ptr, FPU_gettagi(nr));
if (c & COMP_NaN) if (c & COMP_NaN)
{ {
setcc(SW_C3 | SW_C2 | SW_C0); setcc(SW_C3 | SW_C2 | SW_C0);
...@@ -259,7 +260,7 @@ static int compare_st_st(int nr) ...@@ -259,7 +260,7 @@ static int compare_st_st(int nr)
setcc(f); setcc(f);
if (c & COMP_Denormal) if (c & COMP_Denormal)
{ {
return denormal_operand(); return denormal_operand() < 0;
} }
return 0; return 0;
} }
...@@ -268,6 +269,7 @@ static int compare_st_st(int nr) ...@@ -268,6 +269,7 @@ static int compare_st_st(int nr)
static int compare_u_st_st(int nr) static int compare_u_st_st(int nr)
{ {
int f, c; int f, c;
FPU_REG *st_ptr;
if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) )
{ {
...@@ -277,7 +279,8 @@ static int compare_u_st_st(int nr) ...@@ -277,7 +279,8 @@ static int compare_u_st_st(int nr)
return !(control_word & CW_Invalid); return !(control_word & CW_Invalid);
} }
c = compare(&st(nr)); st_ptr = &st(nr);
c = compare(st_ptr, FPU_gettagi(nr));
if (c & COMP_NaN) if (c & COMP_NaN)
{ {
setcc(SW_C3 | SW_C2 | SW_C0); setcc(SW_C3 | SW_C2 | SW_C0);
...@@ -314,7 +317,7 @@ static int compare_u_st_st(int nr) ...@@ -314,7 +317,7 @@ static int compare_u_st_st(int nr)
setcc(f); setcc(f);
if (c & COMP_Denormal) if (c & COMP_Denormal)
{ {
return denormal_operand(); return denormal_operand() < 0;
} }
return 0; return 0;
} }
...@@ -332,7 +335,7 @@ void fcompst() ...@@ -332,7 +335,7 @@ void fcompst()
{ {
/* fcomp st(i) */ /* fcomp st(i) */
if ( !compare_st_st(FPU_rm) ) if ( !compare_st_st(FPU_rm) )
pop(); FPU_pop();
} }
...@@ -361,7 +364,7 @@ void fucomp() ...@@ -361,7 +364,7 @@ void fucomp()
{ {
/* fucomp st(i) */ /* fucomp st(i) */
if ( !compare_u_st_st(FPU_rm) ) if ( !compare_u_st_st(FPU_rm) )
pop(); FPU_pop();
} }
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
| | | |
| All of the constant FPU_REGs | | 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, | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@jacobi.maths.monash.edu.au | | Australia. E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -17,59 +17,52 @@ ...@@ -17,59 +17,52 @@
#include "control_w.h" #include "control_w.h"
FPU_REG const CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS, #define MAKE_REG(s,e,l,h) { l, h, \
0x00000000, 0x80000000 }; ((EXTENDED_Ebias+(e)) | ((SIGN_##s != 0)*0x8000)) }
FPU_REG const CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1,
0x00000000, 0x80000000 }; FPU_REG const CONST_1 = MAKE_REG(POS, 0, 0x00000000, 0x80000000);
FPU_REG const CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1, FPU_REG const CONST_2 = MAKE_REG(POS, 1, 0x00000000, 0x80000000);
0x00000000, 0x80000000 }; FPU_REG const CONST_HALF = MAKE_REG(POS, -1, 0x00000000, 0x80000000);
FPU_REG const CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1, FPU_REG const CONST_L2T = MAKE_REG(POS, 1, 0xcd1b8afe, 0xd49a784b);
0xcd1b8afe, 0xd49a784b }; FPU_REG const CONST_L2E = MAKE_REG(POS, 0, 0x5c17f0bc, 0xb8aa3b29);
FPU_REG const CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS, FPU_REG const CONST_PI = MAKE_REG(POS, 1, 0x2168c235, 0xc90fdaa2);
0x5c17f0bc, 0xb8aa3b29 }; FPU_REG const CONST_PI2 = MAKE_REG(POS, 0, 0x2168c235, 0xc90fdaa2);
FPU_REG const CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1, FPU_REG const CONST_PI4 = MAKE_REG(POS, -1, 0x2168c235, 0xc90fdaa2);
0x2168c235, 0xc90fdaa2 }; FPU_REG const CONST_LG2 = MAKE_REG(POS, -2, 0xfbcff799, 0x9a209a84);
FPU_REG const CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS, FPU_REG const CONST_LN2 = MAKE_REG(POS, -1, 0xd1cf79ac, 0xb17217f7);
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 };
/* Extra bits to take pi/2 to more than 128 bits precision. */ /* Extra bits to take pi/2 to more than 128 bits precision. */
FPU_REG const CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66, FPU_REG const CONST_PI2extra = MAKE_REG(NEG, -66,
0xfc8f8cbb, 0xece675d1 }; 0xfc8f8cbb, 0xece675d1);
/* Only the sign (and tag) is used in internal zeroes */ /* 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 */ /* Only the sign and significand (and tag) are used in internal NaNs */
/* The 80486 never generates one of these /* 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 */ /* 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 */ /* 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; FPU_REG *st_new_ptr;
if ( STACK_OVERFLOW ) if ( STACK_OVERFLOW )
{ {
stack_overflow(); FPU_stack_overflow();
return; return;
} }
push(); 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 st_new_ptr->sigl += adj; /* For all our fldxxx constants, we don't need to
borrow or carry. */ borrow or carry. */
FPU_settag0(tag);
clear_C1(); clear_C1();
} }
...@@ -80,37 +73,37 @@ static void fld_const(FPU_REG const *c, int adj) ...@@ -80,37 +73,37 @@ static void fld_const(FPU_REG const *c, int adj)
static void fld1(int rc) static void fld1(int rc)
{ {
fld_const(&CONST_1, 0); fld_const(&CONST_1, 0, TAG_Valid);
} }
static void fldl2t(int rc) 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) 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) 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) 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) 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) static void fldz(int rc)
{ {
fld_const(&CONST_Z, 0); fld_const(&CONST_Z, 0, TAG_Zero);
} }
typedef void (*FUNC_RC)(int); typedef void (*FUNC_RC)(int);
......
/*---------------------------------------------------------------------------+
| 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;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
| version.h | | version.h |
| | | |
| | | |
| Copyright (C) 1992,1993,1994,1996 | | Copyright (C) 1992,1993,1994,1996,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
| E-mail billm@jacobi.maths.monash.edu.au | | E-mail billm@suburbia.net |
| | | |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
#define FPU_VERSION "wm-FPU-emu version 1.22" #define FPU_VERSION "wm-FPU-emu version 2.00"
...@@ -9,9 +9,9 @@ ...@@ -9,9 +9,9 @@
| Australia. E-mail billm@jacobi.maths.monash.edu.au | | Australia. E-mail billm@jacobi.maths.monash.edu.au |
| | | |
| Call from C as: | | Call from C as: |
| unsigned shrx(void *arg1, unsigned arg2) | | unsigned FPU_shrx(void *arg1, unsigned arg2) |
| and | | and |
| unsigned shrxs(void *arg1, unsigned arg2) | | unsigned FPU_shrxs(void *arg1, unsigned arg2) |
| | | |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
.text .text
/*---------------------------------------------------------------------------+ /*---------------------------------------------------------------------------+
| unsigned shrx(void *arg1, unsigned arg2) | | unsigned FPU_shrx(void *arg1, unsigned arg2) |
| | | |
| Extended shift right function. | | Extended shift right function. |
| Fastest for small shifts. | | Fastest for small shifts. |
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
| Results returned in the 64 bit arg and eax. | | Results returned in the 64 bit arg and eax. |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
ENTRY(shrx) ENTRY(FPU_shrx)
push %ebp push %ebp
movl %esp,%ebp movl %esp,%ebp
pushl %esi pushl %esi
...@@ -95,7 +95,7 @@ L_more_than_95: ...@@ -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 | | Extended shift right function (optimized for small floating point |
| integers). | | integers). |
...@@ -110,7 +110,7 @@ L_more_than_95: ...@@ -110,7 +110,7 @@ L_more_than_95:
| part which has been shifted out of the arg. | | part which has been shifted out of the arg. |
| Results returned in the 64 bit arg and eax. | | Results returned in the 64 bit arg and eax. |
+---------------------------------------------------------------------------*/ +---------------------------------------------------------------------------*/
ENTRY(shrxs) ENTRY(FPU_shrxs)
push %ebp push %ebp
movl %esp,%ebp movl %esp,%ebp
pushl %esi pushl %esi
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
echo Setting up the environment for debugging vmlinux...\n
echo set remotedebug 0 \n
set remotedebug 0
echo cd arch/mips/kernel \n
cd arch/mips/kernel
echo target remote /dev/ttyS0 \n
target remote /dev/ttyS0
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment