diff options
author | gclarkii <gclarkii@FreeBSD.org> | 1994-04-29 20:42:02 +0000 |
---|---|---|
committer | gclarkii <gclarkii@FreeBSD.org> | 1994-04-29 20:42:02 +0000 |
commit | f64e3f3d896af448202e217cbbd99d939e18a48d (patch) | |
tree | 8001f3539d201d43de454968ecd6afef196f8f35 | |
parent | 9ac05a42c33c56a0ba23ef8a67fae31db7b244ac (diff) | |
download | FreeBSD-src-f64e3f3d896af448202e217cbbd99d939e18a48d.zip FreeBSD-src-f64e3f3d896af448202e217cbbd99d939e18a48d.tar.gz |
Import of gpl'ed math emulator.
No changes have been done.
44 files changed, 11697 insertions, 0 deletions
diff --git a/sys/gnu/i386/fpemul/Changelog b/sys/gnu/i386/fpemul/Changelog new file mode 100644 index 0000000..a2fbccd1 --- /dev/null +++ b/sys/gnu/i386/fpemul/Changelog @@ -0,0 +1,36 @@ +This file contains the changes made to W. Metzenthem's 387 FPU +emulator to make it work under NetBSD. + +a, Changes to make it compile: + + 1 - Changed the #include's to get the appropriate .h files. + 2 - Renamed .S to .s, to satisfy the kernel Makefile. + 3 - Changed the C++ style // comments to /* */ + 4 - Changed the FPU_ORIG_EIP macro. A letter from bde included + in the package suggested using tf_isp for using instead + of the linux __orig_eip. This later turned out to interfere + with the user stack, so i created a separate variable, stored + in the i387_union. + 5 - Changed the get_fs_.. put_fs_.. fns to fubyte,fuword,subyte, + suword. + 6 - Removed the verify_area fns. I don't really know what they do, + i suppose they verify access to memory. The sufu routines + should do this. + +b, Changes to make it work: + + 1 - Made math_emulate() to return 0 when successful, so trap() won't + try to generate a signal. + 2 - Changed the size of the save87 struct in /sys/arch/i387/include/ + npx.h to accomodate the i387_union. + +d, Other changes: + + 1 - Removed obsolate and/or linux specific stuff. + 2 - Changed the RE_ENTRANT_CHECK_[ON|OFF] macro to + REENTRANT_CHECK([ON|OFF]) so indent can grok it. + 3 - Re-indented to Berkeley style. + 4 - Limited max no of lookaheads. LOOKAHEAD_LIMIT in fpu_entry.c + + + Szabolcs Szigeti (pink@fsz.bme.hu) diff --git a/sys/gnu/i386/fpemul/README b/sys/gnu/i386/fpemul/README new file mode 100644 index 0000000..4982c35 --- /dev/null +++ b/sys/gnu/i386/fpemul/README @@ -0,0 +1,267 @@ +/* + * wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387 +which is my 80387 emulator for djgpp (gcc under msdos); wm-emu387 was +in turn based upon emu387 which was written by DJ Delorie for djgpp. +The interface to the Linux kernel is based upon the original Linux +math emulator by Linus Torvalds. + +My target FPU for wm-FPU-emu is that described in the Intel486 +Programmer's Reference Manual (1992 edition). Numerous facets of the +functioning of the FPU are not well covered in the Reference Manual; +in the absence of clear details I have made guesses about the most +reasonable behaviour. Recently, this situation has improved because +I now have some access to the results produced by a real 80486 FPU. + +wm-FPU-emu does not implement all of the behaviour of the 80486 FPU. +See "Limitations" later in this file for a partial list of some +differences. I believe that the missing features are never used by +normal C or FORTRAN programs. + + +Please report bugs, etc to me at: + apm233m@vaxc.cc.monash.edu.au + + +--Bill Metzenthen + May 1993 + + +----------------------- Internals of wm-FPU-emu ----------------------- + +Numeric algorithms: +(1) Add, subtract, and multiply. Nothing remarkable in these. +(2) Divide has been tuned to get reasonable performance. The algorithm + is not the obvious one which most people seem to use, but is designed + to take advantage of the characteristics of the 80386. I expect that + it has been invented many times before I discovered it, but I have not + seen it. It is based upon one of those ideas which one carries around + for years without ever bothering to check it out. +(3) The sqrt function has been tuned to get good performance. It is based + upon Newton's classic method. Performance was improved by capitalizing + upon the properties of Newton's method, and the code is once again + structured taking account of the 80386 characteristics. +(4) The trig, log, and exp functions are based in each case upon quasi- + "optimal" polynomial approximations. My definition of "optimal" was + based upon getting good accuracy with reasonable speed. + +The code of the emulator is complicated slightly by the need to +account for a limited form of re-entrancy. Normally, the emulator will +emulate each FPU instruction to completion without interruption. +However, it may happen that when the emulator is accessing the user +memory space, swapping may be needed. In this case the emulator may be +temporarily suspended while disk i/o takes place. During this time +another process may use the emulator, thereby changing some static +variables (eg FPU_st0_ptr, etc). The code which accesses user memory +is confined to five files: + fpu_entry.c + reg_ld_str.c + load_store.c + get_address.c + errors.c + +----------------------- Limitations of wm-FPU-emu ----------------------- + +There are a number of differences between the current wm-FPU-emu +(version beta 1.4) and the 80486 FPU (apart from bugs). Some of the +more important differences are listed below: + +All internal computations are performed at 64 bit or higher precision +and rounded etc as required by the PC bits of the FPU control word. +Under the crt0 version for Linux current at March 1993, the FPU PC +bits specify 53 bits precision. + +The precision flag (PE of the FPU status word) and the Roundup flag +(C1 of the status word) are now partially implemented. Does anyone +write code which uses these features? + +The functions which load/store the FPU state are partially implemented, +but the implementation should be sufficient for handling FPU errors etc +in 32 bit protected mode. + +The implementation of the exception mechanism is flawed for unmasked +interrupts. + +Detection of certain conditions, such as denormal operands, is not yet +complete. + +----------------------- Performance of wm-FPU-emu ----------------------- + +Speed. +----- + +The speed of floating point computation with the emulator will depend +upon instruction mix. Relative performance is best for the instructions +which require most computation. The simple instructions are adversely +affected by the fpu instruction trap overhead. + + +Timing: Some simple timing tests have been made on the emulator functions. +The times include load/store instructions. All times are in microseconds +measured on a 33MHz 386 with 64k cache. The Turbo C tests were under +ms-dos, the next two columns are for emulators running with the djgpp +ms-dos extender. The final column is for wm-FPU-emu in Linux 0.97, +using libm4.0 (hard). + +function Turbo C djgpp 1.06 WM-emu387 wm-FPU-emu + + + 60.5 154.8 76.5 139.4 + - 61.1-65.5 157.3-160.8 76.2-79.5 142.9-144.7 + * 71.0 190.8 79.6 146.6 + / 61.2-75.0 261.4-266.9 75.3-91.6 142.2-158.1 + + sin() 310.8 4692.0 319.0 398.5 + cos() 284.4 4855.2 308.0 388.7 + tan() 495.0 8807.1 394.9 504.7 + atan() 328.9 4866.4 601.1 419.5-491.9 + + sqrt() 128.7 crashed 145.2 227.0 + log() 413.1-419.1 5103.4-5354.21 254.7-282.2 409.4-437.1 + exp() 479.1 6619.2 469.1 850.8 + + +The performance under Linux is improved by the use of look-ahead code. +The following results show the improvement which is obtained under +Linux due to the look-ahead code. Also given are the times for the +original Linux emulator with the 4.1 'soft' lib. + + [ Linus' note: I changed look-ahead to be the default under linux, as + there was no reason not to use it after I had edited it to be + disabled during tracing ] + + wm-FPU-emu w original w + look-ahead 'soft' lib + + 106.4 190.2 + - 108.6-111.6 192.4-216.2 + * 113.4 193.1 + / 108.8-124.4 700.1-706.2 + + sin() 390.5 2642.0 + cos() 381.5 2767.4 + tan() 496.5 3153.3 + atan() 367.2-435.5 2439.4-3396.8 + + sqrt() 195.1 4732.5 + log() 358.0-387.5 3359.2-3390.3 + exp() 619.3 4046.4 + + +These figures are now somewhat out-of-date. The emulator has become +progressively slower for most functions as more of the 80486 features +have been implemented. + + +----------------------- Accuracy of wm-FPU-emu ----------------------- + + +Accuracy: The following table gives the accuracy of the sqrt(), trig +and log functions. Each function was tested at about 400 points. Ideal +results would be 64 bits. The reduced accuracy of cos() and tan() for +arguments greater than pi/4 can be thought of as being due to the +precision of the argument x; e.g. an argument of pi/2-(1e-10) which is +accurate to 64 bits can result in a relative accuracy in cos() of about +64 + log2(cos(x)) = 31 bits. Results for the Turbo C emulator are given +in the last column. + + +Function Tested x range Worst result (bits) Turbo C + +sqrt(x) 1 .. 2 64.1 63.2 +atan(x) 1e-10 .. 200 62.6 62.8 +cos(x) 0 .. pi/2-(1e-10) 63.2 (x <= pi/4) 62.4 + 35.2 (x = pi/2-(1e-10)) 31.9 +sin(x) 1e-10 .. pi/2 63.0 62.8 +tan(x) 1e-10 .. pi/2-(1e-10) 62.4 (x <= pi/4) 62.1 + 35.2 (x = pi/2-(1e-10)) 31.9 +exp(x) 0 .. 1 63.1 62.9 +log(x) 1+1e-6 .. 2 62.4 62.1 + + +As of version 1.3 of the emulator, the accuracy of the basic +arithmetic has been improved (by a small fraction of a bit). Care has +been taken to ensure full accuracy of the rounding of the basic +arithmetic functions (+,-,*,/,and fsqrt), and they all now produce +results which are exact to the 64th bit (unless there are any bugs +left). To ensure this, it was necessary to effectively get information +of up to about 128 bits precision. The emulator now passes the +"paranoia" tests (compiled with gcc 2.3.3) for 'float' variables (24 +bit precision numbers) when precision control is set to 24, 53 or 64 +bits, and for 'double' variables (53 bit precision numbers) when +precision control is set to 53 bits (a properly performing FPU cannot +pass the 'paranoia' tests for 'double' variables when precision +control is set to 64 bits). + +------------------------- Contributors ------------------------------- + +A number of people have contributed to the development of the +emulator, often by just reporting bugs, sometimes with a suggested +fix, and a few kind people have provided me with access in one way or +another to an 80486 machine. Contributors include (to those people who +I have forgotten, please excuse me): + +Linus Torvalds +Tommy.Thorn@daimi.aau.dk +Andrew.Tridgell@anu.edu.au +Nick Holloway alfie@dcs.warwick.ac.uk +Hermano Moura moura@dcs.gla.ac.uk +Jon Jagger J.Jagger@scp.ac.uk +Lennart Benschop +Brian Gallew geek+@CMU.EDU +Thomas Staniszewski ts3v+@andrew.cmu.edu +Martin Howell mph@plasma.apana.org.au +M Saggaf alsaggaf@athena.mit.edu +Peter Barker PETER@socpsy.sci.fau.edu +tom@vlsivie.tuwien.ac.at +Dan Russel russed@rpi.edu +Daniel Carosone danielce@ee.mu.oz.au +cae@jpmorgan.com +Hamish Coleman t933093@minyos.xx.rmit.oz.au + +...and numerous others who responded to my request for help with +a real 80486. + diff --git a/sys/gnu/i386/fpemul/bde_trapinfo.mail b/sys/gnu/i386/fpemul/bde_trapinfo.mail new file mode 100644 index 0000000..2749e04 --- /dev/null +++ b/sys/gnu/i386/fpemul/bde_trapinfo.mail @@ -0,0 +1,35 @@ +From bde@kralizec.zeta.org.au Sun Jun 27 01:18:32 1993 +Received: from ultima.socs.uts.EDU.AU by bsd.coe.montana.edu (5.67/KAOS-1) + id AA11952; Sun, 27 Jun 93 01:18:32 -0600 +Received: by ultima.socs.uts.EDU.AU (5.65+/SMI-3.3) + id AA03033; Sun, 27 Jun 93 17:10:22 +1000 +Received: by kralizec.zeta.org.au (4.0/SMI-4.0) + id AA15074; Sat, 26 Jun 93 02:32:58 EST +Date: Sat, 26 Jun 93 02:32:58 EST +From: bde@kralizec.zeta.org.au (Bruce Evans) +Message-Id: <9306251632.AA15074@kralizec.zeta.org.au> +To: nate@bsd.coe.montana.edu +Subject: Re: Trapframe information +Status: OR + +tf_isp original esp (probably spare - popal ignores it) +tf_trapno s/w trap no (may be spare - trap.c has already looked at it) +tf_err h/w error code (probably spare - gets discarded before iret) + +___fs not stored in 386BSD pcb. Constant anyway unless user has + screwed with it (?). +___gs ditto +___orig_eip in linux, this is on the stack just before the call to the + emulator. The reason that it's not a local variable is to + avoid passing around pointers to it - current->frame (or + whatever) points to everything in the stack frame. The + macros hide a lot of slow memory references + current->frame->var. + +>(And I need to see if I can map orig_eip to one of the three that I'm unsure of +>in the BSD sources) + +tf_isp is the least evil. + +Bruce + diff --git a/sys/gnu/i386/fpemul/control_w.h b/sys/gnu/i386/fpemul/control_w.h new file mode 100644 index 0000000..6d4ee24 --- /dev/null +++ b/sys/gnu/i386/fpemul/control_w.h @@ -0,0 +1,82 @@ +/* + * control_w.h + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _CONTROLW_H_ +#define _CONTROLW_H_ + +#ifdef LOCORE +#define _Const_(x) $/**/x +#else +#define _Const_(x) x +#endif + +#define CW_RC _Const_(0x0C00) /* rounding control */ +#define CW_PC _Const_(0x0300) /* precision control */ + +#define CW_Precision Const_(0x0020) /* loss of precision mask */ +#define CW_Underflow Const_(0x0010) /* underflow mask */ +#define CW_Overflow Const_(0x0008) /* overflow mask */ +#define CW_ZeroDiv Const_(0x0004) /* divide by zero mask */ +#define CW_Denormal Const_(0x0002) /* denormalized operand mask */ +#define CW_Invalid Const_(0x0001) /* invalid operation mask */ + +#define CW_Exceptions _Const_(0x003f) /* all masks */ + +#define RC_RND _Const_(0x0000) +#define RC_DOWN _Const_(0x0400) +#define RC_UP _Const_(0x0800) +#define RC_CHOP _Const_(0x0C00) + +/* p 15-5: Precision control bits affect only the following: + ADD, SUB(R), MUL, DIV(R), and SQRT */ +#define PR_24_BITS _Const_(0x000) +#define PR_53_BITS _Const_(0x200) +#define PR_64_BITS _Const_(0x300) +/* FULL_PRECISION simulates all exceptions masked */ +#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f) + +#endif /* _CONTROLW_H_ */ diff --git a/sys/gnu/i386/fpemul/div_small.s b/sys/gnu/i386/fpemul/div_small.s new file mode 100644 index 0000000..13c2643 --- /dev/null +++ b/sys/gnu/i386/fpemul/div_small.s @@ -0,0 +1,88 @@ + .file "div_small.S" +/* + * div_small.S + * + * Divide a 64 bit integer by a 32 bit integer & return remainder. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/*---------------------------------------------------------------------------+ + | unsigned long div_small(unsigned long long *x, unsigned long y) | + +---------------------------------------------------------------------------*/ + +#include "fpu_asm.h" + +.text + .align 2,144 + +.globl _div_small + +_div_small: + pushl %ebp + movl %esp,%ebp + + pushl %esi + + movl PARAM1,%esi /* pointer to num */ + movl PARAM2,%ecx /* The denominator */ + + movl 4(%esi),%eax /* Get the current num msw */ + xorl %edx,%edx + divl %ecx + + movl %eax,4(%esi) + + movl (%esi),%eax /* Get the num lsw */ + divl %ecx + + movl %eax,(%esi) + + movl %edx,%eax /* Return the remainder in eax */ + + popl %esi + + leave + ret + diff --git a/sys/gnu/i386/fpemul/errors.c b/sys/gnu/i386/fpemul/errors.c new file mode 100644 index 0000000..ed5ba47 --- /dev/null +++ b/sys/gnu/i386/fpemul/errors.c @@ -0,0 +1,599 @@ +/* + * errors.c + * + * The error handling functions for wm-FPU-emu + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/*---------------------------------------------------------------------------+ + | Note: | + | The file contains code which accesses user memory. | + | Emulator static data may change when user memory is accessed, due to | + | other processes using the emulator while swapping is in progress. | + +---------------------------------------------------------------------------*/ + + + + + +#include "param.h" +#include "proc.h" +#include "machine/cpu.h" +#include "machine/pcb.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "exception.h" +#include "status_w.h" +#include "control_w.h" +#include "reg_constant.h" +#include "version.h" + +/* */ +#undef PRINT_MESSAGES +/* */ + + +void +Un_impl(void) +{ + unsigned char byte1, FPU_modrm; + + REENTRANT_CHECK(OFF); + byte1 = fubyte((unsigned char *) FPU_ORIG_EIP); + FPU_modrm = fubyte(1 + (unsigned char *) FPU_ORIG_EIP); + + printf("Unimplemented FPU Opcode at eip=%p : %02x ", + FPU_ORIG_EIP, byte1); + + if (FPU_modrm >= 0300) + printf("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); + else + printf("/%d\n", (FPU_modrm >> 3) & 7); + REENTRANT_CHECK(ON); + + EXCEPTION(EX_Invalid); + +} + + + + +void +emu_printall() +{ + int i; + static char *tag_desc[] = {"Valid", "Zero", "ERROR", "ERROR", + "DeNorm", "Inf", "NaN", "Empty"}; + unsigned char byte1, FPU_modrm; + + REENTRANT_CHECK(OFF); + byte1 = fubyte((unsigned char *) FPU_ORIG_EIP); + FPU_modrm = fubyte(1 + (unsigned char *) FPU_ORIG_EIP); + +#ifdef DEBUGGING + if (status_word & SW_Backward) + printf("SW: backward compatibility\n"); + if (status_word & SW_C3) + printf("SW: condition bit 3\n"); + if (status_word & SW_C2) + printf("SW: condition bit 2\n"); + if (status_word & SW_C1) + printf("SW: condition bit 1\n"); + if (status_word & SW_C0) + printf("SW: condition bit 0\n"); + if (status_word & SW_Summary) + printf("SW: exception summary\n"); + if (status_word & SW_Stack_Fault) + printf("SW: stack fault\n"); + if (status_word & SW_Precision) + printf("SW: loss of precision\n"); + if (status_word & SW_Underflow) + printf("SW: underflow\n"); + if (status_word & SW_Overflow) + printf("SW: overflow\n"); + if (status_word & SW_Zero_Div) + printf("SW: divide by zero\n"); + if (status_word & SW_Denorm_Op) + printf("SW: denormalized operand\n"); + if (status_word & SW_Invalid) + printf("SW: invalid operation\n"); +#endif /* DEBUGGING */ + + status_word = status_word & ~SW_Top; + status_word |= (top & 7) << SW_Top_Shift; + + printf("At %p: %02x ", FPU_ORIG_EIP, byte1); + if (FPU_modrm >= 0300) + printf("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); + else + printf("/%d, mod=%d rm=%d\n", + (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7); + + printf(" SW: b=%d st=%d es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n", + status_word & 0x8000 ? 1 : 0, /* busy */ + (status_word & 0x3800) >> 11, /* stack top pointer */ + status_word & 0x80 ? 1 : 0, /* Error summary status */ + status_word & 0x40 ? 1 : 0, /* Stack flag */ + status_word & SW_C3 ? 1 : 0, status_word & SW_C2 ? 1 : 0, /* cc */ + status_word & SW_C1 ? 1 : 0, status_word & SW_C0 ? 1 : 0, /* cc */ + status_word & SW_Precision ? 1 : 0, status_word & SW_Underflow ? 1 : 0, + status_word & SW_Overflow ? 1 : 0, status_word & SW_Zero_Div ? 1 : 0, + status_word & SW_Denorm_Op ? 1 : 0, status_word & SW_Invalid ? 1 : 0); + + printf(" CW: ic=%d rc=%d%d pc=%d%d iem=%d ef=%d%d%d%d%d%d\n", + control_word & 0x1000 ? 1 : 0, + (control_word & 0x800) >> 11, (control_word & 0x400) >> 10, + (control_word & 0x200) >> 9, (control_word & 0x100) >> 8, + control_word & 0x80 ? 1 : 0, + control_word & SW_Precision ? 1 : 0, control_word & SW_Underflow ? 1 : 0, + control_word & SW_Overflow ? 1 : 0, control_word & SW_Zero_Div ? 1 : 0, + control_word & SW_Denorm_Op ? 1 : 0, control_word & SW_Invalid ? 1 : 0); + + for (i = 0; i < 8; i++) { + FPU_REG *r = &st(i); + switch (r->tag) { + case TW_Empty: + continue; + break; + case TW_Zero: + printf("st(%d) %c .0000 0000 0000 0000 ", + i, r->sign ? '-' : '+'); + break; + case TW_Valid: + case TW_NaN: + case TW_Denormal: + case TW_Infinity: + printf("st(%d) %c .%04x %04x %04x %04x e%+-6d ", i, + r->sign ? '-' : '+', + (long) (r->sigh >> 16), + (long) (r->sigh & 0xFFFF), + (long) (r->sigl >> 16), + (long) (r->sigl & 0xFFFF), + r->exp - EXP_BIAS + 1); + break; + default: + printf("Whoops! Error in errors.c "); + break; + } + printf("%s\n", tag_desc[(int) (unsigned) r->tag]); + } + + printf("[data] %c .%04x %04x %04x %04x e%+-6d ", + FPU_loaded_data.sign ? '-' : '+', + (long) (FPU_loaded_data.sigh >> 16), + (long) (FPU_loaded_data.sigh & 0xFFFF), + (long) (FPU_loaded_data.sigl >> 16), + (long) (FPU_loaded_data.sigl & 0xFFFF), + FPU_loaded_data.exp - EXP_BIAS + 1); + printf("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]); + REENTRANT_CHECK(ON); + +} + +static struct { + int type; + char *name; +} exception_names[] = { + { + EX_StackOver, "stack overflow" + }, + { + EX_StackUnder, "stack underflow" + }, + { + EX_Precision, "loss of precision" + }, + { + EX_Underflow, "underflow" + }, + { + EX_Overflow, "overflow" + }, + { + EX_ZeroDiv, "divide by zero" + }, + { + EX_Denormal, "denormalized operand" + }, + { + EX_Invalid, "invalid operation" + }, + { + EX_INTERNAL, "INTERNAL BUG in " FPU_VERSION + }, + { + 0, NULL + } +}; +/* + EX_INTERNAL is always given with a code which indicates where the + error was detected. + + Internal error types: + 0x14 in e14.c + 0x1nn in a *.c file: + 0x101 in reg_add_sub.c + 0x102 in reg_mul.c + 0x103 in poly_sin.c + 0x104 in poly_tan.c + 0x105 in reg_mul.c + 0x106 in reg_mov.c + 0x107 in fpu_trig.c + 0x108 in reg_compare.c + 0x109 in reg_compare.c + 0x110 in reg_add_sub.c + 0x111 in interface.c + 0x112 in fpu_trig.c + 0x113 in reg_add_sub.c + 0x114 in reg_ld_str.c + 0x115 in fpu_trig.c + 0x116 in fpu_trig.c + 0x117 in fpu_trig.c + 0x118 in fpu_trig.c + 0x119 in fpu_trig.c + 0x120 in poly_atan.c + 0x121 in reg_compare.c + 0x122 in reg_compare.c + 0x123 in reg_compare.c + 0x2nn in an *.s file: + 0x201 in reg_u_add.S + 0x202 in reg_u_div.S + 0x203 in reg_u_div.S + 0x204 in reg_u_div.S + 0x205 in reg_u_mul.S + 0x206 in reg_u_sub.S + 0x207 in wm_sqrt.S + 0x208 in reg_div.S + 0x209 in reg_u_sub.S + 0x210 in reg_u_sub.S + 0x211 in reg_u_sub.S + 0x212 in reg_u_sub.S + 0x213 in wm_sqrt.S + 0x214 in wm_sqrt.S + 0x215 in wm_sqrt.S + 0x216 in reg_round.S + 0x217 in reg_round.S + 0x218 in reg_round.S + */ + +void +exception(int n) +{ + int i, int_type; + + int_type = 0; /* Needed only to stop compiler warnings */ + if (n & EX_INTERNAL) { + int_type = n - EX_INTERNAL; + n = EX_INTERNAL; + /* Set lots of exception bits! */ + status_word |= (SW_Exc_Mask | SW_Summary | FPU_BUSY); + } else { + /* Extract only the bits which we use to set the status word */ + n &= (SW_Exc_Mask); + /* Set the corresponding exception bit */ + status_word |= n; + if (status_word & ~control_word & CW_Exceptions) + status_word |= SW_Summary; + if (n & (SW_Stack_Fault | EX_Precision)) { + if (!(n & SW_C1)) + /* This bit distinguishes over- from underflow + * for a stack fault, and roundup from + * round-down for precision loss. */ + status_word &= ~SW_C1; + } + } + + REENTRANT_CHECK(OFF); + if ((~control_word & n & CW_Exceptions) || (n == EX_INTERNAL)) { +#ifdef PRINT_MESSAGES + /* My message from the sponsor */ + printf(FPU_VERSION " " __DATE__ " (C) W. Metzenthen.\n"); +#endif /* PRINT_MESSAGES */ + + /* Get a name string for error reporting */ + for (i = 0; exception_names[i].type; i++) + if ((exception_names[i].type & n) == exception_names[i].type) + break; + + if (exception_names[i].type) { +#ifdef PRINT_MESSAGES + printf("FP Exception: %s!\n", exception_names[i].name); +#endif /* PRINT_MESSAGES */ + } else + printf("FP emulator: Unknown Exception: 0x%04x!\n", n); + + if (n == EX_INTERNAL) { + printf("FP emulator: Internal error type 0x%04x\n", int_type); + emu_printall(); + } +#ifdef PRINT_MESSAGES + else + emu_printall(); +#endif /* PRINT_MESSAGES */ + + /* The 80486 generates an interrupt on the next non-control + * FPU instruction. So we need some means of flagging it. We + * use the ES (Error Summary) bit for this, assuming that this + * is the way a real FPU does it (until I can check it out), + * if not, then some method such as the following kludge might + * be needed. */ +/* regs[0].tag |= TW_FPU_Interrupt; */ + } + REENTRANT_CHECK(ON); + +#ifdef __DEBUG__ + math_abort(SIGFPE); +#endif /* __DEBUG__ */ + +} + + +/* Real operation attempted on two operands, one a NaN */ +void +real_2op_NaN(FPU_REG * a, FPU_REG * b, FPU_REG * dest) +{ + FPU_REG *x; + int signalling; + + x = a; + if (a->tag == TW_NaN) { + if (b->tag == TW_NaN) { + signalling = !(a->sigh & b->sigh & 0x40000000); + /* find the "larger" */ + if (*(long long *) &(a->sigl) < *(long long *) &(b->sigl)) + x = b; + } else { + /* return the quiet version of the NaN in a */ + signalling = !(a->sigh & 0x40000000); + } + } else +#ifdef PARANOID + if (b->tag == TW_NaN) +#endif /* PARANOID */ + { + signalling = !(b->sigh & 0x40000000); + x = b; + } +#ifdef PARANOID + else { + signalling = 0; + EXCEPTION(EX_INTERNAL | 0x113); + x = &CONST_QNaN; + } +#endif /* PARANOID */ + + if (!signalling) { + if (!(x->sigh & 0x80000000)) /* pseudo-NaN ? */ + x = &CONST_QNaN; + reg_move(x, dest); + return; + } + if (control_word & CW_Invalid) { + /* The masked response */ + if (!(x->sigh & 0x80000000)) /* pseudo-NaN ? */ + x = &CONST_QNaN; + reg_move(x, dest); + /* ensure a Quiet NaN */ + dest->sigh |= 0x40000000; + } + EXCEPTION(EX_Invalid); + + return; +} +/* Invalid arith operation on Valid registers */ +void +arith_invalid(FPU_REG * dest) +{ + + if (control_word & CW_Invalid) { + /* The masked response */ + reg_move(&CONST_QNaN, dest); + } + EXCEPTION(EX_Invalid); + + return; + +} + + +/* Divide a finite number by zero */ +void +divide_by_zero(int sign, FPU_REG * dest) +{ + + if (control_word & CW_ZeroDiv) { + /* The masked response */ + reg_move(&CONST_INF, dest); + dest->sign = (unsigned char) sign; + } + EXCEPTION(EX_ZeroDiv); + + return; + +} + + +/* This may be called often, so keep it lean */ +void +set_precision_flag_up(void) +{ + if (control_word & CW_Precision) + status_word |= (SW_Precision | SW_C1); /* The masked response */ + else + exception(EX_Precision | SW_C1); + +} + + +/* This may be called often, so keep it lean */ +void +set_precision_flag_down(void) +{ + if (control_word & CW_Precision) { /* The masked response */ + status_word &= ~SW_C1; + status_word |= SW_Precision; + } else + exception(EX_Precision); +} + + +int +denormal_operand(void) +{ + if (control_word & CW_Denormal) { /* The masked response */ + status_word |= SW_Denorm_Op; + return 0; + } else { + exception(EX_Denormal); + return 1; + } +} + + +void +arith_overflow(FPU_REG * dest) +{ + + if (control_word & CW_Overflow) { + char sign; + /* The masked response */ +/* **** The response here depends upon the rounding mode */ + sign = dest->sign; + reg_move(&CONST_INF, dest); + dest->sign = sign; + } else { + /* Subtract the magic number from the exponent */ + dest->exp -= (3 * (1 << 13)); + } + + /* By definition, precision is lost. It appears that the roundup bit + * (C1) is also set by convention. */ + EXCEPTION(EX_Overflow | EX_Precision | SW_C1); + + return; + +} + + +void +arith_underflow(FPU_REG * dest) +{ + + if (control_word & CW_Underflow) { + /* The masked response */ + if (dest->exp <= EXP_UNDER - 63) + reg_move(&CONST_Z, dest); + } else { + /* Add the magic number to the exponent */ + dest->exp += (3 * (1 << 13)); + } + + EXCEPTION(EX_Underflow); + + return; +} + + +void +stack_overflow(void) +{ + + if (control_word & CW_Invalid) { + /* The masked response */ + top--; + reg_move(&CONST_QNaN, FPU_st0_ptr = &st(0)); + } + EXCEPTION(EX_StackOver); + + return; + +} + + +void +stack_underflow(void) +{ + + if (control_word & CW_Invalid) { + /* The masked response */ + reg_move(&CONST_QNaN, FPU_st0_ptr); + } + EXCEPTION(EX_StackUnder); + + return; + +} + + +void +stack_underflow_i(int i) +{ + + if (control_word & CW_Invalid) { + /* The masked response */ + reg_move(&CONST_QNaN, &(st(i))); + } + EXCEPTION(EX_StackUnder); + + return; + +} + + +void +stack_underflow_pop(int i) +{ + + if (control_word & CW_Invalid) { + /* The masked response */ + reg_move(&CONST_QNaN, &(st(i))); + pop(); + } + EXCEPTION(EX_StackUnder); + + return; + +} diff --git a/sys/gnu/i386/fpemul/exception.h b/sys/gnu/i386/fpemul/exception.h new file mode 100644 index 0000000..c474eca --- /dev/null +++ b/sys/gnu/i386/fpemul/exception.h @@ -0,0 +1,88 @@ +/* + * exception.h + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + + +#ifdef LOCORE +#define Const_(x) $/**/x +#else +#define Const_(x) x +#endif + +#ifndef SW_C1 +#include "fpu_emu.h" +#endif /* SW_C1 */ + +#define FPU_BUSY Const_(0x8000) /* FPU busy bit (8087 compatibility) */ +#define EX_ErrorSummary Const_(0x0080) /* Error summary status */ +/* Special exceptions: */ +#define EX_INTERNAL Const_(0x8000) /* Internal error in wm-FPU-emu */ +#define EX_StackOver Const_(0x0041|SW_C1) /* stack overflow */ +#define EX_StackUnder Const_(0x0041) /* stack underflow */ +/* Exception flags: */ +#define EX_Precision Const_(0x0020) /* loss of precision */ +#define EX_Underflow Const_(0x0010) /* underflow */ +#define EX_Overflow Const_(0x0008) /* overflow */ +#define EX_ZeroDiv Const_(0x0004) /* divide by zero */ +#define EX_Denormal Const_(0x0002) /* denormalized operand */ +#define EX_Invalid Const_(0x0001) /* invalid operation */ + + +#ifndef LOCORE + +#ifdef DEBUG +#define EXCEPTION(x) { printf("exception in %s at line %d\n", \ + __FILE__, __LINE__); exception(x); } +#else +#define EXCEPTION(x) exception(x) +#endif + +#endif /* LOCORE */ + +#endif /* _EXCEPTION_H_ */ diff --git a/sys/gnu/i386/fpemul/fpu_arith.c b/sys/gnu/i386/fpemul/fpu_arith.c new file mode 100644 index 0000000..24dff5c --- /dev/null +++ b/sys/gnu/i386/fpemul/fpu_arith.c @@ -0,0 +1,222 @@ +/* + * fpu_arith.c + * + * Code to implement the FPU register/register arithmetic instructions + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + + + +#include "param.h" +#include "proc.h" +#include "machine/cpu.h" +#include "machine/pcb.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "control_w.h" + + +void +fadd__() +{ + /* fadd st,st(i) */ + reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); +} + + +void +fmul__() +{ + /* fmul st,st(i) */ + reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); +} + + + +void +fsub__() +{ + /* fsub st,st(i) */ + reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); +} + + +void +fsubr_() +{ + /* fsubr st,st(i) */ + reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word); +} + + +void +fdiv__() +{ + /* fdiv st,st(i) */ + reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); +} + + +void +fdivr_() +{ + /* fdivr st,st(i) */ + reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word); +} + + + +void +fadd_i() +{ + /* fadd st(i),st */ + reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); +} + + +void +fmul_i() +{ + /* fmul st(i),st */ + reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); +} + + +void +fsubri() +{ + /* fsubr st(i),st */ + /* This is the sense of the 80486 manual reg_sub(&st(FPU_rm), + * FPU_st0_ptr, &st(FPU_rm), control_word); */ + reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); +} + + +void +fsub_i() +{ + /* fsub st(i),st */ + /* This is the sense of the 80486 manual reg_sub(FPU_st0_ptr, + * &st(FPU_rm), &st(FPU_rm), control_word); */ + reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); +} + + +void +fdivri() +{ + /* fdivr st(i),st */ + reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); +} + + +void +fdiv_i() +{ + /* fdiv st(i),st */ + reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); +} + + + +void +faddp_() +{ + /* faddp st(i),st */ + reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); + pop(); +} + + +void +fmulp_() +{ + /* fmulp st(i),st */ + reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); + pop(); +} + + + +void +fsubrp() +{ + /* fsubrp st(i),st */ + /* This is the sense of the 80486 manual reg_sub(&st(FPU_rm), + * FPU_st0_ptr, &st(FPU_rm), control_word); */ + reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); + pop(); +} + + +void +fsubp_() +{ + /* fsubp st(i),st */ + /* This is the sense of the 80486 manual reg_sub(FPU_st0_ptr, + * &st(FPU_rm), &st(FPU_rm), control_word); */ + reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); + pop(); +} + + +void +fdivrp() +{ + /* fdivrp st(i),st */ + reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); + pop(); +} + + +void +fdivp_() +{ + /* fdivp st(i),st */ + reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); + pop(); +} diff --git a/sys/gnu/i386/fpemul/fpu_asm.h b/sys/gnu/i386/fpemul/fpu_asm.h new file mode 100644 index 0000000..51df704 --- /dev/null +++ b/sys/gnu/i386/fpemul/fpu_asm.h @@ -0,0 +1,69 @@ +/* + * fpu_asm.h + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FPU_ASM_H_ +#define _FPU_ASM_H_ + +#include "fpu_emu.h" + +#define EXCEPTION _exception + + +#define PARAM1 8(%ebp) +#define PARAM2 12(%ebp) +#define PARAM3 16(%ebp) +#define PARAM4 20(%ebp) + +#define SIGL_OFFSET 8 +#define SIGN(x) (x) +#define TAG(x) 1(x) +#define EXP(x) 4(x) +#define SIG(x) SIGL_OFFSET/**/(x) +#define SIGL(x) SIGL_OFFSET/**/(x) +#define SIGH(x) 12(x) + +#endif /* _FPU_ASM_H_ */ diff --git a/sys/gnu/i386/fpemul/fpu_aux.c b/sys/gnu/i386/fpemul/fpu_aux.c new file mode 100644 index 0000000..652d384 --- /dev/null +++ b/sys/gnu/i386/fpemul/fpu_aux.c @@ -0,0 +1,220 @@ +/* + * fpu_aux.c + * + * Code to implement some of the FPU auxiliary instructions. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "param.h" +#include "proc.h" +#include "machine/cpu.h" +#include "machine/pcb.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "exception.h" +#include "status_w.h" + + + +void +fclex(void) +{ + status_word &= ~(SW_Backward | SW_Summary | SW_Stack_Fault | SW_Precision | + SW_Underflow | SW_Overflow | SW_Zero_Div | SW_Denorm_Op | + SW_Invalid); + FPU_entry_eip = ip_offset; /* We want no net effect */ +} +/* Needs to be externally visible */ +void +finit() +{ + int r; + control_word = 0x037f; + status_word = 0; + top = 0; /* We don't keep top in the status word + * internally. */ + for (r = 0; r < 8; r++) { + regs[r].tag = TW_Empty; + } + FPU_entry_eip = ip_offset = 0; +} + +static FUNC finit_table[] = { + Un_impl, Un_impl, fclex, finit, Un_impl, Un_impl, Un_impl, Un_impl +}; + +void +finit_() +{ + (finit_table[FPU_rm]) (); +} + + +static void +fstsw_ax(void) +{ + + status_word &= ~SW_Top; + status_word |= (top & 7) << SW_Top_Shift; + + *(short *) &FPU_EAX = status_word; + +} + +static FUNC fstsw_table[] = { + fstsw_ax, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl +}; + +void +fstsw_() +{ + (fstsw_table[FPU_rm]) (); +} + + + +static void +fnop(void) +{ +} + +FUNC fp_nop_table[] = { + fnop, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl +}; + +void +fp_nop() +{ + (fp_nop_table[FPU_rm]) (); +} + + +void +fld_i_() +{ + FPU_REG *st_new_ptr; + + if (STACK_OVERFLOW) { + stack_overflow(); + return; + } + /* fld st(i) */ + if (NOT_EMPTY(FPU_rm)) { + reg_move(&st(FPU_rm), st_new_ptr); + push(); + } else { + if (control_word & EX_Invalid) { + /* The masked response */ + push(); + stack_underflow(); + } else + EXCEPTION(EX_StackUnder); + } + +} + + +void +fxch_i() +{ + /* fxch st(i) */ + FPU_REG t; + register FPU_REG *sti_ptr = &st(FPU_rm); + + if (FPU_st0_tag == TW_Empty) { + if (sti_ptr->tag == TW_Empty) { + stack_underflow(); + stack_underflow_i(FPU_rm); + return; + } + reg_move(sti_ptr, FPU_st0_ptr); + stack_underflow_i(FPU_rm); + return; + } + if (sti_ptr->tag == TW_Empty) { + reg_move(FPU_st0_ptr, sti_ptr); + stack_underflow(); + return; + } + reg_move(FPU_st0_ptr, &t); + reg_move(sti_ptr, FPU_st0_ptr); + reg_move(&t, sti_ptr); +} + + +void +ffree_() +{ + /* ffree st(i) */ + st(FPU_rm).tag = TW_Empty; +} + + +void +ffreep() +{ + /* ffree st(i) + pop - unofficial code */ + st(FPU_rm).tag = TW_Empty; + pop(); +} + + +void +fst_i_() +{ + /* fst st(i) */ + reg_move(FPU_st0_ptr, &st(FPU_rm)); +} + + +void +fstp_i() +{ + /* fstp st(i) */ + reg_move(FPU_st0_ptr, &st(FPU_rm)); + pop(); +} diff --git a/sys/gnu/i386/fpemul/fpu_emu.h b/sys/gnu/i386/fpemul/fpu_emu.h new file mode 100644 index 0000000..6bab229 --- /dev/null +++ b/sys/gnu/i386/fpemul/fpu_emu.h @@ -0,0 +1,175 @@ +/* + * fpu_emu.h + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef _FPU_EMU_H_ +#define _FPU_EMU_H_ + +/* + * Define DENORM_OPERAND to make the emulator detect denormals + * and use the denormal flag of the status word. Note: this only + * affects the flag and corresponding interrupt, the emulator + * will always generate denormals and operate upon them as required. + */ +#define DENORM_OPERAND + +/* + * Define PECULIAR_486 to get a closer approximation to 80486 behaviour, + * rather than behaviour which appears to be cleaner. + * This is a matter of opinion: for all I know, the 80486 may simply + * be complying with the IEEE spec. Maybe one day I'll get to see the + * spec... + */ +#define PECULIAR_486 + +#ifdef LOCORE +#include "fpu_asm.h" +#define Const(x) $/**/x +#else +#define Const(x) x +#endif + +#define EXP_BIAS Const(0) +#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */ +#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */ + +#define SIGN_POS Const(0) +#define SIGN_NEG Const(1) + +/* Keep the order TW_Valid, TW_Zero, TW_Denormal */ +#define TW_Valid Const(0)/* valid */ +#define TW_Zero Const(1)/* zero */ +/* The following fold to 2 (Special) in the Tag Word */ +#define TW_Denormal Const(4)/* De-normal */ +#define TW_Infinity Const(5)/* + or - infinity */ +#define TW_NaN Const(6)/* Not a Number */ + +#define TW_Empty Const(7)/* empty */ + + /* #define TW_FPU_Interrupt Const(0x80) *//* Signals an interrupt */ + + +#ifndef LOCORE + +#include "types.h" +#include "math_emu.h" + +#ifdef PARANOID +extern char emulating; +#define REENTRANT_CHECK(state) emulating = (state) +#define ON 1 +#define OFF 0 +#else +#define REENTRANT_CHECK(state) +#endif /* PARANOID */ + +typedef void (*FUNC) (void); +typedef struct fpu_reg FPU_REG; + +#define st(x) ( regs[((top+x) &7 )] ) + +#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty) +#define NOT_EMPTY(i) (st(i).tag != TW_Empty) +#define NOT_EMPTY_0 (FPU_st0_tag ^ TW_Empty) + +extern unsigned char FPU_rm; + +extern char FPU_st0_tag; +extern FPU_REG *FPU_st0_ptr; + +extern void *FPU_data_address; + +extern FPU_REG FPU_loaded_data; + +#define pop() { FPU_st0_ptr->tag = TW_Empty; top++; } + +/* push() does not affect the tags */ +#define push() { top--; FPU_st0_ptr = st_new_ptr; } + + +#define reg_move(x, y) { \ + *(short *)&((y)->sign) = *(short *)&((x)->sign); \ + *(long *)&((y)->exp) = *(long *)&((x)->exp); \ + *(long long *)&((y)->sigl) = *(long long *)&((x)->sigl); } + + +/*----- Prototypes for functions written in assembler -----*/ +/* extern void reg_move(FPU_REG *a, FPU_REG *b); */ + +extern void mul64(long long *a, long long *b, long long *result); +extern void poly_div2(long long *x); +extern void poly_div4(long long *x); +extern void poly_div16(long long *x); +extern void +polynomial(unsigned accum[], unsigned x[], + unsigned short terms[][4], int n); + extern void normalize(FPU_REG * x); + extern void normalize_nuo(FPU_REG * x); + extern void reg_div(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ, + unsigned int control_w); + extern void reg_u_sub(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ, + unsigned int control_w); + extern void reg_u_mul(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ, + unsigned int control_w); + extern void reg_u_div(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ, + unsigned int control_w); + extern void reg_u_add(FPU_REG * arg1, FPU_REG * arg2, FPU_REG * answ, + unsigned int control_w); + extern void wm_sqrt(FPU_REG * n, unsigned int control_w); + extern unsigned shrx(void *l, unsigned x); + extern unsigned shrxs(void *v, unsigned x); + extern unsigned long div_small(unsigned long long *x, unsigned long y); + extern void round_reg(FPU_REG * arg, unsigned int extent, + unsigned int control_w); + +#ifndef MAKING_PROTO +#include "fpu_proto.h" +#endif + +#endif /* LOCORE */ + +#endif /* _FPU_EMU_H_ */ diff --git a/sys/gnu/i386/fpemul/fpu_entry.c b/sys/gnu/i386/fpemul/fpu_entry.c new file mode 100644 index 0000000..e1b59e4 --- /dev/null +++ b/sys/gnu/i386/fpemul/fpu_entry.c @@ -0,0 +1,500 @@ +/* + * fpu_entry.c + * + * The entry function for wm-FPU-emu + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * The purpose of the copyright is to ensure that the covered software + * remains freely available to everyone. It is felt to be necessary to + * try and prevent the software being taken by unscrupulous people and + * (perhaps after modification) being copyrighted or otherwise + * restricted and being made no longer freely available. There are a + * number of examples of corporations hijacking ideas and trying to + * sue the pants of lesser financed entities who try to subsequently + * use the ideas. + * + * If the software were placed in the public domain then there would + * be no protection at all. By claiming a copyright it puts at least a + * strong moral pressure on the greedy to restrain themselves and + * behave ethically. And let's not be naive, we are all subject to the + * emotion of greed to some extent... + * + * Up until now, the software has been covered by the GNU copyleft. + * That copyright mechanism has problems for operating systems which + * are not themselves covered by the GNU copyleft. Hence I have + * decided to allow the covered software to be used with 386BSD under + * restrictions which are meant to be broadly similar, but not + * identical, to those which already cover the 386BSD operating + * system. + * + * The software, in its form for the Linux operating system, remains + * available under the terms of the GNU copyleft. + * + * W. Metzenthen June 1993. + */ + +/*---------------------------------------------------------------------------+ + | Note: | + | The file contains code which accesses user memory. | + | Emulator static data may change when user memory is accessed, due to | + | other processes using the emulator while swapping is in progress. | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | math_emulate() is the sole entry point for wm-FPU-emu | + +---------------------------------------------------------------------------*/ + + +#include "param.h" +#include "systm.h" +#include "proc.h" +#include "machine/cpu.h" +#include "machine/pcb.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "exception.h" +#include "control_w.h" +#include "status_w.h" + + +#define __BAD__ Un_impl /* Not implemented */ + +#define FPU_LOOKAHEAD 1 /* For performance boost */ + +#if FPU_LOOKAHEAD != 0 /* I think thet we have to limit the */ +#define LOOKAHEAD_LIMIT 7 /* Max number of lookahead instructions*/ +#endif /* Or else a prog consisting of a million */ + /* fnops will spend all its time in kernel*/ + +#ifndef NO_UNDOC_CODE /* Un-documented FPU op-codes supported by + * default. */ + +/* WARNING: These codes are not documented by Intel in their 80486 manual + and may not work on FPU clones or later Intel FPUs. */ + +/* Changes to support the un-doc codes provided by Linus Torvalds. */ + +#define _d9_d8_ fstp_i /* unofficial code (19) */ +#define _dc_d0_ fcom_st /* unofficial code (14) */ +#define _dc_d8_ fcompst /* unofficial code (1c) */ +#define _dd_c8_ fxch_i /* unofficial code (0d) */ +#define _de_d0_ fcompst /* unofficial code (16) */ +#define _df_c0_ ffreep /* unofficial code (07) ffree + pop */ +#define _df_c8_ fxch_i /* unofficial code (0f) */ +#define _df_d0_ fstp_i /* unofficial code (17) */ +#define _df_d8_ fstp_i /* unofficial code (1f) */ + +static FUNC st_instr_table[64] = { + fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_, + fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_, + fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_, + fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_, + fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, + fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, + fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, + fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, +}; +#else /* Support only documented FPU op-codes */ + +static FUNC st_instr_table[64] = { + fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__, + fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__, + fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__, + fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__, + fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, + fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, + fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, + fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, +}; +#endif /* NO_UNDOC_CODE */ + + +#define _NONE_ 0 /* Take no special action */ +#define _REG0_ 1 /* Need to check for not empty st(0) */ +#define _REGI_ 2 /* Need to check for not empty st(0) and + * st(rm) */ +#define _REGi_ 0 /* Uses st(rm) */ +#define _PUSH_ 3 /* Need to check for space to push onto stack */ +#define _null_ 4 /* Function illegal or not implemented */ +#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */ +#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm) + * then pop */ +#define _REGIc 0 /* Compare st(0) and st(rm) */ +#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks + * later */ + +#ifndef NO_UNDOC_CODE + +/* Un-documented FPU op-codes supported by default. (see above) */ + +static unsigned char type_table[64] = { + _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_, + _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_, + _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, + _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, + _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_, + _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_, + _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_, + _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_ +}; +#else /* Support only documented FPU op-codes */ + +static unsigned char type_table[64] = { + _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_, + _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_, + _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_, + _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_, + _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_, + _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_, + _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_, + _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_ +}; +#endif /* NO_UNDOC_CODE */ + +/* Be careful when using any of these global variables... + they might change if swapping is triggered */ +unsigned char FPU_rm; +char FPU_st0_tag; +FPU_REG *FPU_st0_ptr; + +#ifdef PARANOID +char emulating = 0; +#endif /* PARANOID */ + +#define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x)) +#define math_abort(signo) \ + FPU_EIP = FPU_ORIG_EIP;REENTRANT_CHECK(OFF);return(signo); + +int +math_emulate(struct trapframe * tframe) +{ + + unsigned char FPU_modrm; + unsigned short code; +#ifdef LOOKAHEAD_LIMIT + int lookahead_limit = LOOKAHEAD_LIMIT; +#endif +#ifdef PARANOID + if (emulating) { + printf("ERROR: wm-FPU-emu is not RE-ENTRANT!\n"); + } + REENTRANT_CHECK(ON); +#endif /* PARANOID */ + + if ((((struct pcb *) curproc->p_addr)->pcb_flags & FP_SOFTFP) == 0) { + finit(); + ((struct pcb *) curproc->p_addr)->pcb_flags |= FP_SOFTFP; + } + FPU_info = tframe; + FPU_ORIG_EIP = FPU_EIP; /* --pink-- */ + + if (FPU_CS != 0x001f) { + printf("math_emulate: %x : %x\n", FPU_CS, FPU_EIP); + panic("FPU emulation in kernel"); + } +#ifdef notyet + /* We cannot handle emulation in v86-mode */ + if (FPU_EFLAGS & 0x00020000) { + FPU_ORIG_EIP = FPU_EIP; + math_abort(FPU_info, SIGILL); + } +#endif + + FPU_lookahead = FPU_LOOKAHEAD; +#if notnow /* I dont know that much of traceing. Is it right? --pink-- */ + if (curproc->p_flag & STRC) + FPU_lookahead = 0; +#endif + +do_another_FPU_instruction: + + REENTRANT_CHECK(OFF); + code = fuword((u_int *) FPU_EIP); + REENTRANT_CHECK(ON); + if ((code & 0xff) == 0x9b) { /* fwait */ + if (status_word & SW_Summary) + goto do_the_FPU_interrupt; + else { + FPU_EIP++; + goto FPU_instruction_done; + } + } + if (status_word & SW_Summary) { + /* Ignore the error for now if the current instruction is a + * no-wait control instruction */ + /* The 80486 manual contradicts itself on this topic, so I use + * the following list of such instructions until I can check + * on a real 80486: fninit, fnstenv, fnsave, fnstsw, fnstenv, + * fnclex. */ + if (!((((code & 0xf803) == 0xe003) || /* fnclex, fninit, + * fnstsw */ + (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, + * fnstenv, fnstsw */ + ((code & 0xc000) != 0xc000))))) { + /* This is a guess about what a real FPU might do to + * this bit: */ +/* status_word &= ~SW_Summary; ****/ + + /* We need to simulate the action of the kernel to FPU + * interrupts here. Currently, the "real FPU" part of + * the kernel (0.99.10) clears the exception flags, + * sets the registers to empty, and passes information + * back to the interrupted process via the cs selector + * and operand selector, so we do the same. */ + do_the_FPU_interrupt: + cs_selector &= 0xffff0000; + cs_selector |= (status_word & ~SW_Top) | ((top & 7) << SW_Top_Shift); + operand_selector = tag_word(); + status_word = 0; + top = 0; + { + int r; + for (r = 0; r < 8; r++) { + regs[r].tag = TW_Empty; + } + } + REENTRANT_CHECK(OFF); + math_abort(SIGFPE); + } + } + FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP; + + if ((code & 0xff) == 0x66) { /* size prefix */ + FPU_EIP++; + REENTRANT_CHECK(OFF); + code = fuword((u_int *) FPU_EIP); + REENTRANT_CHECK(ON); + } + FPU_EIP += 2; + + FPU_modrm = code >> 8; + FPU_rm = FPU_modrm & 7; + + if (FPU_modrm < 0300) { + /* All of these instructions use the mod/rm byte to get a data + * address */ + get_address(FPU_modrm); + if (!(code & 1)) { + unsigned short status1 = status_word; + FPU_st0_ptr = &st(0); + FPU_st0_tag = FPU_st0_ptr->tag; + + /* Stack underflow has priority */ + if (NOT_EMPTY_0) { + switch ((code >> 1) & 3) { + case 0: + reg_load_single(); + break; + case 1: + reg_load_int32(); + break; + case 2: + reg_load_double(); + break; + case 3: + reg_load_int16(); + break; + } + + /* No more access to user memory, it is safe + * to use static data now */ + FPU_st0_ptr = &st(0); + FPU_st0_tag = FPU_st0_ptr->tag; + + /* NaN operands have the next priority. */ + /* We have to delay looking at st(0) until + * after loading the data, because that data + * might contain an SNaN */ + if ((FPU_st0_tag == TW_NaN) || + (FPU_loaded_data.tag == TW_NaN)) { + /* Restore the status word; we might + * have loaded a denormal. */ + status_word = status1; + if ((FPU_modrm & 0x30) == 0x10) { + /* fcom or fcomp */ + EXCEPTION(EX_Invalid); + setcc(SW_C3 | SW_C2 | SW_C0); + if (FPU_modrm & 0x08) + pop(); /* fcomp, so we pop. */ + } else + real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr); + goto reg_mem_instr_done; + } + switch ((FPU_modrm >> 3) & 7) { + case 0: /* fadd */ + reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); + break; + case 1: /* fmul */ + reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); + break; + case 2: /* fcom */ + compare_st_data(); + break; + case 3: /* fcomp */ + compare_st_data(); + pop(); + break; + case 4: /* fsub */ + reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); + break; + case 5: /* fsubr */ + reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word); + break; + case 6: /* fdiv */ + reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); + break; + case 7: /* fdivr */ + if (FPU_st0_tag == TW_Zero) + status_word = status1; /* Undo any denorm tag, + * zero-divide has + * priority. */ + reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word); + break; + } + } else { + if ((FPU_modrm & 0x30) == 0x10) { + /* The instruction is fcom or fcomp */ + EXCEPTION(EX_StackUnder); + setcc(SW_C3 | SW_C2 | SW_C0); + if (FPU_modrm & 0x08) + pop(); /* fcomp, Empty or not, + * we pop. */ + } else + stack_underflow(); + } + } else { + load_store_instr(((FPU_modrm & 0x38) | (code & 6)) >> 1); + } + +reg_mem_instr_done: + + data_operand_offset = (unsigned long) FPU_data_address; + } else { + /* None of these instructions access user memory */ + unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7); + + FPU_st0_ptr = &st(0); + FPU_st0_tag = FPU_st0_ptr->tag; + switch (type_table[(int) instr_index]) { + case _NONE_: /* also _REGIc: _REGIn */ + break; + case _REG0_: + if (!NOT_EMPTY_0) { + stack_underflow(); + goto FPU_instruction_done; + } + break; + case _REGIi: + if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) { + stack_underflow_i(FPU_rm); + goto FPU_instruction_done; + } + break; + case _REGIp: + if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) { + stack_underflow_i(FPU_rm); + pop(); + goto FPU_instruction_done; + } + break; + case _REGI_: + if (!NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm)) { + stack_underflow(); + goto FPU_instruction_done; + } + break; + case _PUSH_: /* Only used by the fld st(i) instruction */ + break; + case _null_: + Un_impl(); + goto FPU_instruction_done; + default: + EXCEPTION(EX_INTERNAL | 0x111); + goto FPU_instruction_done; + } + (*st_instr_table[(int) instr_index]) (); + } + +FPU_instruction_done: + + ip_offset = FPU_entry_eip; + bswapw(code); + *(1 + (unsigned short *) &cs_selector) = code & 0x7ff; + +#ifdef DEBUG + REENTRANT_CHECK(OFF); + emu_printall(); + REENTRANT_CHECK(ON); +#endif /* DEBUG */ +#ifdef LOOKAHEAD_LIMIT +if (--lookahead_limit) +#endif + if (FPU_lookahead) { + unsigned char next; + + /* (This test should generate no machine code) */ + while (1) { + REENTRANT_CHECK(OFF); + next = fubyte((u_char *) FPU_EIP); + REENTRANT_CHECK(ON); + if (((next & 0xf8) == 0xd8) || (next == 0x9b)) { /* fwait */ + goto do_another_FPU_instruction; + } else + if (next == 0x66) { /* size prefix */ + REENTRANT_CHECK(OFF); + next = fubyte((u_char *) (FPU_EIP + 1)); + REENTRANT_CHECK(ON); + if ((next & 0xf8) == 0xd8) { + FPU_EIP++; + goto do_another_FPU_instruction; + } + } + break; + } + } + REENTRANT_CHECK(OFF); + return (0); /* --pink-- */ +} diff --git a/sys/gnu/i386/fpemul/fpu_etc.c b/sys/gnu/i386/fpemul/fpu_etc.c new file mode 100644 index 0000000..72f4526 --- /dev/null +++ b/sys/gnu/i386/fpemul/fpu_etc.c @@ -0,0 +1,162 @@ +/* + * fpu_etc.c + * + * Implement a few FPU instructions. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "param.h" +#include "proc.h" +#include "machine/cpu.h" +#include "machine/pcb.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "exception.h" +#include "status_w.h" +#include "reg_constant.h" + + +static void +fchs(void) +{ + if (NOT_EMPTY_0) { + FPU_st0_ptr->sign ^= SIGN_POS ^ SIGN_NEG; + status_word &= ~SW_C1; + } else + stack_underflow(); +} + +static void +fabs(void) +{ + if (FPU_st0_tag ^ TW_Empty) { + FPU_st0_ptr->sign = SIGN_POS; + status_word &= ~SW_C1; + } else + stack_underflow(); +} + + +static void +ftst_(void) +{ + switch (FPU_st0_tag) { + case TW_Zero: + setcc(SW_C3); + break; + case TW_Valid: + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + if (FPU_st0_ptr->sign == SIGN_POS) + setcc(0); + else + setcc(SW_C0); + break; + case TW_NaN: + setcc(SW_C0 | SW_C2 | SW_C3); /* Operand is not comparable */ + EXCEPTION(EX_Invalid); + break; + case TW_Infinity: + if (FPU_st0_ptr->sign == SIGN_POS) + setcc(0); + else + setcc(SW_C0); + EXCEPTION(EX_Invalid); + break; + case TW_Empty: + setcc(SW_C0 | SW_C2 | SW_C3); + EXCEPTION(EX_StackUnder); + break; + default: + setcc(SW_C0 | SW_C2 | SW_C3); /* Operand is not comparable */ + EXCEPTION(EX_INTERNAL | 0x14); + break; + } +} + +static void +fxam(void) +{ + int c = 0; + switch (FPU_st0_tag) { + case TW_Empty: + c = SW_C3 | SW_C0; + break; + case TW_Zero: + c = SW_C3; + break; + case TW_Valid: + /* This will need to be changed if TW_Denormal is ever used. */ + if (FPU_st0_ptr->exp <= EXP_UNDER) + c = SW_C2 | SW_C3; /* Denormal */ + else + c = SW_C3; + break; + case TW_NaN: + c = SW_C0; + break; + case TW_Infinity: + c = SW_C2 | SW_C0; + break; + } + if (FPU_st0_ptr->sign == SIGN_NEG) + c |= SW_C1; + setcc(c); +} + +static FUNC fp_etc_table[] = { + fchs, fabs, Un_impl, Un_impl, ftst_, fxam, Un_impl, Un_impl +}; + +void +fp_etc() +{ + (fp_etc_table[FPU_rm]) (); +} diff --git a/sys/gnu/i386/fpemul/fpu_proto.h b/sys/gnu/i386/fpemul/fpu_proto.h new file mode 100644 index 0000000..8ce7ed4 --- /dev/null +++ b/sys/gnu/i386/fpemul/fpu_proto.h @@ -0,0 +1,108 @@ +/* errors.c */ +extern void Un_impl(void); +extern void emu_printall(void); +extern void exception(int n); +extern void real_2op_NaN(FPU_REG * a, FPU_REG * b, FPU_REG * dest); +extern void arith_invalid(FPU_REG * dest); +extern void divide_by_zero(int sign, FPU_REG * dest); +extern void set_precision_flag_up(void); +extern void set_precision_flag_down(void); +extern int denormal_operand(void); +extern void arith_overflow(FPU_REG * dest); +extern void arith_underflow(FPU_REG * dest); +extern void stack_overflow(void); +extern void stack_underflow(void); +extern void stack_underflow_i(int i); +extern void stack_underflow_pop(int i); +/* fpu_arith.c */ +extern void fadd__(void); +extern void fmul__(void); +extern void fsub__(void); +extern void fsubr_(void); +extern void fdiv__(void); +extern void fdivr_(void); +extern void fadd_i(void); +extern void fmul_i(void); +extern void fsubri(void); +extern void fsub_i(void); +extern void fdivri(void); +extern void fdiv_i(void); +extern void faddp_(void); +extern void fmulp_(void); +extern void fsubrp(void); +extern void fsubp_(void); +extern void fdivrp(void); +extern void fdivp_(void); +/* fpu_aux.c */ +extern void fclex(void); +extern void finit(void); +extern void finit_(void); +extern void fstsw_(void); +extern void fp_nop(void); +extern void fld_i_(void); +extern void fxch_i(void); +extern void ffree_(void); +extern void ffreep(void); +extern void fst_i_(void); +extern void fstp_i(void); +/* fpu_entry.c */ +extern int math_emulate(struct trapframe * info); +/* fpu_etc.c */ +extern void fp_etc(void); +/* fpu_trig.c */ +extern void convert_l2reg(long *arg, FPU_REG * dest); +extern void trig_a(void); +extern void trig_b(void); +/* get_address.c */ +extern void get_address(unsigned char FPU_modrm); +/* load_store.c */ +extern void load_store_instr(char type); +/* poly_2xm1.c */ +extern int poly_2xm1(FPU_REG * arg, FPU_REG * result); +/* poly_atan.c */ +extern void poly_atan(FPU_REG * arg); +extern void poly_add_1(FPU_REG * src); +/* poly_l2.c */ +extern void poly_l2(FPU_REG * arg, FPU_REG * result); +extern int poly_l2p1(FPU_REG * arg, FPU_REG * result); +/* poly_sin.c */ +extern void poly_sine(FPU_REG * arg, FPU_REG * result); +/* poly_tan.c */ +extern void poly_tan(FPU_REG * arg, FPU_REG * y_reg); +/* reg_add_sub.c */ +extern void reg_add(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w); +extern void reg_sub(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w); +/* reg_compare.c */ +extern int compare(FPU_REG * b); +extern int compare_st_data(void); +extern void fcom_st(void); +extern void fcompst(void); +extern void fcompp(void); +extern void fucom_(void); +extern void fucomp(void); +extern void fucompp(void); +/* reg_constant.c */ +extern void fconst(void); +/* reg_ld_str.c */ +extern void reg_load_extended(void); +extern void reg_load_double(void); +extern void reg_load_single(void); +extern void reg_load_int64(void); +extern void reg_load_int32(void); +extern void reg_load_int16(void); +extern void reg_load_bcd(void); +extern int reg_store_extended(void); +extern int reg_store_double(void); +extern int reg_store_single(void); +extern int reg_store_int64(void); +extern int reg_store_int32(void); +extern int reg_store_int16(void); +extern int reg_store_bcd(void); +extern int round_to_int(FPU_REG * r); +extern char *fldenv(void); +extern void frstor(void); +extern unsigned short tag_word(void); +extern char *fstenv(void); +extern void fsave(void); +/* reg_mul.c */ +extern void reg_mul(FPU_REG * a, FPU_REG * b, FPU_REG * dest, unsigned int control_w); diff --git a/sys/gnu/i386/fpemul/fpu_system.h b/sys/gnu/i386/fpemul/fpu_system.h new file mode 100644 index 0000000..e0488e9 --- /dev/null +++ b/sys/gnu/i386/fpemul/fpu_system.h @@ -0,0 +1,84 @@ +/* + * fpu_system.h + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef _FPU_SYSTEM_H +#define _FPU_SYSTEM_H + +/* system dependent definitions */ + +/* +#include <linux/sched.h> +#include <linux/kernel.h> +*/ + +#define I387 (*(union i387_union *)&(((struct pcb *)curproc->p_addr)->pcb_savefpu)) +#define FPU_info (I387.soft.frame) + +#define FPU_CS (*(unsigned short *) &(FPU_info->tf_cs)) +#define FPU_DS (*(unsigned short *) &(FPU_info->tf_ds)) +#define FPU_EAX (FPU_info->tf_eax) +#define FPU_EFLAGS (FPU_info->tf_eflags) +#define FPU_EIP (FPU_info->tf_eip) +/*#define FPU_ORIG_EIP (FPU_info->___orig_eip) */ +/*#define FPU_ORIG_EIP (FPU_info->tf_isp)*/ +#define FPU_ORIG_EIP (I387.soft.orig_eip) + +#define FPU_lookahead (I387.soft.lookahead) +#define FPU_entry_eip (I387.soft.entry_eip) + +#define status_word (I387.soft.swd) +#define control_word (I387.soft.cwd) +#define regs (I387.soft.regs) +#define top (I387.soft.top) + +#define ip_offset (I387.soft.fip) +#define cs_selector (I387.soft.fcs) +#define data_operand_offset (I387.soft.foo) +#define operand_selector (I387.soft.fos) + +#endif diff --git a/sys/gnu/i386/fpemul/fpu_trig.c b/sys/gnu/i386/fpemul/fpu_trig.c new file mode 100644 index 0000000..b83f615 --- /dev/null +++ b/sys/gnu/i386/fpemul/fpu_trig.c @@ -0,0 +1,1354 @@ +/* + * fpu_trig.c + * + * Implementation of the FPU "transcendental" functions. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "param.h" +#include "proc.h" +#include "machine/cpu.h" +#include "machine/pcb.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "exception.h" +#include "status_w.h" +#include "reg_constant.h" +#include "control_w.h" + +static int +trig_arg(FPU_REG * X) +{ + FPU_REG tmp, quot; + int rv; + long long q; + int old_cw = control_word; + + control_word &= ~CW_RC; + control_word |= RC_CHOP; + + reg_move(X, "); + reg_div(", &CONST_PI2, ", FULL_PRECISION); + + reg_move(", &tmp); + round_to_int(&tmp); + if (tmp.sigh & 0x80000000) + return -1; /* |Arg| is >= 2^63 */ + tmp.exp = EXP_BIAS + 63; + q = *(long long *) &(tmp.sigl); + normalize(&tmp); + + reg_sub(", &tmp, X, FULL_PRECISION); + rv = q & 7; + + control_word = old_cw; + return rv;; +} + + +/* Convert a long to register */ +void +convert_l2reg(long *arg, FPU_REG * dest) +{ + long num = *arg; + + if (num == 0) { + reg_move(&CONST_Z, dest); + return; + } + if (num > 0) + dest->sign = SIGN_POS; + else { + num = -num; + dest->sign = SIGN_NEG; + } + + dest->sigh = num; + dest->sigl = 0; + dest->exp = EXP_BIAS + 31; + dest->tag = TW_Valid; + normalize(dest); +} + + +static void +single_arg_error(void) +{ + switch (FPU_st0_tag) { + case TW_NaN: + if (!(FPU_st0_ptr->sigh & 0x40000000)) { /* Signaling ? */ + EXCEPTION(EX_Invalid); + /* Convert to a QNaN */ + FPU_st0_ptr->sigh |= 0x40000000; + } + break; /* return with a NaN in st(0) */ + case TW_Empty: + stack_underflow(); /* Puts a QNaN in st(0) */ + break; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL | 0x0112); +#endif /* PARANOID */ + } +} + + +/*---------------------------------------------------------------------------*/ + +static void +f2xm1(void) +{ + switch (FPU_st0_tag) { + case TW_Valid: + { + FPU_REG rv, tmp; + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + if (FPU_st0_ptr->sign == SIGN_POS) { + /* poly_2xm1(x) requires 0 < x < 1. */ + if (poly_2xm1(FPU_st0_ptr, &rv)) + return; /* error */ + reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); + } else { +/* **** Should change poly_2xm1() to at least handle numbers near 0 */ + /* poly_2xm1(x) doesn't handle negative + * numbers. */ + /* So we compute (poly_2xm1(x+1)-1)/2, for -1 + * < x < 0 */ + reg_add(FPU_st0_ptr, &CONST_1, &tmp, FULL_PRECISION); + poly_2xm1(&tmp, &rv); + reg_mul(&rv, &tmp, &tmp, FULL_PRECISION); + reg_sub(&tmp, &CONST_1, FPU_st0_ptr, FULL_PRECISION); + FPU_st0_ptr->exp--; + if (FPU_st0_ptr->exp <= EXP_UNDER) + arith_underflow(FPU_st0_ptr); + } + return; + } + case TW_Zero: + return; + case TW_Infinity: + if (FPU_st0_ptr->sign == SIGN_NEG) { + /* -infinity gives -1 (p16-10) */ + reg_move(&CONST_1, FPU_st0_ptr); + FPU_st0_ptr->sign = SIGN_NEG; + } + return; + default: + single_arg_error(); + } +} + +static void +fptan(void) +{ + FPU_REG *st_new_ptr; + int q; + char arg_sign = FPU_st0_ptr->sign; + + if (STACK_OVERFLOW) { + stack_overflow(); + return; + } + switch (FPU_st0_tag) { + case TW_Valid: + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + FPU_st0_ptr->sign = SIGN_POS; + if ((q = trig_arg(FPU_st0_ptr)) != -1) { + if (q & 1) + reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); + + poly_tan(FPU_st0_ptr, FPU_st0_ptr); + + FPU_st0_ptr->sign = (q & 1) ^ arg_sign; + + if (FPU_st0_ptr->exp <= EXP_UNDER) + arith_underflow(FPU_st0_ptr); + + push(); + reg_move(&CONST_1, FPU_st0_ptr); + setcc(0); + } else { + /* Operand is out of range */ + setcc(SW_C2); + FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ + return; + } + break; + case TW_Infinity: + /* Operand is out of range */ + setcc(SW_C2); + FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ + return; + case TW_Zero: + push(); + reg_move(&CONST_1, FPU_st0_ptr); + setcc(0); + break; + default: + single_arg_error(); + break; + } +} + + +static void +fxtract(void) +{ + FPU_REG *st_new_ptr; + register FPU_REG *st1_ptr = FPU_st0_ptr; /* anticipate */ + + if (STACK_OVERFLOW) { + stack_overflow(); + return; + } + if (!(FPU_st0_tag ^ TW_Valid)) { + long e; + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + push(); + reg_move(st1_ptr, FPU_st0_ptr); + FPU_st0_ptr->exp = EXP_BIAS; + e = st1_ptr->exp - EXP_BIAS; + convert_l2reg(&e, st1_ptr); + return; + } else + if (FPU_st0_tag == TW_Zero) { + char sign = FPU_st0_ptr->sign; + divide_by_zero(SIGN_NEG, FPU_st0_ptr); + push(); + reg_move(&CONST_Z, FPU_st0_ptr); + FPU_st0_ptr->sign = sign; + return; + } else + if (FPU_st0_tag == TW_Infinity) { + char sign = FPU_st0_ptr->sign; + FPU_st0_ptr->sign = SIGN_POS; + push(); + reg_move(&CONST_INF, FPU_st0_ptr); + FPU_st0_ptr->sign = sign; + return; + } else + if (FPU_st0_tag == TW_NaN) { + if (!(FPU_st0_ptr->sigh & 0x40000000)) { /* Signaling ? */ + EXCEPTION(EX_Invalid); + /* Convert to a QNaN */ + FPU_st0_ptr->sigh |= 0x40000000; + } + push(); + reg_move(st1_ptr, FPU_st0_ptr); + return; + } else + if (FPU_st0_tag == TW_Empty) { + /* Is this the correct + * behaviour? */ + if (control_word & EX_Invalid) { + stack_underflow(); + push(); + stack_underflow(); + } else + EXCEPTION(EX_StackUnder); + } +#ifdef PARANOID + else + EXCEPTION(EX_INTERNAL | 0x119); +#endif /* PARANOID */ +} + + +static void +fdecstp(void) +{ + top--; /* FPU_st0_ptr will be fixed in math_emulate() + * before the next instr */ +} + +static void +fincstp(void) +{ + top++; /* FPU_st0_ptr will be fixed in math_emulate() + * before the next instr */ +} + + +static void +fsqrt_(void) +{ + if (!(FPU_st0_tag ^ TW_Valid)) { + int expon; + + if (FPU_st0_ptr->sign == SIGN_NEG) { + arith_invalid(FPU_st0_ptr); /* sqrt(negative) is + * invalid */ + return; + } +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + expon = FPU_st0_ptr->exp - EXP_BIAS; + FPU_st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 + * .. 4.0) */ + + wm_sqrt(FPU_st0_ptr, control_word); /* Do the computation */ + + FPU_st0_ptr->exp += expon >> 1; + FPU_st0_ptr->sign = SIGN_POS; + } else + if (FPU_st0_tag == TW_Zero) + return; + else + if (FPU_st0_tag == TW_Infinity) { + if (FPU_st0_ptr->sign == SIGN_NEG) + arith_invalid(FPU_st0_ptr); /* sqrt(-Infinity) is + * invalid */ + return; + } else { + single_arg_error(); + return; + } + +} + + +static void +frndint_(void) +{ + if (!(FPU_st0_tag ^ TW_Valid)) { + if (FPU_st0_ptr->exp > EXP_BIAS + 63) + return; + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + round_to_int(FPU_st0_ptr); /* Fortunately, this can't + * overflow to 2^64 */ + FPU_st0_ptr->exp = EXP_BIAS + 63; + normalize(FPU_st0_ptr); + return; + } else + if ((FPU_st0_tag == TW_Zero) || (FPU_st0_tag == TW_Infinity)) + return; + else + single_arg_error(); +} + + +static void +fsin(void) +{ + char arg_sign = FPU_st0_ptr->sign; + + if (FPU_st0_tag == TW_Valid) { + int q; + FPU_st0_ptr->sign = SIGN_POS; + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + if ((q = trig_arg(FPU_st0_ptr)) != -1) { + FPU_REG rv; + + if (q & 1) + reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); + + poly_sine(FPU_st0_ptr, &rv); + + setcc(0); + if (q & 2) + rv.sign ^= SIGN_POS ^ SIGN_NEG; + rv.sign ^= arg_sign; + reg_move(&rv, FPU_st0_ptr); + + if (FPU_st0_ptr->exp <= EXP_UNDER) + arith_underflow(FPU_st0_ptr); + + set_precision_flag_up(); /* We do not really know + * if up or down */ + + return; + } else { + /* Operand is out of range */ + setcc(SW_C2); + FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ + return; + } + } else + if (FPU_st0_tag == TW_Zero) { + setcc(0); + return; + } else + if (FPU_st0_tag == TW_Infinity) { + /* Operand is out of range */ + setcc(SW_C2); + FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ + return; + } else + single_arg_error(); +} + + +static int +f_cos(FPU_REG * arg) +{ + char arg_sign = arg->sign; + + if (arg->tag == TW_Valid) { + int q; + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return 1; +#endif /* DENORM_OPERAND */ + + arg->sign = SIGN_POS; + if ((q = trig_arg(arg)) != -1) { + FPU_REG rv; + + if (!(q & 1)) + reg_sub(&CONST_1, arg, arg, FULL_PRECISION); + + poly_sine(arg, &rv); + + setcc(0); + if ((q + 1) & 2) + rv.sign ^= SIGN_POS ^ SIGN_NEG; + reg_move(&rv, arg); + + set_precision_flag_up(); /* We do not really know + * if up or down */ + + return 0; + } else { + /* Operand is out of range */ + setcc(SW_C2); + arg->sign = arg_sign; /* restore st(0) */ + return 1; + } + } else + if (arg->tag == TW_Zero) { + reg_move(&CONST_1, arg); + setcc(0); + return 0; + } else + if (FPU_st0_tag == TW_Infinity) { + /* Operand is out of range */ + setcc(SW_C2); + arg->sign = arg_sign; /* restore st(0) */ + return 1; + } else { + single_arg_error(); /* requires arg == + * &st(0) */ + return 1; + } +} + + +static void +fcos(void) +{ + f_cos(FPU_st0_ptr); +} + + +static void +fsincos(void) +{ + FPU_REG *st_new_ptr; + FPU_REG arg; + + if (STACK_OVERFLOW) { + stack_overflow(); + return; + } + reg_move(FPU_st0_ptr, &arg); + if (!f_cos(&arg)) { + fsin(); + push(); + reg_move(&arg, FPU_st0_ptr); + } +} + + +/*---------------------------------------------------------------------------*/ +/* The following all require two arguments: st(0) and st(1) */ + +/* remainder of st(0) / st(1) */ +/* Assumes that st(0) and st(1) are both TW_Valid */ +static void +fprem_kernel(int round) +{ + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + + if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) { + FPU_REG tmp; + int old_cw = control_word; + int expdif = FPU_st0_ptr->exp - (st1_ptr)->exp; + +#ifdef DENORM_OPERAND + if (((FPU_st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + control_word &= ~CW_RC; + control_word |= round; + + if (expdif < 64) { + /* This should be the most common case */ + long long q; + int c = 0; + + reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION); + + round_to_int(&tmp); /* Fortunately, this can't + * overflow to 2^64 */ + tmp.exp = EXP_BIAS + 63; + q = *(long long *) &(tmp.sigl); + normalize(&tmp); + + reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION); + reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION); + + if (q & 4) + c |= SW_C3; + if (q & 2) + c |= SW_C1; + if (q & 1) + c |= SW_C0; + + setcc(c); + } else { + /* There is a large exponent difference ( >= 64 ) */ + int N_exp; + + reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION); + /* N is 'a number between 32 and 63' (p26-113) */ + N_exp = (tmp.exp & 31) + 32; + tmp.exp = EXP_BIAS + N_exp; + + round_to_int(&tmp); /* Fortunately, this can't + * overflow to 2^64 */ + tmp.exp = EXP_BIAS + 63; + normalize(&tmp); + + tmp.exp = EXP_BIAS + expdif - N_exp; + + reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION); + reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION); + + setcc(SW_C2); + } + control_word = old_cw; + + if (FPU_st0_ptr->exp <= EXP_UNDER) + arith_underflow(FPU_st0_ptr); + return; + } else + if ((FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty)) { + stack_underflow(); + return; + } else + if (FPU_st0_tag == TW_Zero) { + if (st1_tag == TW_Valid) { + +#ifdef DENORM_OPERAND + if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + setcc(0); + return; + } else + if (st1_tag == TW_Zero) { + arith_invalid(FPU_st0_ptr); + return; + } + /* fprem(?,0) always invalid */ + else + if (st1_tag == TW_Infinity) { + setcc(0); + return; + } + } else + if (FPU_st0_tag == TW_Valid) { + if (st1_tag == TW_Zero) { + arith_invalid(FPU_st0_ptr); /* fprem(Valid,Zero) is + * invalid */ + return; + } else + if (st1_tag != TW_NaN) { +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + if (st1_tag == TW_Infinity) { + /* fprem(Valid, + * Infinity) + * is o.k. */ + setcc(0); + return; + } + } + } else + if (FPU_st0_tag == TW_Infinity) { + if (st1_tag != TW_NaN) { + arith_invalid(FPU_st0_ptr); /* fprem(Infinity,?) is + * invalid */ + return; + } + } + /* One of the registers must contain a NaN is we got here. */ + +#ifdef PARANOID + if ((FPU_st0_tag != TW_NaN) && (st1_tag != TW_NaN)) + EXCEPTION(EX_INTERNAL | 0x118); +#endif /* PARANOID */ + + real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); + +} + + +/* ST(1) <- ST(1) * log ST; pop ST */ +static void +fyl2x(void) +{ + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + + if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) { + if (FPU_st0_ptr->sign == SIGN_POS) { + int saved_control, saved_status; + +#ifdef DENORM_OPERAND + if (((FPU_st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + /* We use the general purpose arithmetic, so we need + * to save these. */ + saved_status = status_word; + saved_control = control_word; + control_word = FULL_PRECISION; + + poly_l2(FPU_st0_ptr, FPU_st0_ptr); + + /* Enough of the basic arithmetic is done now */ + control_word = saved_control; + status_word = saved_status; + + /* Let the multiply set the flags */ + reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); + + pop(); + FPU_st0_ptr = &st(0); + } else { + /* negative */ + pop(); + FPU_st0_ptr = &st(0); + arith_invalid(FPU_st0_ptr); /* st(0) cannot be + * negative */ + return; + } + } else + if ((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty)) { + stack_underflow_pop(1); + return; + } else + if ((FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN)) { + real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); + pop(); + return; + } else + if ((FPU_st0_tag <= TW_Zero) && (st1_tag <= TW_Zero)) { + /* one of the args is zero, the other + * valid, or both zero */ + if (FPU_st0_tag == TW_Zero) { + pop(); + FPU_st0_ptr = &st(0); + if (FPU_st0_ptr->tag == TW_Zero) + arith_invalid(FPU_st0_ptr); /* Both args zero is + * invalid */ +#ifdef PECULIAR_486 + /* This case is not + * specifically covered in the + * manual, but divide-by-zero + * would seem to be the best + * response. However, a real + * 80486 does it this way... */ + else + if (FPU_st0_ptr->tag == TW_Infinity) { + reg_move(&CONST_INF, FPU_st0_ptr); + return; + } +#endif /* PECULIAR_486 */ + else + divide_by_zero(st1_ptr->sign ^ SIGN_NEG ^ SIGN_POS, FPU_st0_ptr); + return; + } else { + /* st(1) contains zero, st(0) + * valid <> 0 */ + /* Zero is the valid answer */ + char sign = st1_ptr->sign; + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + if (FPU_st0_ptr->sign == SIGN_NEG) { + pop(); + FPU_st0_ptr = &st(0); + arith_invalid(FPU_st0_ptr); /* log(negative) */ + return; + } + if (FPU_st0_ptr->exp < EXP_BIAS) + sign ^= SIGN_NEG ^ SIGN_POS; + pop(); + FPU_st0_ptr = &st(0); + reg_move(&CONST_Z, FPU_st0_ptr); + FPU_st0_ptr->sign = sign; + return; + } + } + /* One or both arg must be an infinity */ + else + if (FPU_st0_tag == TW_Infinity) { + if ((FPU_st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero)) { + pop(); + FPU_st0_ptr = &st(0); + arith_invalid(FPU_st0_ptr); /* log(-infinity) or + * 0*log(infinity) */ + return; + } else { + char sign = st1_ptr->sign; + +#ifdef DENORM_OPERAND + if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + pop(); + FPU_st0_ptr = &st(0); + reg_move(&CONST_INF, FPU_st0_ptr); + FPU_st0_ptr->sign = sign; + return; + } + } + /* st(1) must be infinity here */ + else + if ((FPU_st0_tag == TW_Valid) && (FPU_st0_ptr->sign == SIGN_POS)) { + if (FPU_st0_ptr->exp >= EXP_BIAS) { + if ((FPU_st0_ptr->exp == EXP_BIAS) && + (FPU_st0_ptr->sigh == 0x80000000) && + (FPU_st0_ptr->sigl == 0)) { + /* st(0 + * ) + * hold + * s + * 1.0 */ + pop(); + FPU_st0_ptr = &st(0); + arith_invalid(FPU_st0_ptr); /* infinity*log(1) */ + return; + } + /* st(0) is + * positive + * and > 1.0 */ + pop(); + } else { + /* st(0) is + * positive + * and < 1.0 */ + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + st1_ptr->sign ^= SIGN_NEG; + pop(); + } + return; + } else { + /* st(0) must be zero + * or negative */ + if (FPU_st0_ptr->tag == TW_Zero) { + pop(); + FPU_st0_ptr = st1_ptr; + st1_ptr->sign ^= SIGN_NEG ^ SIGN_POS; + /* This should + * be invalid, + * but a real + * 80486 is + * happy with + * it. */ +#ifndef PECULIAR_486 + divide_by_zero(st1_ptr->sign, FPU_st0_ptr); +#endif /* PECULIAR_486 */ + } else { + pop(); + FPU_st0_ptr = st1_ptr; + arith_invalid(FPU_st0_ptr); /* log(negative) */ + } + return; + } +} + + +static void +fpatan(void) +{ + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + + if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) { + int saved_control, saved_status; + FPU_REG sum; + int quadrant = st1_ptr->sign | ((FPU_st0_ptr->sign) << 1); + +#ifdef DENORM_OPERAND + if (((FPU_st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + /* We use the general purpose arithmetic so we need to save + * these. */ + saved_status = status_word; + saved_control = control_word; + control_word = FULL_PRECISION; + + st1_ptr->sign = FPU_st0_ptr->sign = SIGN_POS; + if (compare(st1_ptr) == COMP_A_lt_B) { + quadrant |= 4; + reg_div(FPU_st0_ptr, st1_ptr, &sum, FULL_PRECISION); + } else + reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION); + + poly_atan(&sum); + + if (quadrant & 4) { + reg_sub(&CONST_PI2, &sum, &sum, FULL_PRECISION); + } + if (quadrant & 2) { + reg_sub(&CONST_PI, &sum, &sum, FULL_PRECISION); + } + if (quadrant & 1) + sum.sign ^= SIGN_POS ^ SIGN_NEG; + + /* All of the basic arithmetic is done now */ + control_word = saved_control; + status_word = saved_status; + + reg_move(&sum, st1_ptr); + } else + if ((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty)) { + stack_underflow_pop(1); + return; + } else + if ((FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN)) { + real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); + pop(); + return; + } else + if ((FPU_st0_tag == TW_Infinity) || (st1_tag == TW_Infinity)) { + char sign = st1_ptr->sign; + if (FPU_st0_tag == TW_Infinity) { + if (st1_tag == TW_Infinity) { + if (FPU_st0_ptr->sign == SIGN_POS) { + reg_move(&CONST_PI4, st1_ptr); + } else + reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION); + } else { + +#ifdef DENORM_OPERAND + if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + if (FPU_st0_ptr->sign == SIGN_POS) { + reg_move(&CONST_Z, st1_ptr); + pop(); + return; + } else + reg_move(&CONST_PI, st1_ptr); + } + } else { + /* st(1) is infinity, st(0) + * not infinity */ +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + reg_move(&CONST_PI2, st1_ptr); + } + st1_ptr->sign = sign; + } else + if (st1_tag == TW_Zero) { + /* st(0) must be valid or zero */ + char sign = st1_ptr->sign; + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + if (FPU_st0_ptr->sign == SIGN_POS) { + reg_move(&CONST_Z, st1_ptr); + pop(); + return; + } else + reg_move(&CONST_PI, st1_ptr); + st1_ptr->sign = sign; + } else + if (FPU_st0_tag == TW_Zero) { + /* st(1) must be + * TW_Valid here */ + char sign = st1_ptr->sign; + +#ifdef DENORM_OPERAND + if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + reg_move(&CONST_PI2, st1_ptr); + st1_ptr->sign = sign; + } +#ifdef PARANOID + else + EXCEPTION(EX_INTERNAL | 0x220); +#endif /* PARANOID */ + + pop(); + set_precision_flag_up();/* We do not really know if up or down */ +} + + +static void +fprem(void) +{ + fprem_kernel(RC_CHOP); +} + + +static void +fprem1(void) +{ + fprem_kernel(RC_RND); +} + + +static void +fyl2xp1(void) +{ + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + + if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) { + int saved_control, saved_status; + +#ifdef DENORM_OPERAND + if (((FPU_st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + /* We use the general purpose arithmetic so we need to save + * these. */ + saved_status = status_word; + saved_control = control_word; + control_word = FULL_PRECISION; + + if (poly_l2p1(FPU_st0_ptr, FPU_st0_ptr)) { + arith_invalid(st1_ptr); /* poly_l2p1() returned + * invalid */ + pop(); + return; + } + /* Enough of the basic arithmetic is done now */ + control_word = saved_control; + status_word = saved_status; + + /* Let the multiply set the flags */ + reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); + + pop(); + } else + if ((FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty)) { + stack_underflow_pop(1); + return; + } else + if (FPU_st0_tag == TW_Zero) { + if (st1_tag <= TW_Zero) { + +#ifdef DENORM_OPERAND + if ((st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) && + (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + st1_ptr->sign ^= FPU_st0_ptr->sign; + reg_move(FPU_st0_ptr, st1_ptr); + } else + if (st1_tag == TW_Infinity) { + arith_invalid(st1_ptr); /* Infinity*log(1) */ + pop(); + return; + } else + if (st1_tag == TW_NaN) { + real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); + pop(); + return; + } +#ifdef PARANOID + else { + EXCEPTION(EX_INTERNAL | 0x116); + return; + } +#endif /* PARANOID */ + pop(); + return; + } else + if (FPU_st0_tag == TW_Valid) { + if (st1_tag == TW_Zero) { + if (FPU_st0_ptr->sign == SIGN_NEG) { + if (FPU_st0_ptr->exp >= EXP_BIAS) { + /* st(0) holds + * <= -1.0 */ + arith_invalid(st1_ptr); /* infinity*log(1) */ + pop(); + return; + } +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + st1_ptr->sign ^= SIGN_POS ^ SIGN_NEG; + pop(); + return; + } +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + pop(); + return; + } + if (st1_tag == TW_Infinity) { + if (FPU_st0_ptr->sign == SIGN_NEG) { + if ((FPU_st0_ptr->exp >= EXP_BIAS) && + !((FPU_st0_ptr->sigh == 0x80000000) && + (FPU_st0_ptr->sigl == 0))) { + /* st(0) holds + * < -1.0 */ + arith_invalid(st1_ptr); + pop(); + return; + } +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + st1_ptr->sign ^= SIGN_POS ^ SIGN_NEG; + pop(); + return; + } +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + pop(); + return; + } + if (st1_tag == TW_NaN) { + real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); + pop(); + return; + } + } else + if (FPU_st0_tag == TW_NaN) { + real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); + pop(); + return; + } else + if (FPU_st0_tag == TW_Infinity) { + if (st1_tag == TW_NaN) { + real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); + pop(); + return; + } else + if ((FPU_st0_ptr->sign == SIGN_NEG) || + (st1_tag == TW_Zero)) { + arith_invalid(st1_ptr); /* log(infinity) */ + pop(); + return; + } + /* st(1) must be valid + * here. */ + +#ifdef DENORM_OPERAND + if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + /* The Manual says + * that log(Infinity) + * is invalid, but a + * real 80486 sensibly + * says that it is + * o.k. */ + { + char sign = st1_ptr->sign; + reg_move(&CONST_INF, st1_ptr); + st1_ptr->sign = sign; + } + pop(); + return; + } +#ifdef PARANOID + else { + EXCEPTION(EX_INTERNAL | 0x117); + } +#endif /* PARANOID */ +} + + +static void +fscale(void) +{ + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + int old_cw = control_word; + + if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) { + long scale; + FPU_REG tmp; + +#ifdef DENORM_OPERAND + if (((FPU_st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + if (st1_ptr->exp > EXP_BIAS + 30) { + /* 2^31 is far too large, would require 2^(2^30) or + * 2^(-2^30) */ + char sign; + + if (st1_ptr->sign == SIGN_POS) { + EXCEPTION(EX_Overflow); + sign = FPU_st0_ptr->sign; + reg_move(&CONST_INF, FPU_st0_ptr); + FPU_st0_ptr->sign = sign; + } else { + EXCEPTION(EX_Underflow); + sign = FPU_st0_ptr->sign; + reg_move(&CONST_Z, FPU_st0_ptr); + FPU_st0_ptr->sign = sign; + } + return; + } + control_word &= ~CW_RC; + control_word |= RC_CHOP; + reg_move(st1_ptr, &tmp); + round_to_int(&tmp); /* This can never overflow here */ + control_word = old_cw; + scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl; + scale += FPU_st0_ptr->exp; + FPU_st0_ptr->exp = scale; + + /* Use round_reg() to properly detect under/overflow etc */ + round_reg(FPU_st0_ptr, 0, control_word); + + return; + } else + if (FPU_st0_tag == TW_Valid) { + if (st1_tag == TW_Zero) { + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + return; + } + if (st1_tag == TW_Infinity) { + char sign = st1_ptr->sign; + +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + if (sign == SIGN_POS) { + reg_move(&CONST_INF, FPU_st0_ptr); + } else + reg_move(&CONST_Z, FPU_st0_ptr); + FPU_st0_ptr->sign = sign; + return; + } + if (st1_tag == TW_NaN) { + real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); + return; + } + } else + if (FPU_st0_tag == TW_Zero) { + if (st1_tag == TW_Valid) { + +#ifdef DENORM_OPERAND + if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + return; + } else + if (st1_tag == TW_Zero) { + return; + } else + if (st1_tag == TW_Infinity) { + if (st1_ptr->sign == SIGN_NEG) + return; + else { + arith_invalid(FPU_st0_ptr); /* Zero scaled by + * +Infinity */ + return; + } + } else + if (st1_tag == TW_NaN) { + real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); + return; + } + } else + if (FPU_st0_tag == TW_Infinity) { + if (st1_tag == TW_Valid) { + +#ifdef DENORM_OPERAND + if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return; +#endif /* DENORM_OPERAND */ + + return; + } + if (((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS)) + || (st1_tag == TW_Zero)) + return; + else + if (st1_tag == TW_Infinity) { + arith_invalid(FPU_st0_ptr); /* Infinity scaled by + * -Infinity */ + return; + } else + if (st1_tag == TW_NaN) { + real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); + return; + } + } else + if (FPU_st0_tag == TW_NaN) { + if (st1_tag != TW_Empty) { + real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); + return; + } + } +#ifdef PARANOID + if (!((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty))) { + EXCEPTION(EX_INTERNAL | 0x115); + return; + } +#endif + + /* At least one of st(0), st(1) must be empty */ + stack_underflow(); + +} + + +/*---------------------------------------------------------------------------*/ + +static FUNC trig_table_a[] = { + f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp +}; + +void +trig_a(void) +{ + (trig_table_a[FPU_rm]) (); +} + + +static FUNC trig_table_b[] = +{ + fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos +}; + +void +trig_b(void) +{ + (trig_table_b[FPU_rm]) (); +} diff --git a/sys/gnu/i386/fpemul/get_address.c b/sys/gnu/i386/fpemul/get_address.c new file mode 100644 index 0000000..9de2c47 --- /dev/null +++ b/sys/gnu/i386/fpemul/get_address.c @@ -0,0 +1,190 @@ +/* + * get_address.c + * + * Get the effective address from an FPU instruction. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/*---------------------------------------------------------------------------+ + | Note: | + | The file contains code which accesses user memory. | + | Emulator static data may change when user memory is accessed, due to | + | other processes using the emulator while swapping is in progress. | + +---------------------------------------------------------------------------*/ + +#include "param.h" +#include "proc.h" +#include "systm.h" +#include "machine/cpu.h" +#include "machine/pcb.h" +#include "machine/reg.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "exception.h" + +static int reg_offset[] = { +tEAX, tECX, tEDX, tEBX, tESP, tEBP, tESI, tEDI}; +#define REG_(x) (*(((int*)FPU_info) + reg_offset[(x)])) + +void *FPU_data_address; + + +/* Decode the SIB byte. This function assumes mod != 0 */ +static void * +sib(int mod) +{ + unsigned char ss, index, base; + long offset; + + REENTRANT_CHECK(OFF); + base = fubyte((char *) FPU_EIP); /* The SIB byte */ + REENTRANT_CHECK(ON); + FPU_EIP++; + ss = base >> 6; + index = (base >> 3) & 7; + base &= 7; + + if ((mod == 0) && (base == 5)) + offset = 0; /* No base register */ + else + offset = REG_(base); + + if (index == 4) { + /* No index register */ + /* A non-zero ss is illegal */ + if (ss) + EXCEPTION(EX_Invalid); + } else { + offset += (REG_(index)) << ss; + } + + if (mod == 1) { + /* 8 bit signed displacement */ + REENTRANT_CHECK(OFF); + offset += (signed char) fubyte((char *) FPU_EIP); + REENTRANT_CHECK(ON); + FPU_EIP++; + } else + if (mod == 2 || base == 5) { /* The second condition also + * has mod==0 */ + /* 32 bit displacment */ + REENTRANT_CHECK(OFF); + offset += (signed) fuword((unsigned long *) FPU_EIP); + REENTRANT_CHECK(ON); + FPU_EIP += 4; + } + return (void *) offset; +} + + +/* + MOD R/M byte: MOD == 3 has a special use for the FPU + SIB byte used iff R/M = 100b + + 7 6 5 4 3 2 1 0 + ..... ......... ......... + MOD OPCODE(2) R/M + + + SIB byte + + 7 6 5 4 3 2 1 0 + ..... ......... ......... + SS INDEX BASE + +*/ + +void +get_address(unsigned char FPU_modrm) +{ + unsigned char mod; + long *cpu_reg_ptr; + int offset = 0; /* Initialized just to stop compiler warnings. */ + + mod = (FPU_modrm >> 6) & 3; + + if (FPU_rm == 4 && mod != 3) { + FPU_data_address = sib(mod); + return; + } + cpu_reg_ptr = (long *) ®_(FPU_rm); + switch (mod) { + case 0: + if (FPU_rm == 5) { + /* Special case: disp32 */ + REENTRANT_CHECK(OFF); + offset = fuword((unsigned long *) FPU_EIP); + REENTRANT_CHECK(ON); + FPU_EIP += 4; + FPU_data_address = (void *) offset; + return; + } else { + FPU_data_address = (void *) *cpu_reg_ptr; /* Just return the + * contents of the cpu + * register */ + return; + } + case 1: + /* 8 bit signed displacement */ + REENTRANT_CHECK(OFF); + offset = (signed char) fubyte((char *) FPU_EIP); + REENTRANT_CHECK(ON); + FPU_EIP++; + break; + case 2: + /* 32 bit displacement */ + REENTRANT_CHECK(OFF); + offset = (signed) fuword((unsigned long *) FPU_EIP); + REENTRANT_CHECK(ON); + FPU_EIP += 4; + break; + case 3: + /* Not legal for the FPU */ + EXCEPTION(EX_Invalid); + } + + FPU_data_address = offset + (char *) *cpu_reg_ptr; +} diff --git a/sys/gnu/i386/fpemul/load_store.c b/sys/gnu/i386/fpemul/load_store.c new file mode 100644 index 0000000..799a319 --- /dev/null +++ b/sys/gnu/i386/fpemul/load_store.c @@ -0,0 +1,256 @@ +/* + * load_store.c + * + * This file contains most of the code to interpret the FPU instructions + * which load and store from user memory. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/*---------------------------------------------------------------------------+ + | Note: | + | The file contains code which accesses user memory. | + | Emulator static data may change when user memory is accessed, due to | + | other processes using the emulator while swapping is in progress. | + +---------------------------------------------------------------------------*/ + +#include "param.h" +#include "proc.h" +#include "systm.h" +#include "machine/cpu.h" +#include "machine/pcb.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "exception.h" +#include "status_w.h" + + +#define _NONE_ 0 /* FPU_st0_ptr etc not needed */ +#define _REG0_ 1 /* Will be storing st(0) */ +#define _PUSH_ 3 /* Need to check for space to push onto stack */ +#define _null_ 4 /* Function illegal or not implemented */ + +#define pop_0() { pop_ptr->tag = TW_Empty; top++; } + + +static unsigned char type_table[32] = { + _PUSH_, _PUSH_, _PUSH_, _PUSH_, + _null_, _null_, _null_, _null_, + _REG0_, _REG0_, _REG0_, _REG0_, + _REG0_, _REG0_, _REG0_, _REG0_, + _NONE_, _null_, _NONE_, _PUSH_, + _NONE_, _PUSH_, _null_, _PUSH_, + _NONE_, _null_, _NONE_, _REG0_, + _NONE_, _REG0_, _NONE_, _REG0_ +}; + +void +load_store_instr(char type) +{ + FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which + * won't change. */ + + pop_ptr = NULL; /* Initialized just to stop compiler warnings. */ + + + switch (type_table[(int) (unsigned) type]) { + case _NONE_: + break; + case _REG0_: + pop_ptr = &st(0); /* Some of these instructions pop + * after storing */ + + FPU_st0_ptr = pop_ptr; /* Set the global variables. */ + FPU_st0_tag = FPU_st0_ptr->tag; + break; + case _PUSH_: + { + pop_ptr = &st(-1); + if (pop_ptr->tag != TW_Empty) { + stack_overflow(); + return; + } + top--; + } + break; + case _null_: + return Un_impl(); +#ifdef PARANOID + default: + return EXCEPTION(EX_INTERNAL); +#endif /* PARANOID */ + } + + switch (type) { + case 000: /* fld m32real */ + reg_load_single(); + setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ + reg_move(&FPU_loaded_data, pop_ptr); + break; + case 001: /* fild m32int */ + reg_load_int32(); + setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ + reg_move(&FPU_loaded_data, pop_ptr); + break; + case 002: /* fld m64real */ + reg_load_double(); + setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ + reg_move(&FPU_loaded_data, pop_ptr); + break; + case 003: /* fild m16int */ + reg_load_int16(); + setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ + reg_move(&FPU_loaded_data, pop_ptr); + break; + case 010: /* fst m32real */ + reg_store_single(); + break; + case 011: /* fist m32int */ + reg_store_int32(); + break; + case 012: /* fst m64real */ + reg_store_double(); + break; + case 013: /* fist m16int */ + reg_store_int16(); + break; + case 014: /* fstp m32real */ + if (reg_store_single()) + pop_0();/* pop only if the number was actually stored + * (see the 80486 manual p16-28) */ + break; + case 015: /* fistp m32int */ + if (reg_store_int32()) + pop_0();/* pop only if the number was actually stored + * (see the 80486 manual p16-28) */ + break; + case 016: /* fstp m64real */ + if (reg_store_double()) + pop_0();/* pop only if the number was actually stored + * (see the 80486 manual p16-28) */ + break; + case 017: /* fistp m16int */ + if (reg_store_int16()) + pop_0();/* pop only if the number was actually stored + * (see the 80486 manual p16-28) */ + break; + case 020: /* fldenv m14/28byte */ + fldenv(); + break; + case 022: /* frstor m94/108byte */ + frstor(); + break; + case 023: /* fbld m80dec */ + reg_load_bcd(); + setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ + reg_move(&FPU_loaded_data, pop_ptr); + break; + case 024: /* fldcw */ + REENTRANT_CHECK(OFF); + control_word = fuword((unsigned short *) FPU_data_address); + REENTRANT_CHECK(ON); +#ifdef NO_UNDERFLOW_TRAP + if (!(control_word & EX_Underflow)) { + control_word |= EX_Underflow; + } +#endif + FPU_data_address = (void *) data_operand_offset; /* We want no net effect */ + FPU_entry_eip = ip_offset; /* We want no net effect */ + break; + case 025: /* fld m80real */ + reg_load_extended(); + setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ + reg_move(&FPU_loaded_data, pop_ptr); + break; + case 027: /* fild m64int */ + reg_load_int64(); + setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */ + reg_move(&FPU_loaded_data, pop_ptr); + break; + case 030: /* fstenv m14/28byte */ + fstenv(); + FPU_data_address = (void *) data_operand_offset; /* We want no net effect */ + FPU_entry_eip = ip_offset; /* We want no net effect */ + break; + case 032: /* fsave */ + fsave(); + FPU_data_address = (void *) data_operand_offset; /* We want no net effect */ + FPU_entry_eip = ip_offset; /* We want no net effect */ + break; + case 033: /* fbstp m80dec */ + if (reg_store_bcd()) + pop_0();/* pop only if the number was actually stored + * (see the 80486 manual p16-28) */ + break; + case 034: /* fstcw m16int */ + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, FPU_data_address, 2);*/ + suword( (short *) FPU_data_address,control_word); + REENTRANT_CHECK(ON); + FPU_data_address = (void *) data_operand_offset; /* We want no net effect */ + FPU_entry_eip = ip_offset; /* We want no net effect */ + break; + case 035: /* fstp m80real */ + if (reg_store_extended()) + pop_0();/* pop only if the number was actually stored + * (see the 80486 manual p16-28) */ + break; + case 036: /* fstsw m2byte */ + status_word &= ~SW_Top; + status_word |= (top & 7) << SW_Top_Shift; + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, FPU_data_address, 2);*/ + suword( (short *) FPU_data_address,status_word); + REENTRANT_CHECK(ON); + FPU_data_address = (void *) data_operand_offset; /* We want no net effect */ + FPU_entry_eip = ip_offset; /* We want no net effect */ + break; + case 037: /* fistp m64int */ + if (reg_store_int64()) + pop_0();/* pop only if the number was actually stored + * (see the 80486 manual p16-28) */ + break; + } +} diff --git a/sys/gnu/i386/fpemul/math_emu.h b/sys/gnu/i386/fpemul/math_emu.h new file mode 100644 index 0000000..825a721 --- /dev/null +++ b/sys/gnu/i386/fpemul/math_emu.h @@ -0,0 +1,41 @@ +#ifndef _MATH_EMU_H +#define _MATH_EMU_H + +struct fpu_reg { + char sign; + char tag; + long exp; + u_long sigl; + u_long sigh; +}; + +union i387_union { + struct i387_hard_struct { + long cwd; + long swd; + long twd; + long fip; + long fcs; + long foo; + long fos; + long st_space[20]; /* 8*10 bytes for each FP-reg = 80 + * bytes */ + } hard; + struct i387_soft_struct { + long cwd; + long swd; + long twd; + long fip; + long fcs; + long foo; + long fos; + long top; + struct fpu_reg regs[8]; /* 8*16 bytes for each FP-reg = 128 + * bytes */ + unsigned char lookahead; + struct trapframe *frame; + unsigned long entry_eip; + int orig_eip; + } soft; +}; +#endif diff --git a/sys/gnu/i386/fpemul/poly_2xm1.c b/sys/gnu/i386/fpemul/poly_2xm1.c new file mode 100644 index 0000000..da42fca --- /dev/null +++ b/sys/gnu/i386/fpemul/poly_2xm1.c @@ -0,0 +1,128 @@ +/* + * poly_2xm1.c + * + * Function to compute 2^x-1 by a polynomial approximation. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" + + + +#define HIPOWER 13 +static unsigned short lterms[HIPOWER][4] = +{ + {0x79b5, 0xd1cf, 0x17f7, 0xb172}, + {0x1b56, 0x058b, 0x7bff, 0x3d7f}, + {0x8bb0, 0x8250, 0x846b, 0x0e35}, + {0xbc65, 0xf747, 0x556d, 0x0276}, + {0x17cb, 0x9e39, 0x61ff, 0x0057}, + {0xe018, 0x9776, 0x1848, 0x000a}, + {0x66f2, 0xff30, 0xffe5, 0x0000}, + {0x682f, 0xffb6, 0x162b, 0x0000}, + {0xb7ca, 0x2956, 0x01b5, 0x0000}, + {0xcd3e, 0x4817, 0x001e, 0x0000}, + {0xb7e2, 0xecbe, 0x0001, 0x0000}, + {0x0ed5, 0x1a27, 0x0000, 0x0000}, + {0x101d, 0x0222, 0x0000, 0x0000}, +}; + + +/*--- poly_2xm1() -----------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +int +poly_2xm1(FPU_REG * arg, FPU_REG * result) +{ + short exponent; + long long Xll; + FPU_REG accum; + + + exponent = arg->exp - EXP_BIAS; + + if (arg->tag == TW_Zero) { + /* Return 0.0 */ + reg_move(&CONST_Z, result); + return 0; + } + if (exponent >= 0) { /* Can't hack a number >= 1.0 */ + arith_invalid(result); /* Number too large */ + return 1; + } + if (arg->sign != SIGN_POS) { /* Can't hack a number < 0.0 */ + arith_invalid(result); /* Number negative */ + return 1; + } + if (exponent < -64) { + reg_move(&CONST_LN2, result); + return 0; + } + *(unsigned *) &Xll = arg->sigl; + *(((unsigned *) &Xll) + 1) = arg->sigh; + if (exponent < -1) { + /* shift the argument right by the required places */ + if (shrx(&Xll, -1 - exponent) >= (unsigned)0x80000000) + Xll++; /* round up */ + } + *(short *) &(accum.sign) = 0; /* will be a valid positive nr with + * expon = 0 */ + accum.exp = 0; + + /* Do the basic fixed point polynomial evaluation */ + polynomial((unsigned *) &accum.sigl, (unsigned *) &Xll, lterms, HIPOWER - 1); + + /* Convert to 64 bit signed-compatible */ + accum.exp += EXP_BIAS - 1; + + reg_move(&accum, result); + + normalize(result); + + return 0; + +} diff --git a/sys/gnu/i386/fpemul/poly_atan.c b/sys/gnu/i386/fpemul/poly_atan.c new file mode 100644 index 0000000..8cc98fa --- /dev/null +++ b/sys/gnu/i386/fpemul/poly_atan.c @@ -0,0 +1,239 @@ +/* + * p_atan.c + * + * Compute the tan of a FPU_REG, using a polynomial approximation. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" + + +#define HIPOWERon 6 /* odd poly, negative terms */ +static unsigned oddnegterms[HIPOWERon][2] = +{ + {0x00000000, 0x00000000}, /* for + 1.0 */ + {0x763b6f3d, 0x1adc4428}, + {0x20f0630b, 0x0502909d}, + {0x4e825578, 0x0198ce38}, + {0x22b7cb87, 0x008da6e3}, + {0x9b30ca03, 0x00239c79} +}; +#define HIPOWERop 6 /* odd poly, positive terms */ +static unsigned oddplterms[HIPOWERop][2] = +{ + {0xa6f67cb8, 0x94d910bd}, + {0xa02ffab4, 0x0a43cb45}, + {0x04265e6b, 0x02bf5655}, + {0x0a728914, 0x00f280f7}, + {0x6d640e01, 0x004d6556}, + {0xf1dd2dbf, 0x000a530a} +}; + + +static unsigned denomterm[2] = +{0xfc4bd208, 0xea2e6612}; + + + +/*--- poly_atan() -----------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +void +poly_atan(FPU_REG * arg) +{ + char recursions = 0; + short exponent; + FPU_REG odd_poly, even_poly, pos_poly, neg_poly; + FPU_REG argSq; + long long arg_signif, argSqSq; + + +#ifdef PARANOID + if (arg->sign != 0) { /* Can't hack a number < 0.0 */ + arith_invalid(arg); + return; + } /* Need a positive number */ +#endif /* PARANOID */ + + exponent = arg->exp - EXP_BIAS; + + if (arg->tag == TW_Zero) { + /* Return 0.0 */ + reg_move(&CONST_Z, arg); + return; + } + if (exponent >= -2) { + /* argument is in the range [0.25 .. 1.0] */ + if (exponent >= 0) { +#ifdef PARANOID + if ((exponent == 0) && + (arg->sigl == 0) && (arg->sigh == 0x80000000)) +#endif /* PARANOID */ + { + reg_move(&CONST_PI4, arg); + return; + } +#ifdef PARANOID + EXCEPTION(EX_INTERNAL | 0x104); /* There must be a logic + * error */ +#endif /* PARANOID */ + } + /* If the argument is greater than sqrt(2)-1 (=0.414213562...) */ + /* convert the argument by an identity for atan */ + if ((exponent >= -1) || (arg->sigh > 0xd413ccd0)) { + FPU_REG numerator, denom; + + recursions++; + + arg_signif = *(long long *) &(arg->sigl); + if (exponent < -1) { + if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000) + arg_signif++; /* round up */ + } + *(long long *) &(numerator.sigl) = -arg_signif; + numerator.exp = EXP_BIAS - 1; + normalize(&numerator); /* 1 - arg */ + + arg_signif = *(long long *) &(arg->sigl); + if (shrx(&arg_signif, -exponent) >= (unsigned)0x80000000) + arg_signif++; /* round up */ + *(long long *) &(denom.sigl) = arg_signif; + denom.sigh |= 0x80000000; /* 1 + arg */ + + arg->exp = numerator.exp; + reg_u_div(&numerator, &denom, arg, FULL_PRECISION); + + exponent = arg->exp - EXP_BIAS; + } + } + *(long long *) &arg_signif = *(long long *) &(arg->sigl); + +#ifdef PARANOID + /* This must always be true */ + if (exponent >= -1) { + EXCEPTION(EX_INTERNAL | 0x120); /* There must be a logic error */ + } +#endif /* PARANOID */ + + /* shift the argument right by the required places */ + if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000) + arg_signif++; /* round up */ + + /* Now have arg_signif with binary point at the left .1xxxxxxxx */ + mul64(&arg_signif, &arg_signif, (long long *) (&argSq.sigl)); + mul64((long long *) (&argSq.sigl), (long long *) (&argSq.sigl), &argSqSq); + + /* will be a valid positive nr with expon = 0 */ + *(short *) &(pos_poly.sign) = 0; + pos_poly.exp = EXP_BIAS; + + /* Do the basic fixed point polynomial evaluation */ + polynomial(&pos_poly.sigl, (unsigned *) &argSqSq, + (unsigned short (*)[4]) oddplterms, HIPOWERop - 1); + mul64((long long *) (&argSq.sigl), (long long *) (&pos_poly.sigl), + (long long *) (&pos_poly.sigl)); + + /* will be a valid positive nr with expon = 0 */ + *(short *) &(neg_poly.sign) = 0; + neg_poly.exp = EXP_BIAS; + + /* Do the basic fixed point polynomial evaluation */ + polynomial(&neg_poly.sigl, (unsigned *) &argSqSq, + (unsigned short (*)[4]) oddnegterms, HIPOWERon - 1); + + /* Subtract the mantissas */ + *((long long *) (&pos_poly.sigl)) -= *((long long *) (&neg_poly.sigl)); + + reg_move(&pos_poly, &odd_poly); + poly_add_1(&odd_poly); + + /* The complete odd polynomial */ + reg_u_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION); + + /* will be a valid positive nr with expon = 0 */ + *(short *) &(even_poly.sign) = 0; + + mul64((long long *) (&argSq.sigl), + (long long *) (&denomterm), (long long *) (&even_poly.sigl)); + + poly_add_1(&even_poly); + + reg_div(&odd_poly, &even_poly, arg, FULL_PRECISION); + + if (recursions) + reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION); +} + + +/* The argument to this function must be polynomial() compatible, + i.e. have an exponent (not checked) of EXP_BIAS-1 but need not + be normalized. + This function adds 1.0 to the (assumed positive) argument. */ +void +poly_add_1(FPU_REG * src) +{ +/* Rounding in a consistent direction produces better results + for the use of this function in poly_atan. Simple truncation + is used here instead of round-to-nearest. */ + +#ifdef OBSOLETE + char round = (src->sigl & 3) == 3; +#endif /* OBSOLETE */ + + shrx(&src->sigl, 1); + +#ifdef OBSOLETE + if (round) + (*(long long *) &src->sigl)++; /* Round to even */ +#endif /* OBSOLETE */ + + src->sigh |= 0x80000000; + + src->exp = EXP_BIAS; + +} diff --git a/sys/gnu/i386/fpemul/poly_div.s b/sys/gnu/i386/fpemul/poly_div.s new file mode 100644 index 0000000..d7dc316 --- /dev/null +++ b/sys/gnu/i386/fpemul/poly_div.s @@ -0,0 +1,131 @@ + .file "poly_div.S" +/* + * poly_div.S + * + * A set of functions to divide 64 bit integers by fixed numbers. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "fpu_asm.h" + +.text + +/*---------------------------------------------------------------------------*/ + .align 2,144 +.globl _poly_div2 +_poly_div2: + pushl %ebp + movl %esp,%ebp + + movl PARAM1,%ecx + movw (%ecx),%ax + + shrl $1,4(%ecx) + rcrl $1,(%ecx) + + testw $1,%ax + je poly_div2_exit + + addl $1,(%ecx) + adcl $0,4(%ecx) +poly_div2_exit: + + leave + ret +/*---------------------------------------------------------------------------*/ + .align 2,144 +.globl _poly_div4 +_poly_div4: + pushl %ebp + movl %esp,%ebp + + movl PARAM1,%ecx + movw (%ecx),%ax + + movl 4(%ecx),%edx + shll $30,%edx + + shrl $2,4(%ecx) + shrl $2,(%ecx) + + orl %edx,(%ecx) + + testw $2,%ax + je poly_div4_exit + + addl $1,(%ecx) + adcl $0,4(%ecx) +poly_div4_exit: + + leave + ret +/*---------------------------------------------------------------------------*/ + .align 2,144 +.globl _poly_div16 +_poly_div16: + pushl %ebp + movl %esp,%ebp + + movl PARAM1,%ecx + movw (%ecx),%ax + + movl 4(%ecx),%edx + shll $28,%edx + + shrl $4,4(%ecx) + shrl $4,(%ecx) + + orl %edx,(%ecx) + + testw $8,%ax + je poly_div16_exit + + addl $1,(%ecx) + adcl $0,4(%ecx) +poly_div16_exit: + + leave + ret +/*---------------------------------------------------------------------------*/ diff --git a/sys/gnu/i386/fpemul/poly_l2.c b/sys/gnu/i386/fpemul/poly_l2.c new file mode 100644 index 0000000..fbb1ab0 --- /dev/null +++ b/sys/gnu/i386/fpemul/poly_l2.c @@ -0,0 +1,305 @@ +/* + * poly_l2.c + * + * Compute the base 2 log of a FPU_REG, using a polynomial approximation. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" + + + +#define HIPOWER 9 +static unsigned short lterms[HIPOWER][4] = +{ + /* Ideal computation with these coeffs gives about 64.6 bit rel + * accuracy. */ + {0xe177, 0xb82f, 0x7652, 0x7154}, + {0xee0f, 0xe80f, 0x2770, 0x7b1c}, + {0x0fc0, 0xbe87, 0xb143, 0x49dd}, + {0x78b9, 0xdadd, 0xec54, 0x34c2}, + {0x003a, 0x5de9, 0x628b, 0x2909}, + {0x5588, 0xed16, 0x4abf, 0x2193}, + {0xb461, 0x85f7, 0x347a, 0x1c6a}, + {0x0975, 0x87b3, 0xd5bf, 0x1876}, + {0xe85c, 0xcec9, 0x84e7, 0x187d} +}; + + + + +/*--- poly_l2() -------------------------------------------------------------+ + | Base 2 logarithm by a polynomial approximation. | + +---------------------------------------------------------------------------*/ +void +poly_l2(FPU_REG * arg, FPU_REG * result) +{ + short exponent; + char zero; /* flag for an Xx == 0 */ + unsigned short bits, shift; + long long Xsq; + FPU_REG accum, denom, num, Xx; + + + exponent = arg->exp - EXP_BIAS; + + accum.tag = TW_Valid; /* set the tags to Valid */ + + if (arg->sigh > (unsigned) 0xb504f334) { + /* This is good enough for the computation of the polynomial + * sum, but actually results in a loss of precision for the + * computation of Xx. This will matter only if exponent + * becomes zero. */ + exponent++; + accum.sign = 1; /* sign to negative */ + num.exp = EXP_BIAS; /* needed to prevent errors in div + * routine */ + reg_u_div(&CONST_1, arg, &num, FULL_PRECISION); + } else { + accum.sign = 0; /* set the sign to positive */ + num.sigl = arg->sigl; /* copy the mantissa */ + num.sigh = arg->sigh; + } + + + /* shift num left, lose the ms bit */ + num.sigh <<= 1; + if (num.sigl & 0x80000000) + num.sigh |= 1; + num.sigl <<= 1; + + denom.sigl = num.sigl; + denom.sigh = num.sigh; + poly_div4((long long *) &(denom.sigl)); + denom.sigh += 0x80000000; /* set the msb */ + Xx.exp = EXP_BIAS; /* needed to prevent errors in div routine */ + reg_u_div(&num, &denom, &Xx, FULL_PRECISION); + + zero = !(Xx.sigh | Xx.sigl); + + mul64((long long *) &Xx.sigl, (long long *) &Xx.sigl, &Xsq); + poly_div16(&Xsq); + + accum.exp = -1; /* exponent of accum */ + + /* Do the basic fixed point polynomial evaluation */ + polynomial((unsigned *) &accum.sigl, (unsigned *) &Xsq, lterms, HIPOWER - 1); + + if (!exponent) { + /* If the exponent is zero, then we would lose precision by + * sticking to fixed point computation here */ + /* We need to re-compute Xx because of loss of precision. */ + FPU_REG lXx; + char sign; + + sign = accum.sign; + accum.sign = 0; + + /* make accum compatible and normalize */ + accum.exp = EXP_BIAS + accum.exp; + normalize(&accum); + + if (zero) { + reg_move(&CONST_Z, result); + } else { + /* we need to re-compute lXx to better accuracy */ + num.tag = TW_Valid; /* set the tags to Vaild */ + num.sign = 0; /* set the sign to positive */ + num.exp = EXP_BIAS - 1; + if (sign) { + /* The argument is of the form 1-x */ + /* Use 1-1/(1-x) = x/(1-x) */ + *((long long *) &num.sigl) = -*((long long *) &(arg->sigl)); + normalize(&num); + reg_div(&num, arg, &num, FULL_PRECISION); + } else { + normalize(&num); + } + + denom.tag = TW_Valid; /* set the tags to Valid */ + denom.sign = SIGN_POS; /* set the sign to positive */ + denom.exp = EXP_BIAS; + + reg_div(&num, &denom, &lXx, FULL_PRECISION); + + reg_u_mul(&lXx, &accum, &accum, FULL_PRECISION); + + reg_u_add(&lXx, &accum, result, FULL_PRECISION); + + normalize(result); + } + + result->sign = sign; + return; + } + mul64((long long *) &accum.sigl, + (long long *) &Xx.sigl, (long long *) &accum.sigl); + + *((long long *) (&accum.sigl)) += *((long long *) (&Xx.sigl)); + + if (Xx.sigh > accum.sigh) { + /* There was an overflow */ + + poly_div2((long long *) &accum.sigl); + accum.sigh |= 0x80000000; + accum.exp++; + } + /* When we add the exponent to the accum result later, we will require + * that their signs are the same. Here we ensure that this is so. */ + if (exponent && ((exponent < 0) ^ (accum.sign))) { + /* signs are different */ + + accum.sign = !accum.sign; + + /* An exceptional case is when accum is zero */ + if (accum.sigl | accum.sigh) { + /* find 1-accum */ + /* Shift to get exponent == 0 */ + if (accum.exp < 0) { + poly_div2((long long *) &accum.sigl); + accum.exp++; + } + /* Just negate, but throw away the sign */ + *((long long *) &(accum.sigl)) = -*((long long *) &(accum.sigl)); + if (exponent < 0) + exponent++; + else + exponent--; + } + } + shift = exponent >= 0 ? exponent : -exponent; + bits = 0; + if (shift) { + if (accum.exp) { + accum.exp++; + poly_div2((long long *) &accum.sigl); + } + while (shift) { + poly_div2((long long *) &accum.sigl); + if (shift & 1) + accum.sigh |= 0x80000000; + shift >>= 1; + bits++; + } + } + /* Convert to 64 bit signed-compatible */ + accum.exp += bits + EXP_BIAS - 1; + + reg_move(&accum, result); + normalize(result); + + return; +} + + +/*--- poly_l2p1() -----------------------------------------------------------+ + | Base 2 logarithm by a polynomial approximation. | + | log2(x+1) | + +---------------------------------------------------------------------------*/ +int +poly_l2p1(FPU_REG * arg, FPU_REG * result) +{ + char sign = 0; + long long Xsq; + FPU_REG arg_pl1, denom, accum, local_arg, poly_arg; + + + sign = arg->sign; + + reg_add(arg, &CONST_1, &arg_pl1, FULL_PRECISION); + + if ((arg_pl1.sign) | (arg_pl1.tag)) { /* We need a valid positive + * number! */ + return 1; + } + reg_add(&CONST_1, &arg_pl1, &denom, FULL_PRECISION); + reg_div(arg, &denom, &local_arg, FULL_PRECISION); + local_arg.sign = 0; /* Make the sign positive */ + + /* Now we need to check that |local_arg| is less than 3-2*sqrt(2) = + * 0.17157.. = .0xafb0ccc0 * 2^-2 */ + + if (local_arg.exp >= EXP_BIAS - 3) { + if ((local_arg.exp > EXP_BIAS - 3) || + (local_arg.sigh > (unsigned) 0xafb0ccc0)) { + /* The argument is large */ + poly_l2(&arg_pl1, result); + return 0; + } + } + /* Make a copy of local_arg */ + reg_move(&local_arg, &poly_arg); + + /* Get poly_arg bits aligned as required */ + shrx((unsigned *) &(poly_arg.sigl), -(poly_arg.exp - EXP_BIAS + 3)); + + mul64((long long *) &(poly_arg.sigl), (long long *) &(poly_arg.sigl), &Xsq); + poly_div16(&Xsq); + + /* Do the basic fixed point polynomial evaluation */ + polynomial(&(accum.sigl), (unsigned *) &Xsq, lterms, HIPOWER - 1); + + accum.tag = TW_Valid; /* set the tags to Valid */ + accum.sign = SIGN_POS; /* and make accum positive */ + + /* make accum compatible and normalize */ + accum.exp = EXP_BIAS - 1; + normalize(&accum); + + reg_u_mul(&local_arg, &accum, &accum, FULL_PRECISION); + + reg_u_add(&local_arg, &accum, result, FULL_PRECISION); + + /* Multiply the result by 2 */ + result->exp++; + + result->sign = sign; + + return 0; +} diff --git a/sys/gnu/i386/fpemul/poly_mul64.s b/sys/gnu/i386/fpemul/poly_mul64.s new file mode 100644 index 0000000..8f1fbbd --- /dev/null +++ b/sys/gnu/i386/fpemul/poly_mul64.s @@ -0,0 +1,111 @@ +/* + * poly_mul64.S + * + * Multiply two 64 bit integers. + * + * Call from C as: + * void mul64(long long *a, long long *b, long long *result) + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "fpu_asm.h" + +.text + .align 2,144 +.globl _mul64 +_mul64: + pushl %ebp + movl %esp,%ebp + subl $16,%esp + pushl %esi + pushl %ebx + + movl PARAM1,%esi + movl PARAM2,%ecx + movl PARAM3,%ebx + + xor %eax,%eax + movl %eax,-4(%ebp) + movl %eax,-8(%ebp) + + movl (%esi),%eax + mull (%ecx) + movl %eax,-16(%ebp) /* Not used */ + movl %edx,-12(%ebp) + + movl (%esi),%eax + mull 4(%ecx) + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 4(%esi),%eax + mull (%ecx) + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 4(%esi),%eax + mull 4(%ecx) + addl %eax,-8(%ebp) + adcl %edx,-4(%ebp) + + testb $128,-9(%ebp) + je L_no_round + + addl $1,-8(%ebp) + adcl $0,-4(%ebp) + +L_no_round: + movl -8(%ebp),%esi + movl %esi,(%ebx) + movl -4(%ebp),%esi + movl %esi,4(%ebx) + + popl %ebx + popl %esi + leave + ret diff --git a/sys/gnu/i386/fpemul/poly_sin.c b/sys/gnu/i386/fpemul/poly_sin.c new file mode 100644 index 0000000..6c5d04a --- /dev/null +++ b/sys/gnu/i386/fpemul/poly_sin.c @@ -0,0 +1,179 @@ +/* + * poly_sin.c + * + * Computation of an approximation of the sin function by a polynomial + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" + + +#define HIPOWER 5 +static unsigned short lterms[HIPOWER][4] = +{ + {0x846a, 0x42d1, 0xb544, 0x921f}, + {0xe110, 0x75aa, 0xbc67, 0x1466}, + {0x503d, 0xa43f, 0x83c1, 0x000a}, + {0x8f9d, 0x7a19, 0x00f4, 0x0000}, + {0xda03, 0x06aa, 0x0000, 0x0000}, +}; + +static unsigned short negterms[HIPOWER][4] = +{ + {0x95ed, 0x2df2, 0xe731, 0xa55d}, + {0xd159, 0xe62b, 0xd2cc, 0x0132}, + {0x6342, 0xe9fb, 0x3c60, 0x0000}, + {0x6256, 0xdf5a, 0x0002, 0x0000}, + {0xf279, 0x000b, 0x0000, 0x0000}, +}; + + +/*--- poly_sine() -----------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +void +poly_sine(FPU_REG * arg, FPU_REG * result) +{ + short exponent; + FPU_REG Xx, Xx2, Xx4, accum, negaccum; + + + exponent = arg->exp - EXP_BIAS; + + if (arg->tag == TW_Zero) { + /* Return 0.0 */ + reg_move(&CONST_Z, result); + return; + } +#ifdef PARANOID + if (arg->sign != 0) { /* Can't hack a number < 0.0 */ + EXCEPTION(EX_Invalid); + reg_move(&CONST_QNaN, result); + return; + } + if (exponent >= 0) { /* Can't hack a number > 1.0 */ + if ((exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000)) { + reg_move(&CONST_1, result); + return; + } + EXCEPTION(EX_Invalid); + reg_move(&CONST_QNaN, result); + return; + } +#endif /* PARANOID */ + + Xx.sigl = arg->sigl; + Xx.sigh = arg->sigh; + if (exponent < -1) { + /* shift the argument right by the required places */ + if (shrx(&(Xx.sigl), -1 - exponent) >= (unsigned)0x80000000) + (*((long long *) (&(Xx.sigl))))++; /* round up */ + } + mul64((long long *) &(Xx.sigl), (long long *) &(Xx.sigl), + (long long *) &(Xx2.sigl)); + mul64((long long *) &(Xx2.sigl), (long long *) &(Xx2.sigl), + (long long *) &(Xx4.sigl)); + + /* will be a valid positive nr with expon = 0 */ + *(short *) &(accum.sign) = 0; + accum.exp = 0; + + /* Do the basic fixed point polynomial evaluation */ + polynomial(&(accum.sigl), &(Xx4.sigl), lterms, HIPOWER - 1); + + /* will be a valid positive nr with expon = 0 */ + *(short *) &(negaccum.sign) = 0; + negaccum.exp = 0; + + /* Do the basic fixed point polynomial evaluation */ + polynomial(&(negaccum.sigl), &(Xx4.sigl), negterms, HIPOWER - 1); + mul64((long long *) &(Xx2.sigl), (long long *) &(negaccum.sigl), + (long long *) &(negaccum.sigl)); + + /* Subtract the mantissas */ + *((long long *) (&(accum.sigl))) -= *((long long *) (&(negaccum.sigl))); + + /* Convert to 64 bit signed-compatible */ + accum.exp = EXP_BIAS - 1 + accum.exp; + + *(short *) &(result->sign) = *(short *) &(accum.sign); + result->exp = accum.exp; + result->sigl = accum.sigl; + result->sigh = accum.sigh; + + normalize(result); + + reg_mul(result, arg, result, FULL_PRECISION); + reg_u_add(result, arg, result, FULL_PRECISION); + + /* A small overflow may be possible... but an illegal result. */ + if (result->exp >= EXP_BIAS) { + if ((result->exp > EXP_BIAS) /* Larger or equal 2.0 */ + ||(result->sigl > 1) /* Larger than 1.0+msb */ + ||(result->sigh != 0x80000000) /* Much > 1.0 */ + ) { +#ifdef DEBUGGING + RE_ENTRANT_CHECK_OFF + printk("\nEXP=%d, MS=%08x, LS=%08x\n", result->exp, + result->sigh, result->sigl); + RE_ENTRANT_CHECK_ON +#endif /* DEBUGGING */ + EXCEPTION(EX_INTERNAL | 0x103); + } +#ifdef DEBUGGING + RE_ENTRANT_CHECK_OFF + printk("\n***CORRECTING ILLEGAL RESULT*** in poly_sin() computation\n"); + printk("EXP=%d, MS=%08x, LS=%08x\n", result->exp, + result->sigh, result->sigl); + RE_ENTRANT_CHECK_ON +#endif /* DEBUGGING */ + + result->sigl = 0; /* Truncate the result to 1.00 */ + } +} diff --git a/sys/gnu/i386/fpemul/poly_tan.c b/sys/gnu/i386/fpemul/poly_tan.c new file mode 100644 index 0000000..b11630a --- /dev/null +++ b/sys/gnu/i386/fpemul/poly_tan.c @@ -0,0 +1,216 @@ +/* + * poly_tan.c + * + * Compute the tan of a FPU_REG, using a polynomial approximation. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" + + +#define HIPOWERop 3 /* odd poly, positive terms */ +static unsigned short oddplterms[HIPOWERop][4] = +{ + {0x846a, 0x42d1, 0xb544, 0x921f}, + {0x6fb2, 0x0215, 0x95c0, 0x099c}, + {0xfce6, 0x0cc8, 0x1c9a, 0x0000} +}; +#define HIPOWERon 2 /* odd poly, negative terms */ +static unsigned short oddnegterms[HIPOWERon][4] = +{ + {0x6906, 0xe205, 0x25c8, 0x8838}, + {0x1dd7, 0x3fe3, 0x944e, 0x002c} +}; +#define HIPOWERep 2 /* even poly, positive terms */ +static unsigned short evenplterms[HIPOWERep][4] = +{ + {0xdb8f, 0x3761, 0x1432, 0x2acf}, + {0x16eb, 0x13c1, 0x3099, 0x0003} +}; +#define HIPOWERen 2 /* even poly, negative terms */ +static unsigned short evennegterms[HIPOWERen][4] = +{ + {0x3a7c, 0xe4c5, 0x7f87, 0x2945}, + {0x572b, 0x664c, 0xc543, 0x018c} +}; + + +/*--- poly_tan() ------------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +void +poly_tan(FPU_REG * arg, FPU_REG * y_reg) +{ + char invert = 0; + short exponent; + FPU_REG odd_poly, even_poly, pos_poly, neg_poly; + FPU_REG argSq; + long long arg_signif, argSqSq; + + + exponent = arg->exp - EXP_BIAS; + + if (arg->tag == TW_Zero) { + /* Return 0.0 */ + reg_move(&CONST_Z, y_reg); + return; + } + if (exponent >= -1) { + /* argument is in the range [0.5 .. 1.0] */ + if (exponent >= 0) { +#ifdef PARANOID + if ((exponent == 0) && + (arg->sigl == 0) && (arg->sigh == 0x80000000)) +#endif /* PARANOID */ + { + arith_overflow(y_reg); + return; + } +#ifdef PARANOID + EXCEPTION(EX_INTERNAL | 0x104); /* There must be a logic + * error */ + return; +#endif /* PARANOID */ + } + /* The argument is in the range [0.5 .. 1.0) */ + /* Convert the argument to a number in the range (0.0 .. 0.5] */ + *((long long *) (&arg->sigl)) = -*((long long *) (&arg->sigl)); + normalize(arg); /* Needed later */ + exponent = arg->exp - EXP_BIAS; + invert = 1; + } +#ifdef PARANOID + if (arg->sign != 0) { /* Can't hack a number < 0.0 */ + arith_invalid(y_reg); + return; + } /* Need a positive number */ +#endif /* PARANOID */ + + *(long long *) &arg_signif = *(long long *) &(arg->sigl); + if (exponent < -1) { + /* shift the argument right by the required places */ + if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000) + arg_signif++; /* round up */ + } + mul64(&arg_signif, &arg_signif, (long long *) (&argSq.sigl)); + mul64((long long *) (&argSq.sigl), (long long *) (&argSq.sigl), &argSqSq); + + /* will be a valid positive nr with expon = 0 */ + *(short *) &(pos_poly.sign) = 0; + pos_poly.exp = EXP_BIAS; + + /* Do the basic fixed point polynomial evaluation */ + polynomial(&pos_poly.sigl, (unsigned *) &argSqSq, oddplterms, HIPOWERop - 1); + + /* will be a valid positive nr with expon = 0 */ + *(short *) &(neg_poly.sign) = 0; + neg_poly.exp = EXP_BIAS; + + /* Do the basic fixed point polynomial evaluation */ + polynomial(&neg_poly.sigl, (unsigned *) &argSqSq, oddnegterms, HIPOWERon - 1); + mul64((long long *) (&argSq.sigl), (long long *) (&neg_poly.sigl), + (long long *) (&neg_poly.sigl)); + + /* Subtract the mantissas */ + *((long long *) (&pos_poly.sigl)) -= *((long long *) (&neg_poly.sigl)); + + /* Convert to 64 bit signed-compatible */ + pos_poly.exp -= 1; + + reg_move(&pos_poly, &odd_poly); + normalize(&odd_poly); + + reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION); + reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION); /* This is just the odd + * polynomial */ + + + /* will be a valid positive nr with expon = 0 */ + *(short *) &(pos_poly.sign) = 0; + pos_poly.exp = EXP_BIAS; + + /* Do the basic fixed point polynomial evaluation */ + polynomial(&pos_poly.sigl, (unsigned *) &argSqSq, evenplterms, HIPOWERep - 1); + mul64((long long *) (&argSq.sigl), + (long long *) (&pos_poly.sigl), (long long *) (&pos_poly.sigl)); + + /* will be a valid positive nr with expon = 0 */ + *(short *) &(neg_poly.sign) = 0; + neg_poly.exp = EXP_BIAS; + + /* Do the basic fixed point polynomial evaluation */ + polynomial(&neg_poly.sigl, (unsigned *) &argSqSq, evennegterms, HIPOWERen - 1); + + /* Subtract the mantissas */ + *((long long *) (&neg_poly.sigl)) -= *((long long *) (&pos_poly.sigl)); + /* and multiply by argSq */ + + /* Convert argSq to a valid reg number */ + *(short *) &(argSq.sign) = 0; + argSq.exp = EXP_BIAS - 1; + normalize(&argSq); + + /* Convert to 64 bit signed-compatible */ + neg_poly.exp -= 1; + + reg_move(&neg_poly, &even_poly); + normalize(&even_poly); + + reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION); + reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION); + reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION); /* This is just the even + * polynomial */ + + /* Now ready to copy the results */ + if (invert) { + reg_div(&even_poly, &odd_poly, y_reg, FULL_PRECISION); + } else { + reg_div(&odd_poly, &even_poly, y_reg, FULL_PRECISION); + } + +} diff --git a/sys/gnu/i386/fpemul/polynomial.s b/sys/gnu/i386/fpemul/polynomial.s new file mode 100644 index 0000000..c5a2596 --- /dev/null +++ b/sys/gnu/i386/fpemul/polynomial.s @@ -0,0 +1,179 @@ +/* + * polynomial.S + * + * Fixed point arithmetic polynomial evaluation. + * + * Call from C as: + * void polynomial(unsigned accum[], unsigned x[], unsigned terms[][2], + * int n) + * + * Computes: + * terms[0] + (terms[1] + (terms[2] + ... + (terms[n-1]*x)*x)*x)*x) ... )*x + * The result is returned in accum. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + .file "fpolynom.s" + +#include "fpu_asm.h" + + +/* #define EXTRA_PRECISE*/ + +#define TERM_SIZE $8 + + +.text + .align 2,144 +.globl _polynomial +_polynomial: + pushl %ebp + movl %esp,%ebp + subl $32,%esp + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi /* accum */ + movl PARAM2,%edi /* x */ + movl PARAM3,%ebx /* terms */ + movl PARAM4,%ecx /* n */ + + movl TERM_SIZE,%eax + mull %ecx + movl %eax,%ecx + + movl 4(%ebx,%ecx,1),%edx /* terms[n] */ + movl %edx,-20(%ebp) + movl (%ebx,%ecx,1),%edx /* terms[n] */ + movl %edx,-24(%ebp) + xor %eax,%eax + movl %eax,-28(%ebp) + + subl TERM_SIZE,%ecx + js L_accum_done + +L_accum_loop: + xor %eax,%eax + movl %eax,-4(%ebp) + movl %eax,-8(%ebp) + +#ifdef EXTRA_PRECISE + movl -28(%ebp),%eax + mull 4(%edi) /* x ms long */ + movl %edx,-12(%ebp) +#endif EXTRA_PRECISE + + movl -24(%ebp),%eax + mull (%edi) /* x ls long */ +/* movl %eax,-16(%ebp) */ /* Not needed */ + addl %edx,-12(%ebp) + adcl $0,-8(%ebp) + + movl -24(%ebp),%eax + mull 4(%edi) /* x ms long */ + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl -20(%ebp),%eax + mull (%edi) + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl -20(%ebp),%eax + mull 4(%edi) + addl %eax,-8(%ebp) + adcl %edx,-4(%ebp) + +/* Now add the next term */ + movl (%ebx,%ecx,1),%eax + addl %eax,-8(%ebp) + movl 4(%ebx,%ecx,1),%eax + adcl %eax,-4(%ebp) + +/* And put into the second register */ + movl -4(%ebp),%eax + movl %eax,-20(%ebp) + movl -8(%ebp),%eax + movl %eax,-24(%ebp) + +#ifdef EXTRA_PRECISE + movl -12(%ebp),%eax + movl %eax,-28(%ebp) +#else + testb $128,-25(%ebp) + je L_no_poly_round + + addl $1,-24(%ebp) + adcl $0,-20(%ebp) +L_no_poly_round: +#endif EXTRA_PRECISE + + subl TERM_SIZE,%ecx + jns L_accum_loop + +L_accum_done: +#ifdef EXTRA_PRECISE +/* And round the result */ + testb $128,-25(%ebp) + je L_poly_done + + addl $1,-24(%ebp) + adcl $0,-20(%ebp) +#endif EXTRA_PRECISE + +L_poly_done: + movl -24(%ebp),%eax + movl %eax,(%esi) + movl -20(%ebp),%eax + movl %eax,4(%esi) + + popl %ebx + popl %edi + popl %esi + leave + ret diff --git a/sys/gnu/i386/fpemul/reg_add_sub.c b/sys/gnu/i386/fpemul/reg_add_sub.c new file mode 100644 index 0000000..cc2a727 --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_add_sub.c @@ -0,0 +1,290 @@ +/* + * reg_add_sub.c + * + * Functions to add or subtract two registers and put the result in a third. + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/*---------------------------------------------------------------------------+ + | For each function, the destination may be any FPU_REG, including one of | + | the source FPU_REGs. | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "fpu_system.h" + + +void +reg_add(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w) +{ + int diff; + + if (!(a->tag | b->tag)) { + /* Both registers are valid */ + if (!(a->sign ^ b->sign)) { + /* signs are the same */ + reg_u_add(a, b, dest, control_w); + dest->sign = a->sign; + return; + } + /* The signs are different, so do a subtraction */ + diff = a->exp - b->exp; + if (!diff) { + diff = a->sigh - b->sigh; /* Works only if ms bits + * are identical */ + if (!diff) { + diff = a->sigl > b->sigl; + if (!diff) + diff = -(a->sigl < b->sigl); + } + } + if (diff > 0) { + reg_u_sub(a, b, dest, control_w); + dest->sign = a->sign; + } else + if (diff == 0) { + reg_move(&CONST_Z, dest); + /* sign depends upon rounding mode */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } else { + reg_u_sub(b, a, dest, control_w); + dest->sign = b->sign; + } + return; + } else { + if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) { + real_2op_NaN(a, b, dest); + return; + } else + if (a->tag == TW_Zero) { + if (b->tag == TW_Zero) { + char different_signs = a->sign ^ b->sign; + /* Both are zero, result will be zero. */ + reg_move(a, dest); + if (different_signs) { + /* Signs are different. */ + /* Sign of answer depends upon + * rounding mode. */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } + } else { +#ifdef DENORM_OPERAND + if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand()) + return; +#endif /* DENORM_OPERAND */ + reg_move(b, dest); + } + return; + } else + if (b->tag == TW_Zero) { +#ifdef DENORM_OPERAND + if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand()) + return; +#endif /* DENORM_OPERAND */ + reg_move(a, dest); + return; + } else + if (a->tag == TW_Infinity) { + if (b->tag != TW_Infinity) { +#ifdef DENORM_OPERAND + if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand()) + return; +#endif /* DENORM_OPERAND */ + reg_move(a, dest); + return; + } + if (a->sign == b->sign) { + /* They are both + or + * - infinity */ + reg_move(a, dest); + return; + } + arith_invalid(dest); /* Infinity-Infinity is + * undefined. */ + return; + } else + if (b->tag == TW_Infinity) { +#ifdef DENORM_OPERAND + if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand()) + return; +#endif /* DENORM_OPERAND */ + reg_move(b, dest); + return; + } + } +#ifdef PARANOID + EXCEPTION(EX_INTERNAL | 0x101); +#endif +} + + +/* Subtract b from a. (a-b) -> dest */ +void +reg_sub(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w) +{ + int diff; + + if (!(a->tag | b->tag)) { + /* Both registers are valid */ + diff = a->exp - b->exp; + if (!diff) { + diff = a->sigh - b->sigh; /* Works only if ms bits + * are identical */ + if (!diff) { + diff = a->sigl > b->sigl; + if (!diff) + diff = -(a->sigl < b->sigl); + } + } + switch (a->sign * 2 + b->sign) { + case 0: /* P - P */ + case 3: /* N - N */ + if (diff > 0) { + reg_u_sub(a, b, dest, control_w); + dest->sign = a->sign; + } else + if (diff == 0) { +#ifdef DENORM_OPERAND + if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand()) + return; +#endif /* DENORM_OPERAND */ + reg_move(&CONST_Z, dest); + /* sign depends upon rounding mode */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } else { + reg_u_sub(b, a, dest, control_w); + dest->sign = a->sign ^ SIGN_POS ^ SIGN_NEG; + } + return; + case 1: /* P - N */ + reg_u_add(a, b, dest, control_w); + dest->sign = SIGN_POS; + return; + case 2: /* N - P */ + reg_u_add(a, b, dest, control_w); + dest->sign = SIGN_NEG; + return; + } + } else { + if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) { + real_2op_NaN(a, b, dest); + return; + } else + if (b->tag == TW_Zero) { + if (a->tag == TW_Zero) { + char same_signs = !(a->sign ^ b->sign); + /* Both are zero, result will be zero. */ + reg_move(a, dest); /* Answer for different + * signs. */ + if (same_signs) { + /* Sign depends upon rounding + * mode */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } + } else { +#ifdef DENORM_OPERAND + if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand()) + return; +#endif /* DENORM_OPERAND */ + reg_move(a, dest); + } + return; + } else + if (a->tag == TW_Zero) { +#ifdef DENORM_OPERAND + if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand()) + return; +#endif /* DENORM_OPERAND */ + reg_move(b, dest); + dest->sign ^= SIGN_POS ^ SIGN_NEG; + return; + } else + if (a->tag == TW_Infinity) { + if (b->tag != TW_Infinity) { +#ifdef DENORM_OPERAND + if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand()) + return; +#endif /* DENORM_OPERAND */ + reg_move(a, dest); + return; + } + /* Both args are Infinity */ + if (a->sign == b->sign) { + arith_invalid(dest); /* Infinity-Infinity is + * undefined. */ + return; + } + reg_move(a, dest); + return; + } else + if (b->tag == TW_Infinity) { +#ifdef DENORM_OPERAND + if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand()) + return; +#endif /* DENORM_OPERAND */ + reg_move(b, dest); + dest->sign ^= SIGN_POS ^ SIGN_NEG; + return; + } + } +#ifdef PARANOID + EXCEPTION(EX_INTERNAL | 0x110); +#endif +} diff --git a/sys/gnu/i386/fpemul/reg_compare.c b/sys/gnu/i386/fpemul/reg_compare.c new file mode 100644 index 0000000..f543311 --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_compare.c @@ -0,0 +1,371 @@ +/* + * reg_compare.c + * + * Compare two floating point registers + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/*---------------------------------------------------------------------------+ + | compare() is the core FPU_REG comparison function | + +---------------------------------------------------------------------------*/ +#include "param.h" +#include "proc.h" +#include "systm.h" +#include "machine/cpu.h" +#include "machine/pcb.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "exception.h" +#include "control_w.h" +#include "status_w.h" + + +int +compare(FPU_REG * b) +{ + int diff; + + if (FPU_st0_ptr->tag | b->tag) { + if (FPU_st0_ptr->tag == TW_Zero) { + if (b->tag == TW_Zero) + return COMP_A_eq_B; + if (b->tag == TW_Valid) { +#ifdef DENORM_OPERAND + if ((b->exp <= EXP_UNDER) && (denormal_operand())) + return COMP_Denormal; +#endif /* DENORM_OPERAND */ + return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B; + } + } else + if (b->tag == TW_Zero) { + if (FPU_st0_ptr->tag == TW_Valid) { +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) + return COMP_Denormal; +#endif /* DENORM_OPERAND */ + return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B; + } + } + if (FPU_st0_ptr->tag == TW_Infinity) { + if ((b->tag == TW_Valid) || (b->tag == TW_Zero)) { +#ifdef DENORM_OPERAND + if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) + && (denormal_operand())) + return COMP_Denormal; +#endif /* DENORM_OPERAND */ + return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B; + } else + if (b->tag == TW_Infinity) { + /* The 80486 book says that infinities + * can be equal! */ + return (FPU_st0_ptr->sign == b->sign) ? COMP_A_eq_B : + ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); + } + /* Fall through to the NaN code */ + } else + if (b->tag == TW_Infinity) { + if ((FPU_st0_ptr->tag == TW_Valid) || (FPU_st0_ptr->tag == TW_Zero)) { +#ifdef DENORM_OPERAND + if ((FPU_st0_ptr->tag == TW_Valid) + && (FPU_st0_ptr->exp <= EXP_UNDER) + && (denormal_operand())) + return COMP_Denormal; +#endif /* DENORM_OPERAND */ + return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B; + } + /* Fall through to the NaN code */ + } + /* The only possibility now should be that one of the + * arguments is a NaN */ + if ((FPU_st0_ptr->tag == TW_NaN) || (b->tag == TW_NaN)) { + if (((FPU_st0_ptr->tag == TW_NaN) && !(FPU_st0_ptr->sigh & 0x40000000)) + || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000))) + /* At least one arg is a signaling NaN */ + return COMP_No_Comp | COMP_SNaN | COMP_NaN; + else + /* Neither is a signaling NaN */ + return COMP_No_Comp | COMP_NaN; + } + EXCEPTION(EX_Invalid); + } +#ifdef PARANOID + if (!(FPU_st0_ptr->sigh & 0x80000000)) + EXCEPTION(EX_Invalid); + if (!(b->sigh & 0x80000000)) + EXCEPTION(EX_Invalid); +#endif /* PARANOID */ + +#ifdef DENORM_OPERAND + if (((FPU_st0_ptr->exp <= EXP_UNDER) || + (b->exp <= EXP_UNDER)) && (denormal_operand())) + return COMP_Denormal; +#endif /* DENORM_OPERAND */ + + if (FPU_st0_ptr->sign != b->sign) + return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B; + + diff = FPU_st0_ptr->exp - b->exp; + if (diff == 0) { + diff = FPU_st0_ptr->sigh - b->sigh; /* Works only if ms bits + * are identical */ + if (diff == 0) { + diff = FPU_st0_ptr->sigl > b->sigl; + if (diff == 0) + diff = -(FPU_st0_ptr->sigl < b->sigl); + } + } + if (diff > 0) + return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B; + if (diff < 0) + return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B; + return COMP_A_eq_B; + +} + + +/* This function requires that st(0) is not empty */ +int +compare_st_data(void) +{ + int f, c; + + c = compare(&FPU_loaded_data); + + if (c & (COMP_NaN | COMP_Denormal)) { + if (c & COMP_NaN) { + EXCEPTION(EX_Invalid); + f = SW_C3 | SW_C2 | SW_C0; + } else { + /* One of the operands is a de-normal */ + return 0; + } + } else + switch (c) { + case COMP_A_lt_B: + f = SW_C0; + break; + case COMP_A_eq_B: + f = SW_C3; + break; + case COMP_A_gt_B: + f = 0; + break; + case COMP_No_Comp: + f = SW_C3 | SW_C2 | SW_C0; + break; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL | 0x121); + f = SW_C3 | SW_C2 | SW_C0; + break; +#endif /* PARANOID */ + } + setcc(f); + return 1; +} + + +static int +compare_st_st(int nr) +{ + int f, c; + + if (!NOT_EMPTY_0 || !NOT_EMPTY(nr)) { + setcc(SW_C3 | SW_C2 | SW_C0); + /* Stack fault */ + EXCEPTION(EX_StackUnder); + return control_word & CW_Invalid; + } + c = compare(&st(nr)); + if (c & (COMP_NaN | COMP_Denormal)) { + if (c & COMP_NaN) { + setcc(SW_C3 | SW_C2 | SW_C0); + EXCEPTION(EX_Invalid); + return control_word & CW_Invalid; + } else { + /* One of the operands is a de-normal */ + return control_word & CW_Denormal; + } + } else + switch (c) { + case COMP_A_lt_B: + f = SW_C0; + break; + case COMP_A_eq_B: + f = SW_C3; + break; + case COMP_A_gt_B: + f = 0; + break; + case COMP_No_Comp: + f = SW_C3 | SW_C2 | SW_C0; + break; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL | 0x122); + f = SW_C3 | SW_C2 | SW_C0; + break; +#endif /* PARANOID */ + } + setcc(f); + return 1; +} + + +static int +compare_u_st_st(int nr) +{ + int f, c; + + if (!NOT_EMPTY_0 || !NOT_EMPTY(nr)) { + setcc(SW_C3 | SW_C2 | SW_C0); + /* Stack fault */ + EXCEPTION(EX_StackUnder); + return control_word & CW_Invalid; + } + c = compare(&st(nr)); + if (c & (COMP_NaN | COMP_Denormal)) { + if (c & COMP_NaN) { + setcc(SW_C3 | SW_C2 | SW_C0); + if (c & COMP_SNaN) { /* This is the only difference + * between un-ordered and + * ordinary comparisons */ + EXCEPTION(EX_Invalid); + return control_word & CW_Invalid; + } + return 1; + } else { + /* One of the operands is a de-normal */ + return control_word & CW_Denormal; + } + } else + switch (c) { + case COMP_A_lt_B: + f = SW_C0; + break; + case COMP_A_eq_B: + f = SW_C3; + break; + case COMP_A_gt_B: + f = 0; + break; + case COMP_No_Comp: + f = SW_C3 | SW_C2 | SW_C0; + break; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL | 0x123); + f = SW_C3 | SW_C2 | SW_C0; + break; +#endif /* PARANOID */ + } + setcc(f); + return 1; +} +/*---------------------------------------------------------------------------*/ + +void +fcom_st() +{ + /* fcom st(i) */ + compare_st_st(FPU_rm); +} + + +void +fcompst() +{ + /* fcomp st(i) */ + if (compare_st_st(FPU_rm)) + pop(); +} + + +void +fcompp() +{ + /* fcompp */ + if (FPU_rm != 1) + return Un_impl(); + if (compare_st_st(1)) { + pop(); + FPU_st0_ptr = &st(0); + pop(); + } +} + + +void +fucom_() +{ + /* fucom st(i) */ + compare_u_st_st(FPU_rm); + +} + + +void +fucomp() +{ + /* fucomp st(i) */ + if (compare_u_st_st(FPU_rm)) + pop(); +} + + +void +fucompp() +{ + /* fucompp */ + if (FPU_rm == 1) { + if (compare_u_st_st(1)) { + pop(); + FPU_st0_ptr = &st(0); + pop(); + } + } else + Un_impl(); +} diff --git a/sys/gnu/i386/fpemul/reg_constant.c b/sys/gnu/i386/fpemul/reg_constant.c new file mode 100644 index 0000000..d779862 --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_constant.c @@ -0,0 +1,159 @@ +/* + * reg_constant.c + * + * All of the constant FPU_REGs + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "param.h" +#include "proc.h" +#include "systm.h" +#include "machine/cpu.h" +#include "machine/pcb.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "status_w.h" +#include "reg_constant.h" + + +FPU_REG CONST_1 = {SIGN_POS, TW_Valid, EXP_BIAS, +0x00000000, 0x80000000}; +FPU_REG CONST_2 = {SIGN_POS, TW_Valid, EXP_BIAS + 1, +0x00000000, 0x80000000}; +FPU_REG CONST_HALF = {SIGN_POS, TW_Valid, EXP_BIAS - 1, +0x00000000, 0x80000000}; +FPU_REG CONST_L2T = {SIGN_POS, TW_Valid, EXP_BIAS + 1, +0xcd1b8afe, 0xd49a784b}; +FPU_REG CONST_L2E = {SIGN_POS, TW_Valid, EXP_BIAS, +0x5c17f0bc, 0xb8aa3b29}; +FPU_REG CONST_PI = {SIGN_POS, TW_Valid, EXP_BIAS + 1, +0x2168c235, 0xc90fdaa2}; +FPU_REG CONST_PI2 = {SIGN_POS, TW_Valid, EXP_BIAS, +0x2168c235, 0xc90fdaa2}; +FPU_REG CONST_PI4 = {SIGN_POS, TW_Valid, EXP_BIAS - 1, +0x2168c235, 0xc90fdaa2}; +FPU_REG CONST_LG2 = {SIGN_POS, TW_Valid, EXP_BIAS - 2, +0xfbcff799, 0x9a209a84}; +FPU_REG CONST_LN2 = {SIGN_POS, TW_Valid, EXP_BIAS - 1, +0xd1cf79ac, 0xb17217f7}; +/* Only the sign (and tag) is used in internal zeroes */ +FPU_REG CONST_Z = {SIGN_POS, TW_Zero, 0, 0x0, 0x0}; +/* Only the sign and significand (and tag) are used in internal NaNs */ +/* The 80486 never generates one of these +FPU_REG CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 }; + */ +/* This is the real indefinite QNaN */ +FPU_REG CONST_QNaN = {SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000}; +/* Only the sign (and tag) is used in internal infinities */ +FPU_REG CONST_INF = {SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000}; + + + +static void +fld_const(FPU_REG * c) +{ + FPU_REG *st_new_ptr; + + if (STACK_OVERFLOW) { + stack_overflow(); + return; + } + push(); + reg_move(c, FPU_st0_ptr); + status_word &= ~SW_C1; +} + + +static void +fld1(void) +{ + fld_const(&CONST_1); +} + +static void +fldl2t(void) +{ + fld_const(&CONST_L2T); +} + +static void +fldl2e(void) +{ + fld_const(&CONST_L2E); +} + +static void +fldpi(void) +{ + fld_const(&CONST_PI); +} + +static void +fldlg2(void) +{ + fld_const(&CONST_LG2); +} + +static void +fldln2(void) +{ + fld_const(&CONST_LN2); +} + +static void +fldz(void) +{ + fld_const(&CONST_Z); +} + +static FUNC constants_table[] = { + fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, Un_impl +}; + +void +fconst(void) +{ + (constants_table[FPU_rm]) (); +} diff --git a/sys/gnu/i386/fpemul/reg_constant.h b/sys/gnu/i386/fpemul/reg_constant.h new file mode 100644 index 0000000..a5181f9 --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_constant.h @@ -0,0 +1,69 @@ +/* + * reg_constant.h + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _REG_CONSTANT_H_ +#define _REG_CONSTANT_H_ + +#include "fpu_emu.h" + +extern FPU_REG CONST_1; +extern FPU_REG CONST_2; +extern FPU_REG CONST_HALF; +extern FPU_REG CONST_L2T; +extern FPU_REG CONST_L2E; +extern FPU_REG CONST_PI; +extern FPU_REG CONST_PI2; +extern FPU_REG CONST_PI4; +extern FPU_REG CONST_LG2; +extern FPU_REG CONST_LN2; +extern FPU_REG CONST_Z; +extern FPU_REG CONST_PINF; +extern FPU_REG CONST_INF; +extern FPU_REG CONST_MINF; +extern FPU_REG CONST_QNaN; + +#endif /* _REG_CONSTANT_H_ */ diff --git a/sys/gnu/i386/fpemul/reg_div.s b/sys/gnu/i386/fpemul/reg_div.s new file mode 100644 index 0000000..0abdc05 --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_div.s @@ -0,0 +1,282 @@ + .file "reg_div.S" +/* + * reg_div.S + * + * Divide one FPU_REG by another and put the result in a destination FPU_REG. + * + * Call from C as: + * void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * unsigned int control_word) + */ + +#include "exception.h" +#include "fpu_asm.h" +#include "control_w.h" + +.text + .align 2 + +.globl _reg_div +_reg_div: + pushl %ebp + movl %esp,%ebp + + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi + movl PARAM2,%ebx + movl PARAM3,%edi + + movb TAG(%esi),%al + orb TAG(%ebx),%al + + jne L_div_special /* Not (both numbers TW_Valid) */ + +#ifdef DENORM_OPERAND +/* Check for denormals */ + cmpl EXP_UNDER,EXP(%esi) + jg xL_arg1_not_denormal + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit + +xL_arg1_not_denormal: + cmpl EXP_UNDER,EXP(%ebx) + jg xL_arg2_not_denormal + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit + +xL_arg2_not_denormal: +#endif DENORM_OPERAND + +/* Both arguments are TW_Valid */ + movb TW_Valid,TAG(%edi) + + movb SIGN(%esi),%cl + cmpb %cl,SIGN(%ebx) + setne (%edi) /* Set the sign, requires SIGN_NEG=1, SIGN_POS=0 */ + + movl EXP(%esi),%edx + movl EXP(%ebx),%eax + subl %eax,%edx + addl EXP_BIAS,%edx + movl %edx,EXP(%edi) + + jmp _divide_kernel + + +/*-----------------------------------------------------------------------*/ +L_div_special: + cmpb TW_NaN,TAG(%esi) /* A NaN with anything to give NaN */ + je L_arg1_NaN + + cmpb TW_NaN,TAG(%ebx) /* A NaN with anything to give NaN */ + jne L_no_NaN_arg + +/* Operations on NaNs */ +L_arg1_NaN: +L_arg2_NaN: + pushl %edi /* Destination */ + pushl %ebx + pushl %esi + call _real_2op_NaN + jmp LDiv_exit + +/* Invalid operations */ +L_zero_zero: +L_inf_inf: + pushl %edi /* Destination */ + call _arith_invalid /* 0/0 or Infinity/Infinity */ + jmp LDiv_exit + +L_no_NaN_arg: + cmpb TW_Infinity,TAG(%esi) + jne L_arg1_not_inf + + cmpb TW_Infinity,TAG(%ebx) + je L_inf_inf /* invalid operation */ + + cmpb TW_Valid,TAG(%ebx) + je L_inf_valid + +#ifdef PARANOID + /* arg2 must be zero or valid */ + cmpb TW_Zero,TAG(%ebx) + ja L_unknown_tags +#endif PARANOID + + /* Note that p16-9 says that infinity/0 returns infinity */ + jmp L_copy_arg1 /* Answer is Inf */ + +L_inf_valid: +#ifdef DENORM_OPERAND + cmpl EXP_UNDER,EXP(%ebx) + jg L_copy_arg1 /* Answer is Inf */ + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit +#endif DENORM_OPERAND + + jmp L_copy_arg1 /* Answer is Inf */ + +L_arg1_not_inf: + cmpb TW_Zero,TAG(%ebx) /* Priority to div-by-zero error */ + jne L_arg2_not_zero + + cmpb TW_Zero,TAG(%esi) + je L_zero_zero /* invalid operation */ + +#ifdef PARANOID + /* arg1 must be valid */ + cmpb TW_Valid,TAG(%esi) + ja L_unknown_tags +#endif PARANOID + +/* Division by zero error */ + pushl %edi /* destination */ + movb SIGN(%esi),%al + xorb SIGN(%ebx),%al + pushl %eax /* lower 8 bits have the sign */ + call _divide_by_zero + jmp LDiv_exit + +L_arg2_not_zero: + cmpb TW_Infinity,TAG(%ebx) + jne L_arg2_not_inf + +#ifdef DENORM_OPERAND + cmpb TW_Valid,TAG(%esi) + jne L_return_zero + + cmpl EXP_UNDER,EXP(%esi) + jg L_return_zero /* Answer is zero */ + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit +#endif DENORM_OPERAND + + jmp L_return_zero /* Answer is zero */ + +L_arg2_not_inf: + +#ifdef PARANOID + cmpb TW_Zero,TAG(%esi) + jne L_unknown_tags +#endif PARANOID + + /* arg1 is zero, arg2 is not Infinity or a NaN */ + +#ifdef DENORM_OPERAND + cmpl EXP_UNDER,EXP(%ebx) + jg L_copy_arg1 /* Answer is zero */ + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit +#endif DENORM_OPERAND + +L_copy_arg1: + movb TAG(%esi),%ax + movb %ax,TAG(%edi) + movl EXP(%esi),%eax + movl %eax,EXP(%edi) + movl SIGL(%esi),%eax + movl %eax,SIGL(%edi) + movl SIGH(%esi),%eax + movl %eax,SIGH(%edi) + + movb SIGN(%esi),%cl + cmpb %cl,SIGN(%ebx) + jne LDiv_negative_result + + movb SIGN_POS,SIGN(%edi) + jmp LDiv_exit + +LDiv_set_result_sign: + movb SIGN(%esi),%cl + cmpb %cl,SIGN(%edi) + jne LDiv_negative_result + + movb SIGN_POS,SIGN(%ebx) + jmp LDiv_exit + +LDiv_negative_result: + movb SIGN_NEG,SIGN(%edi) + +LDiv_exit: + leal -12(%ebp),%esp + + popl %ebx + popl %edi + popl %esi + leave + ret + + +L_return_zero: + movb TW_Zero,TAG(%edi) + jmp LDiv_set_result_sign + +#ifdef PARANOID +L_unknown_tags: + push EX_INTERNAL | 0x208 + call EXCEPTION + + /* Generate a NaN for unknown tags */ + movl _CONST_QNaN,%eax + movl %eax,(%edi) + movl _CONST_QNaN+4,%eax + movl %eax,SIGL(%edi) + movl _CONST_QNaN+8,%eax + movl %eax,SIGH(%edi) + jmp LDiv_exit +#endif PARANOID diff --git a/sys/gnu/i386/fpemul/reg_ld_str.c b/sys/gnu/i386/fpemul/reg_ld_str.c new file mode 100644 index 0000000..a0c5568 --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_ld_str.c @@ -0,0 +1,1374 @@ +/* + * reg_ld_str.c + * + * All of the functions which transfer data between user memory and FPU_REGs. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/*---------------------------------------------------------------------------+ + | Note: | + | The file contains code which accesses user memory. | + | Emulator static data may change when user memory is accessed, due to | + | other processes using the emulator while swapping is in progress. | + +---------------------------------------------------------------------------*/ +#include "param.h" +#include "proc.h" +#include "systm.h" +#include "machine/cpu.h" +#include "machine/pcb.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "exception.h" +#include "reg_constant.h" +#include "control_w.h" +#include "status_w.h" + + +#define EXTENDED_Emax 0x3fff /* largest valid exponent */ +#define EXTENDED_Ebias 0x3fff +#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */ + +#define DOUBLE_Emax 1023 /* largest valid exponent */ +#define DOUBLE_Ebias 1023 +#define DOUBLE_Emin (-1022) /* smallest valid exponent */ + +#define SINGLE_Emax 127 /* largest valid exponent */ +#define SINGLE_Ebias 127 +#define SINGLE_Emin (-126) /* smallest valid exponent */ + +#define LOST_UP (EX_Precision | SW_C1) +#define LOST_DOWN EX_Precision + +FPU_REG FPU_loaded_data; + + +/* Get a long double from user memory */ +void +reg_load_extended(void) +{ + long double *s = (long double *) FPU_data_address; + unsigned long sigl, sigh, exp; + + REENTRANT_CHECK(OFF); + /* Use temporary variables here because FPU_loaded data is static and + * hence re-entrancy problems can arise */ + sigl = fuword((unsigned long *) s); + sigh = fuword(1 + (unsigned long *) s); + exp = fuword(4 + (unsigned short *) s); + REENTRANT_CHECK(ON); + + FPU_loaded_data.sigl = sigl; + FPU_loaded_data.sigh = sigh; + FPU_loaded_data.exp = exp; + + if (FPU_loaded_data.exp & 0x8000) + FPU_loaded_data.sign = SIGN_NEG; + else + FPU_loaded_data.sign = SIGN_POS; + if ((FPU_loaded_data.exp &= 0x7fff) == 0) { + if (!(FPU_loaded_data.sigl | FPU_loaded_data.sigh)) { + FPU_loaded_data.tag = TW_Zero; + return; + } + /* The number is a de-normal or pseudodenormal. */ + /* The 80486 doesn't regard pseudodenormals as denormals here. */ + if (!(FPU_loaded_data.sigh & 0x80000000)) + EXCEPTION(EX_Denormal); + FPU_loaded_data.exp++; + + /* The default behaviour will now take care of it. */ + } else + if (FPU_loaded_data.exp == 0x7fff) { + FPU_loaded_data.exp = EXTENDED_Emax; + if ((FPU_loaded_data.sigh == 0x80000000) + && (FPU_loaded_data.sigl == 0)) { + FPU_loaded_data.tag = TW_Infinity; + return; + } else + if (!(FPU_loaded_data.sigh & 0x80000000)) { + /* Unsupported NaN data type */ + EXCEPTION(EX_Invalid); + FPU_loaded_data.tag = TW_NaN; + return; + } + FPU_loaded_data.tag = TW_NaN; + return; + } + FPU_loaded_data.exp = (FPU_loaded_data.exp & 0x7fff) - EXTENDED_Ebias + + EXP_BIAS; + FPU_loaded_data.tag = TW_Valid; + + if (!(sigh & 0x80000000)) { + /* Unsupported data type */ + EXCEPTION(EX_Invalid); + normalize_nuo(&FPU_loaded_data); + } +} + + +/* Get a double from user memory */ +void +reg_load_double(void) +{ + double *dfloat = (double *) FPU_data_address; + int exp; + unsigned m64, l64; + + REENTRANT_CHECK(OFF); + m64 = fuword(1 + (unsigned long *) dfloat); + l64 = fuword((unsigned long *) dfloat); + REENTRANT_CHECK(ON); + + if (m64 & 0x80000000) + FPU_loaded_data.sign = SIGN_NEG; + else + FPU_loaded_data.sign = SIGN_POS; + exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias; + m64 &= 0xfffff; + if (exp > DOUBLE_Emax) { + /* Infinity or NaN */ + if ((m64 == 0) && (l64 == 0)) { + /* +- infinity */ + FPU_loaded_data.exp = EXTENDED_Emax; + FPU_loaded_data.tag = TW_Infinity; + return; + } else { + /* Must be a signaling or quiet NaN */ + FPU_loaded_data.exp = EXTENDED_Emax; + FPU_loaded_data.tag = TW_NaN; + FPU_loaded_data.sigh = (m64 << 11) | 0x80000000; + FPU_loaded_data.sigh |= l64 >> 21; + FPU_loaded_data.sigl = l64 << 11; + return; + } + } else + if (exp < DOUBLE_Emin) { + /* Zero or de-normal */ + if ((m64 == 0) && (l64 == 0)) { + /* Zero */ + int c = FPU_loaded_data.sign; + reg_move(&CONST_Z, &FPU_loaded_data); + FPU_loaded_data.sign = c; + return; + } else { + /* De-normal */ + EXCEPTION(EX_Denormal); + FPU_loaded_data.exp = DOUBLE_Emin + EXP_BIAS; + FPU_loaded_data.tag = TW_Valid; + FPU_loaded_data.sigh = m64 << 11; + FPU_loaded_data.sigh |= l64 >> 21; + FPU_loaded_data.sigl = l64 << 11; + normalize_nuo(&FPU_loaded_data); + return; + } + } else { + FPU_loaded_data.exp = exp + EXP_BIAS; + FPU_loaded_data.tag = TW_Valid; + FPU_loaded_data.sigh = (m64 << 11) | 0x80000000; + FPU_loaded_data.sigh |= l64 >> 21; + FPU_loaded_data.sigl = l64 << 11; + + return; + } +} + + +/* Get a float from user memory */ +void +reg_load_single(void) +{ + float *single = (float *) FPU_data_address; + unsigned m32; + int exp; + + REENTRANT_CHECK(OFF); + m32 = fuword((unsigned long *) single); + REENTRANT_CHECK(ON); + + if (m32 & 0x80000000) + FPU_loaded_data.sign = SIGN_NEG; + else + FPU_loaded_data.sign = SIGN_POS; + if (!(m32 & 0x7fffffff)) { + /* Zero */ + int c = FPU_loaded_data.sign; + reg_move(&CONST_Z, &FPU_loaded_data); + FPU_loaded_data.sign = c; + return; + } + exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias; + m32 = (m32 & 0x7fffff) << 8; + if (exp < SINGLE_Emin) { + /* De-normals */ + EXCEPTION(EX_Denormal); + FPU_loaded_data.exp = SINGLE_Emin + EXP_BIAS; + FPU_loaded_data.tag = TW_Valid; + FPU_loaded_data.sigh = m32; + FPU_loaded_data.sigl = 0; + normalize_nuo(&FPU_loaded_data); + return; + } else + if (exp > SINGLE_Emax) { + /* Infinity or NaN */ + if (m32 == 0) { + /* +- infinity */ + FPU_loaded_data.exp = EXTENDED_Emax; + FPU_loaded_data.tag = TW_Infinity; + return; + } else { + /* Must be a signaling or quiet NaN */ + FPU_loaded_data.exp = EXTENDED_Emax; + FPU_loaded_data.tag = TW_NaN; + FPU_loaded_data.sigh = m32 | 0x80000000; + FPU_loaded_data.sigl = 0; + return; + } + } else { + FPU_loaded_data.exp = exp + EXP_BIAS; + FPU_loaded_data.sigh = m32 | 0x80000000; + FPU_loaded_data.sigl = 0; + FPU_loaded_data.tag = TW_Valid; + } +} + + +/* Get a long long from user memory */ +void +reg_load_int64(void) +{ + long long *_s = (long long *) FPU_data_address; + int e; + long long s; + + REENTRANT_CHECK(OFF); + ((unsigned long *) &s)[0] = fuword((unsigned long *) _s); + ((unsigned long *) &s)[1] = fuword(1 + (unsigned long *) _s); + REENTRANT_CHECK(ON); + + if (s == 0) { + reg_move(&CONST_Z, &FPU_loaded_data); + return; + } + if (s > 0) + FPU_loaded_data.sign = SIGN_POS; + else { + s = -s; + FPU_loaded_data.sign = SIGN_NEG; + } + + e = EXP_BIAS + 63; + *((long long *) &FPU_loaded_data.sigl) = s; + FPU_loaded_data.exp = e; + FPU_loaded_data.tag = TW_Valid; + normalize_nuo(&FPU_loaded_data); +} + + +/* Get a long from user memory */ +void +reg_load_int32(void) +{ + long *_s = (long *) FPU_data_address; + long s; + int e; + + REENTRANT_CHECK(OFF); + s = (long) fuword((unsigned long *) _s); + REENTRANT_CHECK(ON); + + if (s == 0) { + reg_move(&CONST_Z, &FPU_loaded_data); + return; + } + if (s > 0) + FPU_loaded_data.sign = SIGN_POS; + else { + s = -s; + FPU_loaded_data.sign = SIGN_NEG; + } + + e = EXP_BIAS + 31; + FPU_loaded_data.sigh = s; + FPU_loaded_data.sigl = 0; + FPU_loaded_data.exp = e; + FPU_loaded_data.tag = TW_Valid; + normalize_nuo(&FPU_loaded_data); +} + + +/* Get a short from user memory */ +void +reg_load_int16(void) +{ + short *_s = (short *) FPU_data_address; + int s, e; + + REENTRANT_CHECK(OFF); + /* Cast as short to get the sign extended. */ + s = (short) fuword((unsigned short *) _s); + REENTRANT_CHECK(ON); + + if (s == 0) { + reg_move(&CONST_Z, &FPU_loaded_data); + return; + } + if (s > 0) + FPU_loaded_data.sign = SIGN_POS; + else { + s = -s; + FPU_loaded_data.sign = SIGN_NEG; + } + + e = EXP_BIAS + 15; + FPU_loaded_data.sigh = s << 16; + + FPU_loaded_data.sigl = 0; + FPU_loaded_data.exp = e; + FPU_loaded_data.tag = TW_Valid; + normalize_nuo(&FPU_loaded_data); +} + + +/* Get a packed bcd array from user memory */ +void +reg_load_bcd(void) +{ + char *s = (char *) FPU_data_address; + int pos; + unsigned char bcd; + long long l = 0; + + for (pos = 8; pos >= 0; pos--) { + l *= 10; + REENTRANT_CHECK(OFF); + bcd = (unsigned char) fubyte((unsigned char *) s + pos); + REENTRANT_CHECK(ON); + l += bcd >> 4; + l *= 10; + l += bcd & 0x0f; + } + + /* Finish all access to user memory before putting stuff into the + * static FPU_loaded_data */ + REENTRANT_CHECK(OFF); + FPU_loaded_data.sign = + ((unsigned char) fubyte((unsigned char *) s + 9)) & 0x80 ? + SIGN_NEG : SIGN_POS; + REENTRANT_CHECK(ON); + + if (l == 0) { + char sign = FPU_loaded_data.sign; + reg_move(&CONST_Z, &FPU_loaded_data); + FPU_loaded_data.sign = sign; + } else { + *((long long *) &FPU_loaded_data.sigl) = l; + FPU_loaded_data.exp = EXP_BIAS + 63; + FPU_loaded_data.tag = TW_Valid; + normalize_nuo(&FPU_loaded_data); + } +} +/*===========================================================================*/ + +/* Put a long double into user memory */ +int +reg_store_extended(void) +{ + long double *d = (long double *) FPU_data_address; + long e = FPU_st0_ptr->exp - EXP_BIAS + EXTENDED_Ebias; + unsigned short sign = FPU_st0_ptr->sign * 0x8000; + unsigned long ls, ms; + + + if (FPU_st0_tag == TW_Valid) { + if (e >= 0x7fff) { + EXCEPTION(EX_Overflow); /* Overflow */ + /* This is a special case: see sec 16.2.5.1 of the + * 80486 book */ + if (control_word & EX_Overflow) { + /* Overflow to infinity */ + ls = 0; + ms = 0x80000000; + e = 0x7fff; + } else + return 0; + } else + if (e <= 0) { + if (e > -63) { + /* Correctly format the de-normal */ + int precision_loss; + FPU_REG tmp; + + EXCEPTION(EX_Denormal); + reg_move(FPU_st0_ptr, &tmp); + tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */ + if ((precision_loss = round_to_int(&tmp))) { + EXCEPTION(EX_Underflow | precision_loss); + /* This is a special case: see + * sec 16.2.5.1 of the 80486 + * book */ + if (!(control_word & EX_Underflow)) + return 0; + } + e = 0; + ls = tmp.sigl; + ms = tmp.sigh; + } else { + /* ****** ??? This should not be + * possible */ + EXCEPTION(EX_Underflow); /* Underflow */ + /* This is a special case: see sec + * 16.2.5.1 of the 80486 book */ + if (control_word & EX_Underflow) { + /* Underflow to zero */ + ls = 0; + ms = 0; + e = FPU_st0_ptr->sign == SIGN_POS ? 0x7fff : 0xffff; + } else + return 0; + } + } else { + ls = FPU_st0_ptr->sigl; + ms = FPU_st0_ptr->sigh; + } + } else + if (FPU_st0_tag == TW_Zero) { + ls = ms = 0; + e = 0; + } else + if (FPU_st0_tag == TW_Infinity) { + ls = 0; + ms = 0x80000000; + e = 0x7fff; + } else + if (FPU_st0_tag == TW_NaN) { + ls = FPU_st0_ptr->sigl; + ms = FPU_st0_ptr->sigh; + e = 0x7fff; + } else + if (FPU_st0_tag == TW_Empty) { + /* Empty register (stack + * underflow) */ + EXCEPTION(EX_StackUnder); + if (control_word & EX_Invalid) { + /* The masked response */ + /* Put out the QNaN + * indefinite */ + ls = 0; + ms = 0xc0000000; + e = 0xffff; + } else + return 0; + } else { + /* We don't use TW_Denormal + * yet ... perhaps never! */ + EXCEPTION(EX_Invalid); + /* Store a NaN */ + e = 0x7fff; + ls = 1; + ms = 0x80000000; + } + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, d, 10); */ + suword((unsigned long *) d, ls); + suword(1 + (unsigned long *) d, ms); + suword(4 + (short *) d, (unsigned short) e | sign); + REENTRANT_CHECK(ON); + + return 1; + +} + + +/* Put a double into user memory */ +int +reg_store_double(void) +{ + double *dfloat = (double *) FPU_data_address; + unsigned long l[2]; + if (FPU_st0_tag == TW_Valid) { + int exp; + FPU_REG tmp; + + reg_move(FPU_st0_ptr, &tmp); + exp = tmp.exp - EXP_BIAS; + + if (exp < DOUBLE_Emin) { /* It may be a denormal */ + /* Make a de-normal */ + int precision_loss; + + if (exp <= -EXTENDED_Ebias) + EXCEPTION(EX_Denormal); + + tmp.exp += -DOUBLE_Emin + 52; /* largest exp to be 51 */ + + if ((precision_loss = round_to_int(&tmp))) { +#ifdef PECULIAR_486 + /* Did it round to a non-denormal ? */ + /* This behaviour might be regarded as + * peculiar, it appears that the 80486 rounds + * to the dest precision, then converts to + * decide underflow. */ + if ((tmp.sigh == 0x00100000) && (tmp.sigl == 0) && + (FPU_st0_ptr->sigl & 0x000007ff)) + EXCEPTION(precision_loss); + else +#endif /* PECULIAR_486 */ + { + EXCEPTION(EX_Underflow | precision_loss); + /* This is a special case: see sec + * 16.2.5.1 of the 80486 book */ + if (!(control_word & EX_Underflow)) + return 0; + } + } + l[0] = tmp.sigl; + l[1] = tmp.sigh; + } else { + if (tmp.sigl & 0x000007ff) { + unsigned long increment = 0; /* avoid gcc warnings */ + + switch (control_word & CW_RC) { + case RC_RND: + /* Rounding can get a little messy.. */ + increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */ + ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */ + break; + case RC_DOWN: /* towards -infinity */ + increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff; + break; + case RC_UP: /* towards +infinity */ + increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0; + break; + case RC_CHOP: + increment = 0; + break; + } + + /* Truncate the mantissa */ + tmp.sigl &= 0xfffff800; + + if (increment) { + set_precision_flag_up(); + + if (tmp.sigl >= 0xfffff800) { + /* the sigl part overflows */ + if (tmp.sigh == 0xffffffff) { + /* The sigh part + * overflows */ + tmp.sigh = 0x80000000; + exp++; + if (exp >= EXP_OVER) + goto overflow; + } else { + tmp.sigh++; + } + tmp.sigl = 0x00000000; + } else { + /* We only need to increment + * sigl */ + tmp.sigl += 0x00000800; + } + } else + set_precision_flag_down(); + } + l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21); + l[1] = ((tmp.sigh >> 11) & 0xfffff); + + if (exp > DOUBLE_Emax) { + overflow: + EXCEPTION(EX_Overflow); + /* This is a special case: see sec 16.2.5.1 of + * the 80486 book */ + if (control_word & EX_Overflow) { + /* Overflow to infinity */ + l[0] = 0x00000000; /* Set to */ + l[1] = 0x7ff00000; /* + INF */ + } else + return 0; + } else { + /* Add the exponent */ + l[1] |= (((exp + DOUBLE_Ebias) & 0x7ff) << 20); + } + } + } else + if (FPU_st0_tag == TW_Zero) { + /* Number is zero */ + l[0] = 0; + l[1] = 0; + } else + if (FPU_st0_tag == TW_Infinity) { + l[0] = 0; + l[1] = 0x7ff00000; + } else + if (FPU_st0_tag == TW_NaN) { + /* See if we can get a valid NaN from + * the FPU_REG */ + l[0] = (FPU_st0_ptr->sigl >> 11) | (FPU_st0_ptr->sigh << 21); + l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff); + if (!(l[0] | l[1])) { + /* This case does not seem to + * be handled by the 80486 + * specs */ + EXCEPTION(EX_Invalid); + /* Make the quiet NaN "real + * indefinite" */ + goto put_indefinite; + } + l[1] |= 0x7ff00000; + } else + if (FPU_st0_tag == TW_Empty) { + /* Empty register (stack + * underflow) */ + EXCEPTION(EX_StackUnder); + if (control_word & EX_Invalid) { + /* The masked response */ + /* Put out the QNaN + * indefinite */ + put_indefinite: + REENTRANT_CHECK(OFF); + /* verify_area(VERIFY_W + * RITE, (void *) + * dfloat, 8); */ + suword((unsigned long *) dfloat, 0); + suword(1 + (unsigned long *) dfloat, 0xfff80000); + REENTRANT_CHECK(ON); + return 1; + } else + return 0; + } +#if 0 /* TW_Denormal is not used yet, and probably + * won't be */ + else + if (FPU_st0_tag == TW_Denormal) { + /* Extended real -> + * double real will + * always underflow */ + l[0] = l[1] = 0; + EXCEPTION(EX_Underflow); + } +#endif + if (FPU_st0_ptr->sign) + l[1] |= 0x80000000; + + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, (void *) dfloat, 8);*/ + suword((u_long *) dfloat, l[0]); + suword((u_long *) dfloat + 1, l[1]); +/* + suword(l[0], (unsigned long *) dfloat); + suword(l[1], 1 + (unsigned long *) dfloat);*/ + REENTRANT_CHECK(ON); + + return 1; +} + + +/* Put a float into user memory */ +int +reg_store_single(void) +{ + float *single = (float *) FPU_data_address; + long templ; + + if (FPU_st0_tag == TW_Valid) { + int exp; + FPU_REG tmp; + + reg_move(FPU_st0_ptr, &tmp); + exp = tmp.exp - EXP_BIAS; + + if (exp < SINGLE_Emin) { + /* Make a de-normal */ + int precision_loss; + + if (exp <= -EXTENDED_Ebias) + EXCEPTION(EX_Denormal); + + tmp.exp += -SINGLE_Emin + 23; /* largest exp to be 22 */ + + if ((precision_loss = round_to_int(&tmp))) { +#ifdef PECULIAR_486 + /* Did it round to a non-denormal ? */ + /* This behaviour might be regarded as + * peculiar, it appears that the 80486 rounds + * to the dest precision, then converts to + * decide underflow. */ + if ((tmp.sigl == 0x00800000) && + ((FPU_st0_ptr->sigh & 0x000000ff) || FPU_st0_ptr->sigl)) + EXCEPTION(precision_loss); + else +#endif /* PECULIAR_486 */ + { + EXCEPTION(EX_Underflow | precision_loss); + /* This is a special case: see sec + * 16.2.5.1 of the 80486 book */ + if (!(control_word & EX_Underflow)) + return 0; + } + } + templ = tmp.sigl; + } else { + if (tmp.sigl | (tmp.sigh & 0x000000ff)) { + unsigned long increment = 0; /* avoid gcc warnings */ + unsigned long sigh = tmp.sigh; + unsigned long sigl = tmp.sigl; + + switch (control_word & CW_RC) { + case RC_RND: + increment = ((sigh & 0xff) > 0x80) /* more than half */ + ||(((sigh & 0xff) == 0x80) && sigl) /* more than half */ + ||((sigh & 0x180) == 0x180); /* round to even */ + break; + case RC_DOWN: /* towards -infinity */ + increment = (tmp.sign == SIGN_POS) + ? 0 : (sigl | (sigh & 0xff)); + break; + case RC_UP: /* towards +infinity */ + increment = (tmp.sign == SIGN_POS) + ? (sigl | (sigh & 0xff)) : 0; + break; + case RC_CHOP: + increment = 0; + break; + } + + /* Truncate part of the mantissa */ + tmp.sigl = 0; + + if (increment) { + set_precision_flag_up(); + + if (sigh >= 0xffffff00) { + /* The sigh part overflows */ + tmp.sigh = 0x80000000; + exp++; + if (exp >= EXP_OVER) + goto overflow; + } else { + tmp.sigh &= 0xffffff00; + tmp.sigh += 0x100; + } + } else { + set_precision_flag_down(); + tmp.sigh &= 0xffffff00; /* Finish the truncation */ + } + } + templ = (tmp.sigh >> 8) & 0x007fffff; + + if (exp > SINGLE_Emax) { + overflow: + EXCEPTION(EX_Overflow); + /* This is a special case: see sec 16.2.5.1 of + * the 80486 book */ + if (control_word & EX_Overflow) { + /* Overflow to infinity */ + templ = 0x7f800000; + } else + return 0; + } else + templ |= ((exp + SINGLE_Ebias) & 0xff) << 23; + } + } else + if (FPU_st0_tag == TW_Zero) { + templ = 0; + } else + if (FPU_st0_tag == TW_Infinity) { + templ = 0x7f800000; + } else + if (FPU_st0_tag == TW_NaN) { + /* See if we can get a valid NaN from + * the FPU_REG */ + templ = FPU_st0_ptr->sigh >> 8; + if (!(templ & 0x3fffff)) { + /* This case does not seem to + * be handled by the 80486 + * specs */ + EXCEPTION(EX_Invalid); + /* Make the quiet NaN "real + * indefinite" */ + goto put_indefinite; + } + templ |= 0x7f800000; + } else + if (FPU_st0_tag == TW_Empty) { + /* Empty register (stack + * underflow) */ + EXCEPTION(EX_StackUnder); + if (control_word & EX_Invalid) { + /* The masked response */ + /* Put out the QNaN + * indefinite */ + put_indefinite: + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, (void *) single, 4); */ + suword((unsigned long *) single, 0xffc00000); + REENTRANT_CHECK(ON); + return 1; + } else + return 0; + } +#if 0 /* TW_Denormal is not used yet, and probably + * won't be */ + else + if (FPU_st0_tag == TW_Denormal) { + /* Extended real -> + * real will always + * underflow */ + templ = 0; + EXCEPTION(EX_Underflow); + } +#endif +#ifdef PARANOID + else { + EXCEPTION(EX_INTERNAL | 0x106); + return 0; + } +#endif + if (FPU_st0_ptr->sign) + templ |= 0x80000000; + + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, (void *) single, 4); */ + suword((unsigned long *) single, templ); + REENTRANT_CHECK(ON); + + return 1; +} + + +/* Put a long long into user memory */ +int +reg_store_int64(void) +{ + long long *d = (long long *) FPU_data_address; + FPU_REG t; + long long tll; + + if (FPU_st0_tag == TW_Empty) { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + if (control_word & EX_Invalid) { + /* The masked response */ + /* Put out the QNaN indefinite */ + goto put_indefinite; + } else + return 0; + } + reg_move(FPU_st0_ptr, &t); + round_to_int(&t); + ((long *) &tll)[0] = t.sigl; + ((long *) &tll)[1] = t.sigh; + if ((t.sigh & 0x80000000) && + !((t.sigh == 0x80000000) && (t.sigl == 0) && (t.sign == SIGN_NEG))) { + EXCEPTION(EX_Invalid); + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + if (control_word & EX_Invalid) { + /* Produce "indefinite" */ + put_indefinite: + ((long *) &tll)[1] = 0x80000000; + ((long *) &tll)[0] = 0; + } else + return 0; + } else + if (t.sign) + tll = -tll; + + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, (void *) d, 8); */ + suword((unsigned long *) d, ((long *) &tll)[0]); + suword(1 + (unsigned long *) d, ((long *) &tll)[1]); + REENTRANT_CHECK(ON); + + return 1; +} + + +/* Put a long into user memory */ +int +reg_store_int32(void) +{ + long *d = (long *) FPU_data_address; + FPU_REG t; + + if (FPU_st0_tag == TW_Empty) { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + if (control_word & EX_Invalid) { + /* The masked response */ + /* Put out the QNaN indefinite */ + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, d, 4);*/ + suword((unsigned long *) d, 0x80000000); + REENTRANT_CHECK(ON); + return 1; + } else + return 0; + } + reg_move(FPU_st0_ptr, &t); + round_to_int(&t); + if (t.sigh || + ((t.sigl & 0x80000000) && + !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG)))) { + EXCEPTION(EX_Invalid); + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + if (control_word & EX_Invalid) { + /* Produce "indefinite" */ + t.sigl = 0x80000000; + } else + return 0; + } else + if (t.sign) + t.sigl = -(long) t.sigl; + + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, d, 4); */ + suword((unsigned long *) d, t.sigl); + REENTRANT_CHECK(ON); + + return 1; +} + + +/* Put a short into user memory */ +int +reg_store_int16(void) +{ + short *d = (short *) FPU_data_address; + FPU_REG t; + short ts; + + if (FPU_st0_tag == TW_Empty) { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + if (control_word & EX_Invalid) { + /* The masked response */ + /* Put out the QNaN indefinite */ + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, d, 2);*/ + suword((unsigned short *) d, 0x8000); + REENTRANT_CHECK(ON); + return 1; + } else + return 0; + } + reg_move(FPU_st0_ptr, &t); + round_to_int(&t); + if (t.sigh || + ((t.sigl & 0xffff8000) && + !((t.sigl == 0x8000) && (t.sign == SIGN_NEG)))) { + EXCEPTION(EX_Invalid); + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + if (control_word & EX_Invalid) { + /* Produce "indefinite" */ + ts = 0x8000; + } else + return 0; + } else + if (t.sign) + t.sigl = -t.sigl; + + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, d, 2); */ + suword((short *) d, (short) t.sigl); + REENTRANT_CHECK(ON); + + return 1; +} + + +/* Put a packed bcd array into user memory */ +int +reg_store_bcd(void) +{ + char *d = (char *) FPU_data_address; + FPU_REG t; + long long ll; + unsigned char b; + int i; + unsigned char sign = (FPU_st0_ptr->sign == SIGN_NEG) ? 0x80 : 0; + + if (FPU_st0_tag == TW_Empty) { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + if (control_word & EX_Invalid) { + /* The masked response */ + /* Put out the QNaN indefinite */ + goto put_indefinite; + } else + return 0; + } + reg_move(FPU_st0_ptr, &t); + round_to_int(&t); + ll = *(long long *) (&t.sigl); + + /* Check for overflow, by comparing with 999999999999999999 decimal. */ + if ((t.sigh > 0x0de0b6b3) || + ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff))) { + EXCEPTION(EX_Invalid); + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + if (control_word & EX_Invalid) { + put_indefinite: + /* Produce "indefinite" */ + REENTRANT_CHECK(OFF); +/* verify_area(VERIFY_WRITE, d, 10);*/ + subyte((unsigned char *) d + 7, 0xff); + subyte((unsigned char *) d + 8, 0xff); + subyte((unsigned char *) d + 9, 0xff); + REENTRANT_CHECK(ON); + return 1; + } else + return 0; + } +/* verify_area(VERIFY_WRITE, d, 10);*/ + for (i = 0; i < 9; i++) { + b = div_small(&ll, 10); + b |= (div_small(&ll, 10)) << 4; + REENTRANT_CHECK(OFF); + subyte((unsigned char *) d + i, b); + REENTRANT_CHECK(ON); + } + REENTRANT_CHECK(OFF); + subyte((unsigned char *) d + 9, sign); + REENTRANT_CHECK(ON); + + return 1; +} +/*===========================================================================*/ + +/* r gets mangled such that sig is int, sign: + it is NOT normalized */ +/* The return value (in eax) is zero if the result is exact, + if bits are changed due to rounding, truncation, etc, then + a non-zero value is returned */ +/* Overflow is signalled by a non-zero return value (in eax). + In the case of overflow, the returned significand always has the + the largest possible value */ +/* The value returned in eax is never actually needed :-) */ +int +round_to_int(FPU_REG * r) +{ + char very_big; + unsigned eax; + + if (r->tag == TW_Zero) { + /* Make sure that zero is returned */ + *(long long *) &r->sigl = 0; + return 0; /* o.k. */ + } + if (r->exp > EXP_BIAS + 63) { + r->sigl = r->sigh = ~0; /* The largest representable number */ + return 1; /* overflow */ + } + eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp); + very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */ +#define half_or_more (eax & 0x80000000) +#define frac_part (eax) +#define more_than_half ((eax & 0x80000001) == 0x80000001) + switch (control_word & CW_RC) { + case RC_RND: + if (more_than_half /* nearest */ + || (half_or_more && (r->sigl & 1))) { /* odd -> even */ + if (very_big) + return 1; /* overflow */ + (*(long long *) (&r->sigl))++; + return LOST_UP; + } + break; + case RC_DOWN: + if (frac_part && r->sign) { + if (very_big) + return 1; /* overflow */ + (*(long long *) (&r->sigl))++; + return LOST_UP; + } + break; + case RC_UP: + if (frac_part && !r->sign) { + if (very_big) + return 1; /* overflow */ + (*(long long *) (&r->sigl))++; + return LOST_UP; + } + break; + case RC_CHOP: + break; + } + + return eax ? LOST_DOWN : 0; + +} +/*===========================================================================*/ + +char * +fldenv(void) +{ + char *s = (char *) FPU_data_address; + unsigned short tag_word = 0; + unsigned char tag; + int i; + + REENTRANT_CHECK(OFF); + control_word = fuword((unsigned short *) s); + status_word = fuword((unsigned short *) (s + 4)); + tag_word = fuword((unsigned short *) (s + 8)); + ip_offset = fuword((unsigned long *) (s + 0x0c)); + cs_selector = fuword((unsigned long *) (s + 0x10)); + data_operand_offset = fuword((unsigned long *) (s + 0x14)); + operand_selector = fuword((unsigned long *) (s + 0x18)); + REENTRANT_CHECK(ON); + + top = (status_word >> SW_Top_Shift) & 7; + + for (i = 0; i < 8; i++) { + tag = tag_word & 3; + tag_word >>= 2; + + switch (tag) { + case 0: + regs[i].tag = TW_Valid; + break; + case 1: + regs[i].tag = TW_Zero; + break; + case 2: + regs[i].tag = TW_NaN; + break; + case 3: + regs[i].tag = TW_Empty; + break; + } + } + + FPU_data_address = (void *) data_operand_offset; /* We want no net effect */ + FPU_entry_eip = ip_offset; /* We want no net effect */ + + return s + 0x1c; +} + + +void +frstor(void) +{ + int i, stnr; + unsigned char tag; + unsigned short saved_status, saved_control; + char *s = (char *) fldenv(); + + saved_status = status_word; + saved_control = control_word; + control_word = 0x037f; /* Mask all interrupts while we load. */ + for (i = 0; i < 8; i++) { + /* load each register */ + FPU_data_address = (void *) (s + i * 10); + reg_load_extended(); + stnr = (i + top) & 7; + tag = regs[stnr].tag; /* derived from the loaded tag word */ + reg_move(&FPU_loaded_data, ®s[stnr]); + if (tag == TW_NaN) { + /* The current data is a special, i.e. NaN, + * unsupported, infinity, or denormal */ + unsigned char t = regs[stnr].tag; /* derived from the new + * data */ + if ( /* (t == TW_Valid) || *** */ (t == TW_Zero)) + regs[stnr].tag = TW_NaN; + } else + regs[stnr].tag = tag; + } + control_word = saved_control; + status_word = saved_status; + + FPU_data_address = (void *) data_operand_offset; /* We want no net effect */ +} + + +unsigned short +tag_word(void) +{ + unsigned short word = 0; + unsigned char tag; + int i; + + for (i = 7; i >= 0; i--) { + switch (tag = regs[i].tag) { +#if 0 /* TW_Denormal is not used yet, and probably + * won't be */ + case TW_Denormal: +#endif + case TW_Valid: + if (regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias)) + tag = 2; + break; + case TW_Infinity: + case TW_NaN: + tag = 2; + break; + case TW_Empty: + tag = 3; + break; + /* TW_Valid and TW_Zero already have the correct value */ + } + word <<= 2; + word |= tag; + } + return word; +} + + +char * +fstenv(void) +{ + char *d = (char *) FPU_data_address; + +/* verify_area(VERIFY_WRITE, d, 28);*/ + +#if 0 /****/ + *(unsigned short *) &cs_selector = fpu_cs; + *(unsigned short *) &operand_selector = fpu_os; +#endif /****/ + + REENTRANT_CHECK(OFF); + suword((unsigned short *) d, control_word); + suword((unsigned short *) (d + 4), (status_word & ~SW_Top) | ((top & 7) << SW_Top_Shift)); + suword((unsigned short *) (d + 8), tag_word()); + suword((unsigned long *) (d + 0x0c), ip_offset); + suword((unsigned long *) (d + 0x10), cs_selector); + suword((unsigned long *) (d + 0x14), data_operand_offset); + suword((unsigned long *) (d + 0x18), operand_selector); + REENTRANT_CHECK(ON); + + return d + 0x1c; +} + + +void +fsave(void) +{ + char *d; + FPU_REG tmp, *rp; + int i; + short e; + + d = fstenv(); +/* verify_area(VERIFY_WRITE, d, 80);*/ + for (i = 0; i < 8; i++) { + /* Store each register in the order: st(0), st(1), ... */ + rp = ®s[(top + i) & 7]; + + e = rp->exp - EXP_BIAS + EXTENDED_Ebias; + + if (rp->tag == TW_Valid) { + if (e >= 0x7fff) { + /* Overflow to infinity */ + REENTRANT_CHECK(OFF); + suword((unsigned long *) (d + i * 10), 0); + suword((unsigned long *) (d + i * 10 + 4), 0); + REENTRANT_CHECK(ON); + e = 0x7fff; + } else + if (e <= 0) { + if (e > -63) { + /* Make a de-normal */ + reg_move(rp, &tmp); + tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */ + round_to_int(&tmp); + REENTRANT_CHECK(OFF); + suword((unsigned long *) (d + i * 10), tmp.sigl); + suword((unsigned long *) (d + i * 10 + 4), tmp.sigh); + REENTRANT_CHECK(ON); + } else { + /* Underflow to zero */ + REENTRANT_CHECK(OFF); + suword((unsigned long *) (d + i * 10), 0); + suword((unsigned long *) (d + i * 10 + 4), 0); + REENTRANT_CHECK(ON); + } + e = 0; + } else { + REENTRANT_CHECK(OFF); + suword((unsigned long *) (d + i * 10), rp->sigl); + suword((unsigned long *) (d + i * 10 + 4), rp->sigh); + REENTRANT_CHECK(ON); + } + } else + if (rp->tag == TW_Zero) { + REENTRANT_CHECK(OFF); + suword((unsigned long *) (d + i * 10), 0); + suword((unsigned long *) (d + i * 10 + 4), 0); + REENTRANT_CHECK(ON); + e = 0; + } else + if (rp->tag == TW_Infinity) { + REENTRANT_CHECK(OFF); + suword((unsigned long *) (d + i * 10), 0); + suword((unsigned long *) (d + i * 10 + 4), 0x80000000); + REENTRANT_CHECK(ON); + e = 0x7fff; + } else + if (rp->tag == TW_NaN) { + REENTRANT_CHECK(OFF); + suword((unsigned long *) (d + i * 10), rp->sigl); + suword((unsigned long *) (d + i * 10 + 4), rp->sigh); + REENTRANT_CHECK(ON); + e = 0x7fff; + } else + if (rp->tag == TW_Empty) { + /* just copy the reg */ + REENTRANT_CHECK(OFF); + suword((unsigned long *) (d + i * 10), rp->sigl); + suword((unsigned long *) (d + i * 10 + 4), rp->sigh); + REENTRANT_CHECK(ON); + } + e |= rp->sign == SIGN_POS ? 0 : 0x8000; + REENTRANT_CHECK(OFF); + suword((unsigned short *) (d + i * 10 + 8), e); + REENTRANT_CHECK(ON); + } + + finit(); + +} +/*===========================================================================*/ diff --git a/sys/gnu/i386/fpemul/reg_mul.c b/sys/gnu/i386/fpemul/reg_mul.c new file mode 100644 index 0000000..4ae5fb3 --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_mul.c @@ -0,0 +1,149 @@ +/* + * reg_mul.c + * + * Multiply one FPU_REG by another, put the result in a destination FPU_REG. + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/*---------------------------------------------------------------------------+ + | The destination may be any FPU_REG, including one of the source FPU_REGs. | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "fpu_system.h" + + +/* This routine must be called with non-empty source registers */ +void +reg_mul(FPU_REG * a, FPU_REG * b, FPU_REG * dest, unsigned int control_w) +{ + char sign = (a->sign ^ b->sign); + + if (!(a->tag | b->tag)) { + /* This should be the most common case */ + reg_u_mul(a, b, dest, control_w); + dest->sign = sign; + return; + } else + if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero)) { +#ifdef DENORM_OPERAND + if (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) || + ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER))) { + if (denormal_operand()) + return; + } +#endif /* DENORM_OPERAND */ + /* Must have either both arguments == zero, or one + * valid and the other zero. The result is therefore + * zero. */ + reg_move(&CONST_Z, dest); +#ifdef PECULIAR_486 + /* The 80486 book says that the answer is +0, but a + * real 80486 appears to behave this way... */ + dest->sign = sign; +#endif /* PECULIAR_486 */ + return; + } +#if 0 /* TW_Denormal is not used yet... perhaps + * never will be. */ + else + if ((a->tag <= TW_Denormal) && (b->tag <= TW_Denormal)) { + /* One or both arguments are de-normalized */ + /* Internal de-normalized numbers are not + * supported yet */ + EXCEPTION(EX_INTERNAL | 0x105); + reg_move(&CONST_Z, dest); + } +#endif + else { + /* Must have infinities, NaNs, etc */ + if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) { + real_2op_NaN(a, b, dest); + return; + } else + if (a->tag == TW_Infinity) { + if (b->tag == TW_Zero) { + arith_invalid(dest); + return; + } + /* Zero*Infinity is invalid */ + else { +#ifdef DENORM_OPERAND + if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand()) + return; +#endif /* DENORM_OPERAND */ + reg_move(a, dest); + dest->sign = sign; + } + return; + } else + if (b->tag == TW_Infinity) { + if (a->tag == TW_Zero) { + arith_invalid(dest); + return; + } + /* Zero*Infinity is + * invalid */ + else { +#ifdef DENORM_OPERAND + if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand()) + return; +#endif /* DENORM_OPERAND */ + reg_move(b, dest); + dest->sign = sign; + } + return; + } +#ifdef PARANOID + else { + EXCEPTION(EX_INTERNAL | 0x102); + } +#endif /* PARANOID */ + } +} diff --git a/sys/gnu/i386/fpemul/reg_norm.s b/sys/gnu/i386/fpemul/reg_norm.s new file mode 100644 index 0000000..261c9b0 --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_norm.s @@ -0,0 +1,169 @@ +/* + * reg_norm.s + * + * Normalize the value in a FPU_REG. + * + * Call from C as: + * void normalize(FPU_REG *n) + * + * void normalize_nuo(FPU_REG *n) + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "fpu_asm.h" + + +.text + + .align 2,144 +.globl _normalize + +_normalize: + pushl %ebp + movl %esp,%ebp + pushl %ebx + + movl PARAM1,%ebx + + movl SIGH(%ebx),%edx + movl SIGL(%ebx),%eax + + orl %edx,%edx /* ms bits */ + js L_done /* Already normalized */ + jnz L_shift_1 /* Shift left 1 - 31 bits */ + + orl %eax,%eax + jz L_zero /* The contents are zero */ + +/* L_shift_32: */ + movl %eax,%edx + xorl %eax,%eax + subl $32,EXP(%ebx) /* This can cause an underflow */ + +/* We need to shift left by 1 - 31 bits */ +L_shift_1: + bsrl %edx,%ecx /* get the required shift in %ecx */ + subl $31,%ecx + negl %ecx + shld %cl,%eax,%edx + shl %cl,%eax + subl %ecx,EXP(%ebx) /* This can cause an underflow */ + + movl %edx,SIGH(%ebx) + movl %eax,SIGL(%ebx) + +L_done: + cmpl EXP_OVER,EXP(%ebx) + jge L_overflow + + cmpl EXP_UNDER,EXP(%ebx) + jle L_underflow + +L_exit: + popl %ebx + leave + ret + + +L_zero: + movl EXP_UNDER,EXP(%ebx) + movb TW_Zero,TAG(%ebx) + jmp L_exit + +L_underflow: + push %ebx + call _arith_underflow + pop %ebx + jmp L_exit + +L_overflow: + push %ebx + call _arith_overflow + pop %ebx + jmp L_exit + + + +/* Normalise without reporting underflow or overflow */ + .align 2,144 +.globl _normalize_nuo + +_normalize_nuo: + pushl %ebp + movl %esp,%ebp + pushl %ebx + + movl PARAM1,%ebx + + movl SIGH(%ebx),%edx + movl SIGL(%ebx),%eax + + orl %edx,%edx /* ms bits */ + js L_exit /* Already normalized */ + jnz L_nuo_shift_1 /* Shift left 1 - 31 bits */ + + orl %eax,%eax + jz L_zero /* The contents are zero */ + +/* L_nuo_shift_32: */ + movl %eax,%edx + xorl %eax,%eax + subl $32,EXP(%ebx) /* This can cause an underflow */ + +/* We need to shift left by 1 - 31 bits */ +L_nuo_shift_1: + bsrl %edx,%ecx /* get the required shift in %ecx */ + subl $31,%ecx + negl %ecx + shld %cl,%eax,%edx + shl %cl,%eax + subl %ecx,EXP(%ebx) /* This can cause an underflow */ + + movl %edx,SIGH(%ebx) + movl %eax,SIGL(%ebx) + jmp L_exit + + diff --git a/sys/gnu/i386/fpemul/reg_round.s b/sys/gnu/i386/fpemul/reg_round.s new file mode 100644 index 0000000..91a8a16 --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_round.s @@ -0,0 +1,640 @@ + .file "reg_round.S" +/* + * reg_round.S + * + * Rounding/truncation/etc for FPU basic arithmetic functions. + * + * This code has four possible entry points. + * The following must be entered by a jmp intruction: + * FPU_round, FPU_round_sqrt, and FPU_Arith_exit. + * + * The _round_reg entry point is intended to be used by C code. + * From C, call as: + * void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/*---------------------------------------------------------------------------+ + | Four entry points. | + | | + | Needed by both the FPU_round and FPU_round_sqrt entry points: | + | %eax:%ebx 64 bit significand | + | %edx 32 bit extension of the significand | + | %edi pointer to an FPU_REG for the result to be stored | + | stack calling function must have set up a C stack frame and | + | pushed %esi, %edi, and %ebx | + | | + | Needed just for the FPU_round_sqrt entry point: | + | %cx A control word in the same format as the FPU control word. | + | Otherwise, PARAM4 must give such a value. | + | | + | | + | The significand and its extension are assumed to be exact in the | + | following sense: | + | If the significand by itself is the exact result then the significand | + | extension (%edx) must contain 0, otherwise the significand extension | + | must be non-zero. | + | If the significand extension is non-zero then the significand is | + | smaller than the magnitude of the correct exact result by an amount | + | greater than zero and less than one ls bit of the significand. | + | The significand extension is only required to have three possible | + | non-zero values: | + | less than 0x80000000 <=> the significand is less than 1/2 an ls | + | bit smaller than the magnitude of the | + | true exact result. | + | exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit | + | smaller than the magnitude of the true | + | exact result. | + | greater than 0x80000000 <=> the significand is more than 1/2 an ls | + | bit smaller than the magnitude of the | + | true exact result. | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | The code in this module has become quite complex, but it should handle | + | all of the FPU flags which are set at this stage of the basic arithmetic | + | computations. | + | There are a few rare cases where the results are not set identically to | + | a real FPU. These require a bit more thought because at this stage the | + | results of the code here appear to be more consistent... | + | This may be changed in a future version. | + +---------------------------------------------------------------------------*/ + + +#include "fpu_asm.h" +#include "exception.h" +#include "control_w.h" + +#define LOST_DOWN $1 +#define LOST_UP $2 +#define DENORMAL $1 +#define UNMASKED_UNDERFLOW $2 + +.data + .align 2,0 +FPU_bits_lost: + .byte 0 +FPU_denormal: + .byte 0 + +.text + .align 2,144 +.globl FPU_round +.globl FPU_round_sqrt +.globl FPU_Arith_exit +.globl _round_reg + +/* Entry point when called from C */ +_round_reg: + pushl %ebp + movl %esp,%ebp + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%edi + movl SIGH(%edi),%eax + movl SIGL(%edi),%ebx + movl PARAM2,%edx + movl PARAM3,%ecx + jmp FPU_round_sqrt + +FPU_round: /* Normal entry point */ + movl PARAM4,%ecx + +FPU_round_sqrt: /* Entry point from wm_sqrt.S */ + +#ifdef PARANOID +/* Cannot use this here yet */ +/* orl %eax,%eax */ +/* jns L_entry_bugged */ +#endif PARANOID + + cmpl EXP_UNDER,EXP(%edi) + jle xMake_denorm /* The number is a de-normal*/ + + movb $0,FPU_denormal /* 0 -> not a de-normal*/ + +xDenorm_done: + movb $0,FPU_bits_lost /*No bits yet lost in rounding*/ + + movl %ecx,%esi + andl CW_PC,%ecx + cmpl PR_64_BITS,%ecx + je LRound_To_64 + + cmpl PR_53_BITS,%ecx + je LRound_To_53 + + cmpl PR_24_BITS,%ecx + je LRound_To_24 + +#ifdef PARANOID + jmp L_bugged /* There is no bug, just a bad control word */ +#endif PARANOID + + +/* Round etc to 24 bit precision */ +LRound_To_24: + movl %esi,%ecx + andl CW_RC,%ecx + cmpl RC_RND,%ecx + je LRound_nearest_24 + + cmpl RC_CHOP,%ecx + je LCheck_truncate_24 + + cmpl RC_UP,%ecx /* Towards +infinity */ + je LUp_24 + + cmpl RC_DOWN,%ecx /* Towards -infinity */ + je LDown_24 + +#ifdef PARANOID + jmp L_bugged +#endif PARANOID + +LUp_24: + cmpb SIGN_POS,SIGN(%edi) + jne LCheck_truncate_24 /* If negative then up==truncate */ + + jmp LCheck_24_round_up + +LDown_24: + cmpb SIGN_POS,SIGN(%edi) + je LCheck_truncate_24 /* If positive then down==truncate */ + +LCheck_24_round_up: + movl %eax,%ecx + andl $0x000000ff,%ecx + orl %ebx,%ecx + orl %edx,%ecx + jnz LDo_24_round_up + jmp LRe_normalise + +LRound_nearest_24: + /* Do rounding of the 24th bit if needed (nearest or even) */ + movl %eax,%ecx + andl $0x000000ff,%ecx + cmpl $0x00000080,%ecx + jc LCheck_truncate_24 /*less than half, no increment needed*/ + + jne LGreater_Half_24 /* greater than half, increment needed*/ + + /* Possibly half, we need to check the ls bits */ + orl %ebx,%ebx + jnz LGreater_Half_24 /* greater than half, increment needed*/ + + orl %edx,%edx + jnz LGreater_Half_24 /* greater than half, increment needed*/ + + /* Exactly half, increment only if 24th bit is 1 (round to even)*/ + testl $0x00000100,%eax + jz LDo_truncate_24 + +LGreater_Half_24: /*Rounding: increment at the 24th bit*/ +LDo_24_round_up: + andl $0xffffff00,%eax /*Truncate to 24 bits*/ + xorl %ebx,%ebx + movb LOST_UP,FPU_bits_lost + addl $0x00000100,%eax + jmp LCheck_Round_Overflow + +LCheck_truncate_24: + movl %eax,%ecx + andl $0x000000ff,%ecx + orl %ebx,%ecx + orl %edx,%ecx + jz LRe_normalise /* No truncation needed*/ + +LDo_truncate_24: + andl $0xffffff00,%eax /* Truncate to 24 bits*/ + xorl %ebx,%ebx + movb LOST_DOWN,FPU_bits_lost + jmp LRe_normalise + + +/* Round etc to 53 bit precision */ +LRound_To_53: + movl %esi,%ecx + andl CW_RC,%ecx + cmpl RC_RND,%ecx + je LRound_nearest_53 + + cmpl RC_CHOP,%ecx + je LCheck_truncate_53 + + cmpl RC_UP,%ecx /* Towards +infinity*/ + je LUp_53 + + cmpl RC_DOWN,%ecx /* Towards -infinity*/ + je LDown_53 + +#ifdef PARANOID + jmp L_bugged +#endif PARANOID + +LUp_53: + cmpb SIGN_POS,SIGN(%edi) + jne LCheck_truncate_53 /* If negative then up==truncate*/ + + jmp LCheck_53_round_up + +LDown_53: + cmpb SIGN_POS,SIGN(%edi) + je LCheck_truncate_53 /* If positive then down==truncate*/ + +LCheck_53_round_up: + movl %ebx,%ecx + andl $0x000007ff,%ecx + orl %edx,%ecx + jnz LDo_53_round_up + jmp LRe_normalise + +LRound_nearest_53: + /*Do rounding of the 53rd bit if needed (nearest or even)*/ + movl %ebx,%ecx + andl $0x000007ff,%ecx + cmpl $0x00000400,%ecx + jc LCheck_truncate_53 /* less than half, no increment needed*/ + + jnz LGreater_Half_53 /* greater than half, increment needed*/ + + /*Possibly half, we need to check the ls bits*/ + orl %edx,%edx + jnz LGreater_Half_53 /* greater than half, increment needed*/ + + /* Exactly half, increment only if 53rd bit is 1 (round to even)*/ + testl $0x00000800,%ebx + jz LTruncate_53 + +LGreater_Half_53: /*Rounding: increment at the 53rd bit*/ +LDo_53_round_up: + movb LOST_UP,FPU_bits_lost + andl $0xfffff800,%ebx /* Truncate to 53 bits*/ + addl $0x00000800,%ebx + adcl $0,%eax + jmp LCheck_Round_Overflow + +LCheck_truncate_53: + movl %ebx,%ecx + andl $0x000007ff,%ecx + orl %edx,%ecx + jz LRe_normalise + +LTruncate_53: + movb LOST_DOWN,FPU_bits_lost + andl $0xfffff800,%ebx /* Truncate to 53 bits*/ + jmp LRe_normalise + + +/* Round etc to 64 bit precision*/ +LRound_To_64: + movl %esi,%ecx + andl CW_RC,%ecx + cmpl RC_RND,%ecx + je LRound_nearest_64 + + cmpl RC_CHOP,%ecx + je LCheck_truncate_64 + + cmpl RC_UP,%ecx /* Towards +infinity*/ + je LUp_64 + + cmpl RC_DOWN,%ecx /* Towards -infinity*/ + je LDown_64 + +#ifdef PARANOID + jmp L_bugged +#endif PARANOID + +LUp_64: + cmpb SIGN_POS,SIGN(%edi) + jne LCheck_truncate_64 /* If negative then up==truncate*/ + + orl %edx,%edx + jnz LDo_64_round_up + jmp LRe_normalise + +LDown_64: + cmpb SIGN_POS,SIGN(%edi) + je LCheck_truncate_64 /*If positive then down==truncate*/ + + orl %edx,%edx + jnz LDo_64_round_up + jmp LRe_normalise + +LRound_nearest_64: + cmpl $0x80000000,%edx + jc LCheck_truncate_64 + + jne LDo_64_round_up + + /* Now test for round-to-even */ + testb $1,%ebx + jz LCheck_truncate_64 + +LDo_64_round_up: + movb LOST_UP,FPU_bits_lost + addl $1,%ebx + adcl $0,%eax + +LCheck_Round_Overflow: + jnc LRe_normalise /* Rounding done, no overflow */ + + /* Overflow, adjust the result (to 1.0) */ + rcrl $1,%eax + rcrl $1,%ebx + incl EXP(%edi) + jmp LRe_normalise + +LCheck_truncate_64: + orl %edx,%edx + jz LRe_normalise + +LTruncate_64: + movb LOST_DOWN,FPU_bits_lost + +LRe_normalise: + testb $0xff,FPU_denormal + jnz xNormalise_result + +xL_Normalised: + cmpb LOST_UP,FPU_bits_lost + je xL_precision_lost_up + + cmpb LOST_DOWN,FPU_bits_lost + je xL_precision_lost_down + +xL_no_precision_loss: + cmpl EXP_OVER,EXP(%edi) + jge L_overflow + + /* store the result */ + movb TW_Valid,TAG(%edi) + +xL_Store_significand: + movl %eax,SIGH(%edi) + movl %ebx,SIGL(%edi) + +FPU_Arith_exit: + popl %ebx + popl %edi + popl %esi + leave + ret + + +/* Set the FPU status flags to represent precision loss due to*/ +/* round-up.*/ +xL_precision_lost_up: + push %eax + call _set_precision_flag_up + popl %eax + jmp xL_no_precision_loss + +/* Set the FPU status flags to represent precision loss due to*/ +/* truncation.*/ +xL_precision_lost_down: + push %eax + call _set_precision_flag_down + popl %eax + jmp xL_no_precision_loss + + +/* The number is a denormal (which might get rounded up to a normal) +// Shift the number right the required number of bits, which will +// have to be undone later...*/ +xMake_denorm: + /* The action to be taken depends upon whether the underflow + // exception is masked*/ + testb CW_Underflow,%cl /* Underflow mask.*/ + jz xUnmasked_underflow /* Do not make a denormal.*/ + + movb DENORMAL,FPU_denormal + + pushl %ecx /* Save*/ + movl EXP(%edi),%ecx + subl EXP_UNDER+1,%ecx + negl %ecx + + cmpl $64,%ecx /* shrd only works for 0..31 bits */ + jnc xDenorm_shift_more_than_63 + + cmpl $32,%ecx /* shrd only works for 0..31 bits */ + jnc xDenorm_shift_more_than_32 + +/* We got here without jumps by assuming that the most common requirement +// is for a small de-normalising shift. +// Shift by [1..31] bits */ + addl %ecx,EXP(%edi) + orl %edx,%edx /* extension*/ + setne %ch + xorl %edx,%edx + shrd %cl,%ebx,%edx + shrd %cl,%eax,%ebx + shr %cl,%eax + orb %ch,%dl + popl %ecx + jmp xDenorm_done + +/* Shift by [32..63] bits*/ +xDenorm_shift_more_than_32: + addl %ecx,EXP(%edi) + subb $32,%cl + orl %edx,%edx + setne %ch + orb %ch,%bl + xorl %edx,%edx + shrd %cl,%ebx,%edx + shrd %cl,%eax,%ebx + shr %cl,%eax + orl %edx,%edx /*test these 32 bits*/ + setne %cl + orb %ch,%bl + orb %cl,%bl + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + popl %ecx + jmp xDenorm_done + +/* Shift by [64..) bits*/ +xDenorm_shift_more_than_63: + cmpl $64,%ecx + jne xDenorm_shift_more_than_64 + +/* Exactly 64 bit shift*/ + addl %ecx,EXP(%edi) + xorl %ecx,%ecx + orl %edx,%edx + setne %cl + orl %ebx,%ebx + setne %ch + orb %ch,%cl + orb %cl,%al + movl %eax,%edx + xorl %eax,%eax + xorl %ebx,%ebx + popl %ecx + jmp xDenorm_done + +xDenorm_shift_more_than_64: + movl EXP_UNDER+1,EXP(%edi) +/* This is easy, %eax must be non-zero, so..*/ + movl $1,%edx + xorl %eax,%eax + xorl %ebx,%ebx + popl %ecx + jmp xDenorm_done + + +xUnmasked_underflow: + /* Increase the exponent by the magic number*/ + addl $(3*(1<<13)),EXP(%edi) + movb UNMASKED_UNDERFLOW,FPU_denormal + jmp xDenorm_done + + +/* Undo the de-normalisation.*/ +xNormalise_result: + cmpb UNMASKED_UNDERFLOW,FPU_denormal + je xSignal_underflow + +/* The number must be a denormal if we got here.*/ +#ifdef PARANOID + /* But check it... just in case.*/ + cmpl EXP_UNDER+1,EXP(%edi) + jne L_norm_bugged +#endif PARANOID + + orl %eax,%eax /* ms bits*/ + jnz LNormalise_shift_up_to_31 /* Shift left 0 - 31 bits*/ + + orl %ebx,%ebx + jz L_underflow_to_zero /* The contents are zero*/ + +/* Shift left 32 - 63 bits*/ + movl %ebx,%eax + xorl %ebx,%ebx + subl $32,EXP(%edi) + +LNormalise_shift_up_to_31: + bsrl %eax,%ecx /* get the required shift in %ecx */ + subl $31,%ecx + negl %ecx + shld %cl,%ebx,%eax + shl %cl,%ebx + subl %ecx,EXP(%edi) + +LNormalise_shift_done: + testb $0xff,FPU_bits_lost /* bits lost == underflow*/ + jz xL_Normalised + + /* There must be a masked underflow*/ + push %eax + pushl EX_Underflow + call _exception + popl %eax + popl %eax + jmp xL_Normalised + + +/* The operations resulted in a number too small to represent. +// Masked response.*/ +L_underflow_to_zero: + push %eax + call _set_precision_flag_down + popl %eax + + push %eax + pushl EX_Underflow + call _exception + popl %eax + popl %eax + + movb TW_Zero,TAG(%edi) + jmp xL_Store_significand + + +/* The operations resulted in a number too large to represent.*/ +L_overflow: + push %edi + call _arith_overflow + pop %edi + jmp FPU_Arith_exit + + +xSignal_underflow: + push %eax + pushl EX_Underflow + call EXCEPTION + popl %eax + popl %eax + jmp xL_Normalised + + +#ifdef PARANOID +/* If we ever get here then we have problems! */ +L_bugged: + pushl EX_INTERNAL|0x201 + call EXCEPTION + popl %ebx + jmp FPU_Arith_exit + +L_norm_bugged: + pushl EX_INTERNAL|0x216 + call EXCEPTION + popl %ebx + jmp FPU_Arith_exit + +L_entry_bugged: + pushl EX_INTERNAL|0x217 + call EXCEPTION + popl %ebx + jmp FPU_Arith_exit +#endif PARANOID diff --git a/sys/gnu/i386/fpemul/reg_u_add.s b/sys/gnu/i386/fpemul/reg_u_add.s new file mode 100644 index 0000000..c7d9bfa --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_u_add.s @@ -0,0 +1,231 @@ + .file "reg_u_add.S" +/* + * reg_u_add.S + * + * Add two valid (TW_Valid) FPU_REG numbers, of the same sign, and put the + * result in a destination FPU_REG. + * + * Call from C as: + * void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, + * int control_w) + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/* + | Kernel addition routine reg_u_add(reg *arg1, reg *arg2, reg *answ). + | Takes two valid reg f.p. numbers (TW_Valid), which are + | treated as unsigned numbers, + | and returns their sum as a TW_Valid or TW_S f.p. number. + | The returned number is normalized. + | Basic checks are performed if PARANOID is defined. + */ + +#include "exception.h" +#include "fpu_asm.h" +#include "control_w.h" + +.text + .align 2,144 +.globl _reg_u_add +_reg_u_add: + pushl %ebp + movl %esp,%ebp +/* subl $16,%esp*/ + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi /* source 1 */ + movl PARAM2,%edi /* source 2 */ + +#ifdef DENORM_OPERAND + cmpl EXP_UNDER,EXP(%esi) + jg xOp1_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit + +xOp1_not_denorm: + cmpl EXP_UNDER,EXP(%edi) + jg xOp2_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit + +xOp2_not_denorm: +#endif DENORM_OPERAND + +/* xorl %ecx,%ecx*/ + movl EXP(%esi),%ecx + subl EXP(%edi),%ecx /* exp1 - exp2 */ +/* jnc L_arg1_larger*/ + jge L_arg1_larger + + /* num1 is smaller */ + movl SIGL(%esi),%ebx + movl SIGH(%esi),%eax + + movl %edi,%esi + negw %cx + jmp L_accum_loaded + +L_arg1_larger: + /* num1 has larger or equal exponent */ + movl SIGL(%edi),%ebx + movl SIGH(%edi),%eax + +L_accum_loaded: + movl PARAM3,%edi /* destination */ + movb SIGN(%esi),%dl + movb %dl,SIGN(%edi) /* Copy the sign from the first arg */ + + + movl EXP(%esi),%edx + movl %edx,EXP(%edi) /* Copy exponent to destination */ + + xorl %edx,%edx /* clear the extension */ + +#ifdef PARANOID + testl $0x80000000,%eax + je L_bugged + + testl $0x80000000,SIGH(%esi) + je L_bugged +#endif PARANOID + +/* The number to be shifted is in %eax:%ebx:%edx*/ + cmpw $32,%cx /* shrd only works for 0..31 bits */ + jnc L_more_than_31 + +/* less than 32 bits */ + shrd %cl,%ebx,%edx + shrd %cl,%eax,%ebx + shr %cl,%eax + jmp L_shift_done + +L_more_than_31: + cmpw $64,%cx + jnc L_more_than_63 + + subb $32,%cl + jz L_exactly_32 + + shrd %cl,%eax,%edx + shr %cl,%eax + orl %ebx,%ebx + jz L_more_31_no_low /* none of the lowest bits is set*/ + + orl $1,%edx /* record the fact in the extension*/ + +L_more_31_no_low: + movl %eax,%ebx + xorl %eax,%eax + jmp L_shift_done + +L_exactly_32: + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + jmp L_shift_done + +L_more_than_63: + cmpw $65,%cx + jnc L_more_than_64 + + movl %eax,%edx + orl %ebx,%ebx + jz L_more_63_no_low + + orl $1,%edx + jmp L_more_63_no_low + +L_more_than_64: + movl $1,%edx /* The shifted nr always at least one '1'*/ + +L_more_63_no_low: + xorl %ebx,%ebx + xorl %eax,%eax + +L_shift_done: + /* Now do the addition */ + addl SIGL(%esi),%ebx + adcl SIGH(%esi),%eax + jnc L_round_the_result + + /* Overflow, adjust the result */ + rcrl $1,%eax + rcrl $1,%ebx + rcrl $1,%edx + jnc L_no_bit_lost + + orl $1,%edx + +L_no_bit_lost: + incl EXP(%edi) + +L_round_the_result: + jmp FPU_round /* Round the result*/ + + + +#ifdef PARANOID +/* If we ever get here then we have problems! */ +L_bugged: + pushl EX_INTERNAL|0x201 + call EXCEPTION + pop %ebx + jmp L_exit +#endif PARANOID + + +L_exit: + popl %ebx + popl %edi + popl %esi + leave + ret diff --git a/sys/gnu/i386/fpemul/reg_u_div.s b/sys/gnu/i386/fpemul/reg_u_div.s new file mode 100644 index 0000000..9a87ff6 --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_u_div.s @@ -0,0 +1,493 @@ + .file "reg_u_div.S" +/* + * reg_u_div.S + * + * Core division routines + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/*---------------------------------------------------------------------------+ + | Kernel for the division routines. | + | | + | void reg_u_div(FPU_REG *a, FPU_REG *a, | + | FPU_REG *dest, unsigned int control_word) | + | | + | Does not compute the destination exponent, but does adjust it. | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "fpu_asm.h" +#include "control_w.h" + + +/* #define dSIGL(x) (x) */ +/* #define dSIGH(x) 4(x) */ + + +.data +/* + Local storage: + Result: accum_3:accum_2:accum_1:accum_0 + Overflow flag: ovfl_flag + */ + .align 2,0 +accum_3: + .long 0 +accum_2: + .long 0 +accum_1: + .long 0 +accum_0: + .long 0 +result_1: + .long 0 +result_2: + .long 0 +ovfl_flag: + .byte 0 + + +.text + .align 2,144 + +.globl _reg_u_div + +.globl _divide_kernel + +_reg_u_div: + pushl %ebp + movl %esp,%ebp + + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi /* pointer to num */ + movl PARAM2,%ebx /* pointer to denom */ + movl PARAM3,%edi /* pointer to answer */ + +#ifdef DENORM_OPERAND + movl EXP(%esi),%eax + cmpl EXP_UNDER,%eax + jg xOp1_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit + +xOp1_not_denorm: + movl EXP(%ebx),%eax + cmpl EXP_UNDER,%eax + jg xOp2_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit + +xOp2_not_denorm: +#endif DENORM_OPERAND + +_divide_kernel: +#ifdef PARANOID +/* testl $0x80000000, SIGH(%esi) *//* Dividend */ +/* je L_bugged */ + testl $0x80000000, SIGH(%ebx) /* Divisor*/ + je L_bugged +#endif PARANOID + +/* Check if the divisor can be treated as having just 32 bits */ + cmpl $0,SIGL(%ebx) + jnz L_Full_Division /* Can't do a quick divide */ + +/* We should be able to zip through the division here */ + movl SIGH(%ebx),%ecx /* The divisor */ + movl SIGH(%esi),%edx /* Dividend */ + movl SIGL(%esi),%eax /* Dividend */ + + cmpl %ecx,%edx + setaeb ovfl_flag /* Keep a record */ + jb L_no_adjust + + subl %ecx,%edx /* Prevent the overflow */ + +L_no_adjust: + /* Divide the 64 bit number by the 32 bit denominator */ + divl %ecx + movl %eax,result_2 + + /* Work on the remainder of the first division */ + xorl %eax,%eax + divl %ecx + movl %eax,result_1 + + /* Work on the remainder of the 64 bit division */ + xorl %eax,%eax + divl %ecx + + testb $255,ovfl_flag /* was the num > denom ? */ + je L_no_overflow + + /* Do the shifting here */ + /* increase the exponent */ + incl EXP(%edi) + + /* shift the mantissa right one bit */ + stc /* To set the ms bit */ + rcrl result_2 + rcrl result_1 + rcrl %eax + +L_no_overflow: + jmp LRound_precision /* Do the rounding as required*/ + + +/*---------------------------------------------------------------------------+ + | Divide: Return arg1/arg2 to arg3. | + | | + | This routine does not use the exponents of arg1 and arg2, but does | + | adjust the exponent of arg3. | + | | + | The maximum returned value is (ignoring exponents) | + | .ffffffff ffffffff | + | ------------------ = 1.ffffffff fffffffe | + | .80000000 00000000 | + | and the minimum is | + | .80000000 00000000 | + | ------------------ = .80000000 00000001 (rounded) | + | .ffffffff ffffffff | + | | + +---------------------------------------------------------------------------*/ + + +L_Full_Division: + /* Save extended dividend in local register*/ + movl SIGL(%esi),%eax + movl %eax,accum_2 + movl SIGH(%esi),%eax + movl %eax,accum_3 + xorl %eax,%eax + movl %eax,accum_1 /* zero the extension */ + movl %eax,accum_0 /* zero the extension */ + + movl SIGL(%esi),%eax /* Get the current num */ + movl SIGH(%esi),%edx + +/*----------------------------------------------------------------------*/ +/* Initialization done */ +/* Do the first 32 bits */ + + movb $0,ovfl_flag + cmpl SIGH(%ebx),%edx /* Test for imminent overflow */ + jb LLess_than_1 + ja LGreater_than_1 + + cmpl SIGL(%ebx),%eax + jb LLess_than_1 + +LGreater_than_1: +/* The dividend is greater or equal, would cause overflow */ + setaeb ovfl_flag /* Keep a record */ + + subl SIGL(%ebx),%eax + sbbl SIGH(%ebx),%edx /* Prevent the overflow */ + movl %eax,accum_2 + movl %edx,accum_3 + +LLess_than_1: +/* At this point, we have a dividend < divisor, with a record of + adjustment in ovfl_flag */ + + /* We will divide by a number which is too large */ + movl SIGH(%ebx),%ecx + addl $1,%ecx + jnc LFirst_div_not_1 + + /* here we need to divide by 100000000h, + i.e., no division at all.. */ + mov %edx,%eax + jmp LFirst_div_done + +LFirst_div_not_1: + divl %ecx /* Divide the numerator by the augmented + denom ms dw */ + +LFirst_div_done: + movl %eax,result_2 /* Put the result in the answer */ + + mull SIGH(%ebx) /* mul by the ms dw of the denom */ + + subl %eax,accum_2 /* Subtract from the num local reg */ + sbbl %edx,accum_3 + + movl result_2,%eax /* Get the result back */ + mull SIGL(%ebx) /* now mul the ls dw of the denom */ + + subl %eax,accum_1 /* Subtract from the num local reg */ + sbbl %edx,accum_2 + sbbl $0,accum_3 + je LDo_2nd_32_bits /* Must check for non-zero result here */ + +#ifdef PARANOID + jb L_bugged_1 +#endif PARANOID + + /* need to subtract another once of the denom */ + incl result_2 /* Correct the answer */ + + movl SIGL(%ebx),%eax + movl SIGH(%ebx),%edx + subl %eax,accum_1 /* Subtract from the num local reg */ + sbbl %edx,accum_2 + +#ifdef PARANOID + sbbl $0,accum_3 + jne L_bugged_1 /* Must check for non-zero result here */ +#endif PARANOID + +/*----------------------------------------------------------------------*/ +/* Half of the main problem is done, there is just a reduced numerator + to handle now */ +/* Work with the second 32 bits, accum_0 not used from now on */ +LDo_2nd_32_bits: + movl accum_2,%edx /* get the reduced num */ + movl accum_1,%eax + + /* need to check for possible subsequent overflow */ + cmpl SIGH(%ebx),%edx + jb LDo_2nd_div + ja LPrevent_2nd_overflow + + cmpl SIGL(%ebx),%eax + jb LDo_2nd_div + +LPrevent_2nd_overflow: +/* The numerator is greater or equal, would cause overflow */ + /* prevent overflow */ + subl SIGL(%ebx),%eax + sbbl SIGH(%ebx),%edx + movl %edx,accum_2 + movl %eax,accum_1 + + incl result_2 /* Reflect the subtraction in the answer */ + +#ifdef PARANOID + je L_bugged_2 /* Can't bump the result to 1.0 */ +#endif PARANOID + +LDo_2nd_div: + cmpl $0,%ecx /* augmented denom msw*/ + jnz LSecond_div_not_1 + + /* %ecx == 0, we are dividing by 1.0 */ + mov %edx,%eax + jmp LSecond_div_done + +LSecond_div_not_1: + divl %ecx /* Divide the numerator by the denom ms dw */ + +LSecond_div_done: + movl %eax,result_1 /* Put the result in the answer */ + + mull SIGH(%ebx) /* mul by the ms dw of the denom */ + + subl %eax,accum_1 /* Subtract from the num local reg */ + sbbl %edx,accum_2 + +#ifdef PARANOID + jc L_bugged_2 +#endif PARANOID + + movl result_1,%eax /* Get the result back */ + mull SIGL(%ebx) /* now mul the ls dw of the denom */ + + subl %eax,accum_0 /* Subtract from the num local reg */ + sbbl %edx,accum_1 /* Subtract from the num local reg */ + sbbl $0,accum_2 + +#ifdef PARANOID + jc L_bugged_2 +#endif PARANOID + + jz LDo_3rd_32_bits + +#ifdef PARANOID + cmpl $1,accum_2 + jne L_bugged_2 +#endif PARANOID + + /* need to subtract another once of the denom */ + movl SIGL(%ebx),%eax + movl SIGH(%ebx),%edx + subl %eax,accum_0 /* Subtract from the num local reg */ + sbbl %edx,accum_1 + sbbl $0,accum_2 + +#ifdef PARANOID + jc L_bugged_2 + jne L_bugged_2 +#endif PARANOID + + addl $1,result_1 /* Correct the answer */ + adcl $0,result_2 + +#ifdef PARANOID + jc L_bugged_2 /* Must check for non-zero result here */ +#endif PARANOID + +/*----------------------------------------------------------------------*/ +/* The division is essentially finished here, we just need to perform + tidying operations. */ +/* deal with the 3rd 32 bits */ +LDo_3rd_32_bits: + movl accum_1,%edx /* get the reduced num */ + movl accum_0,%eax + + /* need to check for possible subsequent overflow */ + cmpl SIGH(%ebx),%edx /* denom*/ + jb LRound_prep + ja LPrevent_3rd_overflow + + cmpl SIGL(%ebx),%eax /* denom */ + jb LRound_prep + +LPrevent_3rd_overflow: + /* prevent overflow */ + subl SIGL(%ebx),%eax + sbbl SIGH(%ebx),%edx + movl %edx,accum_1 + movl %eax,accum_0 + + addl $1,result_1 /* Reflect the subtraction in the answer */ + adcl $0,result_2 + jne LRound_prep + jnc LRound_prep + + /* This is a tricky spot, there is an overflow of the answer */ + movb $255,ovfl_flag /* Overflow -> 1.000 */ + +LRound_prep: +/* Prepare for rounding. +// To test for rounding, we just need to compare 2*accum with the +// denom. */ + movl accum_0,%ecx + movl accum_1,%edx + movl %ecx,%eax + orl %edx,%eax + jz LRound_ovfl /* The accumulator contains zero.*/ + + /* Multiply by 2 */ + clc + rcll $1,%ecx + rcll $1,%edx + jc LRound_large /* No need to compare, denom smaller */ + + subl SIGL(%ebx),%ecx + sbbl SIGH(%ebx),%edx + jnc LRound_not_small + + movl $0x70000000,%eax /* Denom was larger */ + jmp LRound_ovfl + +LRound_not_small: + jnz LRound_large + + movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */ + jmp LRound_ovfl + +LRound_large: + movl $0xff000000,%eax /* Denom was smaller */ + +LRound_ovfl: +/* We are now ready to deal with rounding, but first we must get + the bits properly aligned */ + testb $255,ovfl_flag /* was the num > denom ? */ + je LRound_precision + + incl EXP(%edi) + + /* shift the mantissa right one bit */ + stc /* Will set the ms bit */ + rcrl result_2 + rcrl result_1 + rcrl %eax + +/* Round the result as required */ +LRound_precision: + decl EXP(%edi) /* binary point between 1st & 2nd bits */ + + movl %eax,%edx + movl result_1,%ebx + movl result_2,%eax + jmp FPU_round + + +#ifdef PARANOID +/* The logic is wrong if we got here */ +L_bugged: + pushl EX_INTERNAL|0x202 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged_1: + pushl EX_INTERNAL|0x203 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged_2: + pushl EX_INTERNAL|0x204 + call EXCEPTION + pop %ebx + jmp L_exit + +L_exit: + popl %ebx + popl %edi + popl %esi + + leave + ret +#endif PARANOID diff --git a/sys/gnu/i386/fpemul/reg_u_mul.s b/sys/gnu/i386/fpemul/reg_u_mul.s new file mode 100644 index 0000000..8b36e7c --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_u_mul.s @@ -0,0 +1,186 @@ + .file "reg_u_mul.S" +/* + * reg_u_mul.S + * + * Core multiplication routine + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/*---------------------------------------------------------------------------+ + | Basic multiplication routine. | + | Does not check the resulting exponent for overflow/underflow | + | | + | reg_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw); | + | | + | Internal working is at approx 128 bits. | + | Result is rounded to nearest 53 or 64 bits, using "nearest or even". | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "fpu_asm.h" +#include "control_w.h" + + +.data + .align 2,0 +accum_0: + .long 0 +accum_1: + .long 0 + + +.text + .align 2,144 + +.globl _reg_u_mul +_reg_u_mul: + pushl %ebp + movl %esp,%ebp + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi + movl PARAM2,%edi + +#ifdef PARANOID + testl $0x80000000,SIGH(%esi) + jz L_bugged + testl $0x80000000,SIGH(%edi) + jz L_bugged +#endif PARANOID + +#ifdef DENORM_OPERAND + movl EXP(%esi),%eax + cmpl EXP_UNDER,%eax + jg xOp1_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit + +xOp1_not_denorm: + movl EXP(%edi),%eax + cmpl EXP_UNDER,%eax + jg xOp2_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit + +xOp2_not_denorm: +#endif DENORM_OPERAND + + xorl %ecx,%ecx + xorl %ebx,%ebx + + movl SIGL(%esi),%eax + mull SIGL(%edi) + movl %eax,accum_0 + movl %edx,accum_1 + + movl SIGL(%esi),%eax + mull SIGH(%edi) + addl %eax,accum_1 + adcl %edx,%ebx +/* adcl $0,%ecx *//* overflow here is not possible */ + + movl SIGH(%esi),%eax + mull SIGL(%edi) + addl %eax,accum_1 + adcl %edx,%ebx + adcl $0,%ecx + + movl SIGH(%esi),%eax + mull SIGH(%edi) + addl %eax,%ebx + adcl %edx,%ecx + + movl EXP(%esi),%eax /* Compute the exponent */ + addl EXP(%edi),%eax + subl EXP_BIAS-1,%eax +/* Have now finished with the sources */ + movl PARAM3,%edi /* Point to the destination */ + movl %eax,EXP(%edi) + +/* Now make sure that the result is normalized */ + testl $0x80000000,%ecx + jnz LResult_Normalised + + /* Normalize by shifting left one bit */ + shll $1,accum_0 + rcll $1,accum_1 + rcll $1,%ebx + rcll $1,%ecx + decl EXP(%edi) + +LResult_Normalised: + movl accum_0,%eax + movl accum_1,%edx + orl %eax,%eax + jz L_extent_zero + + orl $1,%edx + +L_extent_zero: + movl %ecx,%eax + jmp FPU_round + + +#ifdef PARANOID +L_bugged: + pushl EX_INTERNAL|0x205 + call EXCEPTION + pop %ebx + jmp L_exit + +L_exit: + popl %ebx + popl %edi + popl %esi + leave + ret +#endif PARANOID + diff --git a/sys/gnu/i386/fpemul/reg_u_sub.s b/sys/gnu/i386/fpemul/reg_u_sub.s new file mode 100644 index 0000000..8a7d2d9 --- /dev/null +++ b/sys/gnu/i386/fpemul/reg_u_sub.s @@ -0,0 +1,348 @@ + .file "reg_u_sub.S" +/* + * reg_u_sub.S + * + * Core floating point subtraction routine. + * + * Call from C as: + * void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, + * int control_w) + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + | Kernel subtraction routine reg_u_sub(reg *arg1, reg *arg2, reg *answ). + | Takes two valid reg f.p. numbers (TW_Valid), which are + | treated as unsigned numbers, + | and returns their difference as a TW_Valid or TW_Zero f.p. + | number. + | The first number (arg1) must be the larger. + | The returned number is normalized. + | Basic checks are performed if PARANOID is defined. + */ + +#include "exception.h" +#include "fpu_asm.h" +#include "control_w.h" + +.text + .align 2,144 +.globl _reg_u_sub +_reg_u_sub: + pushl %ebp + movl %esp,%ebp + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi /* source 1 */ + movl PARAM2,%edi /* source 2 */ + +#ifdef DENORM_OPERAND + cmpl EXP_UNDER,EXP(%esi) + jg xOp1_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit + +xOp1_not_denorm: + cmpl EXP_UNDER,EXP(%edi) + jg xOp2_not_denorm + + call _denormal_operand + orl %eax,%eax + jnz FPU_Arith_exit + +xOp2_not_denorm: +#endif DENORM_OPERAND + +/* xorl %ecx,%ecx */ + movl EXP(%esi),%ecx + subl EXP(%edi),%ecx /* exp1 - exp2 */ + +#ifdef PARANOID + /* source 2 is always smaller than source 1 */ +/* jc L_bugged */ + js L_bugged_1 + + testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */ + je L_bugged_2 + + testl $0x80000000,SIGH(%esi) + je L_bugged_2 +#endif PARANOID + +/*--------------------------------------+ + | Form a register holding the | + | smaller number | + +--------------------------------------*/ + movl SIGH(%edi),%eax /* register ms word */ + movl SIGL(%edi),%ebx /* register ls word */ + + movl PARAM3,%edi /* destination */ + movl EXP(%esi),%edx + movl %edx,EXP(%edi) /* Copy exponent to destination */ + movb SIGN(%esi),%dl + movb %dl,SIGN(%edi) /* Copy the sign from the first arg */ + + xorl %edx,%edx /* register extension */ + +/*--------------------------------------+ + | Shift the temporary register | + | right the required number of | + | places. | + +--------------------------------------*/ +L_shift_r: + cmpl $32,%ecx /* shrd only works for 0..31 bits */ + jnc L_more_than_31 + +/* less than 32 bits */ + shrd %cl,%ebx,%edx + shrd %cl,%eax,%ebx + shr %cl,%eax + jmp L_shift_done + +L_more_than_31: + cmpl $64,%ecx + jnc L_more_than_63 + + subb $32,%cl + jz L_exactly_32 + + shrd %cl,%eax,%edx + shr %cl,%eax + orl %ebx,%ebx + jz L_more_31_no_low /* none of the lowest bits is set */ + + orl $1,%edx /* record the fact in the extension */ + +L_more_31_no_low: + movl %eax,%ebx + xorl %eax,%eax + jmp L_shift_done + +L_exactly_32: + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + jmp L_shift_done + +L_more_than_63: + cmpw $65,%cx + jnc L_more_than_64 + + /* Shift right by 64 bits */ + movl %eax,%edx + orl %ebx,%ebx + jz L_more_63_no_low + + orl $1,%edx + jmp L_more_63_no_low + +L_more_than_64: + jne L_more_than_65 + + /* Shift right by 65 bits */ + /* Carry is clear if we get here */ + movl %eax,%edx + rcrl %edx + jnc L_shift_65_nc + + orl $1,%edx + jmp L_more_63_no_low + +L_shift_65_nc: + orl %ebx,%ebx + jz L_more_63_no_low + + orl $1,%edx + jmp L_more_63_no_low + +L_more_than_65: + movl $1,%edx /* The shifted nr always at least one '1' */ + +L_more_63_no_low: + xorl %ebx,%ebx + xorl %eax,%eax + +L_shift_done: +L_subtr: +/*------------------------------+ + | Do the subtraction | + +------------------------------*/ + xorl %ecx,%ecx + subl %edx,%ecx + movl %ecx,%edx + movl SIGL(%esi),%ecx + sbbl %ebx,%ecx + movl %ecx,%ebx + movl SIGH(%esi),%ecx + sbbl %eax,%ecx + movl %ecx,%eax + +#ifdef PARANOID + /* We can never get a borrow */ + jc L_bugged +#endif PARANOID + +/*--------------------------------------+ + | Normalize the result | + +--------------------------------------*/ + testl $0x80000000,%eax + jnz L_round /* no shifting needed */ + + orl %eax,%eax + jnz L_shift_1 /* shift left 1 - 31 bits */ + + orl %ebx,%ebx + jnz L_shift_32 /* shift left 32 - 63 bits */ + +/* A rare case, the only one which is non-zero if we got here +// is: 1000000 .... 0000 +// -0111111 .... 1111 1 +// -------------------- +// 0000000 .... 0000 1 */ + + cmpl $0x80000000,%edx + jnz L_must_be_zero + + /* Shift left 64 bits */ + subl $64,EXP(%edi) + movl %edx,%eax + jmp L_store + +L_must_be_zero: +#ifdef PARANOID + orl %edx,%edx + jnz L_bugged_3 +#endif PARANOID + + /* The result is zero */ + movb TW_Zero,TAG(%edi) + movl $0,EXP(%edi) /* exponent */ + movl $0,SIGL(%edi) + movl $0,SIGH(%edi) + jmp L_exit /* Does not underflow */ + +L_shift_32: + movl %ebx,%eax + movl %edx,%ebx + movl $0,%edx + subl $32,EXP(%edi) /* Can get underflow here */ + +/* We need to shift left by 1 - 31 bits */ +L_shift_1: + bsrl %eax,%ecx /* get the required shift in %ecx */ + subl $31,%ecx + negl %ecx + shld %cl,%ebx,%eax + shld %cl,%edx,%ebx + shl %cl,%edx + subl %ecx,EXP(%edi) /* Can get underflow here */ + +L_round: + jmp FPU_round /* Round the result */ + + +#ifdef PARANOID +L_bugged_1: + pushl EX_INTERNAL|0x206 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged_2: + pushl EX_INTERNAL|0x209 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged_3: + pushl EX_INTERNAL|0x210 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged_4: + pushl EX_INTERNAL|0x211 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged: + pushl EX_INTERNAL|0x212 + call EXCEPTION + pop %ebx + jmp L_exit +#endif PARANOID + + +L_store: +/*------------------------------+ + | Store the result | + +------------------------------*/ + movl %eax,SIGH(%edi) + movl %ebx,SIGL(%edi) + + movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */ + + cmpl EXP_UNDER,EXP(%edi) + jle L_underflow + +L_exit: + popl %ebx + popl %edi + popl %esi + leave + ret + + +L_underflow: + push %edi + call _arith_underflow + pop %ebx + jmp L_exit + diff --git a/sys/gnu/i386/fpemul/status_w.h b/sys/gnu/i386/fpemul/status_w.h new file mode 100644 index 0000000..4526f38 --- /dev/null +++ b/sys/gnu/i386/fpemul/status_w.h @@ -0,0 +1,93 @@ +/* + * status_w.h + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef _STATUS_H_ +#define _STATUS_H_ + + +#ifdef LOCORE +#define Const__(x) $/**/x +#else +#define Const__(x) x +#endif + +#define SW_Backward Const__(0x8000) /* backward compatibility */ +#define SW_C3 Const__(0x4000) /* condition bit 3 */ +#define SW_Top Const__(0x3800) /* top of stack */ +#define SW_Top_Shift Const__(11) /* shift for top of stack bits */ +#define SW_C2 Const__(0x0400) /* condition bit 2 */ +#define SW_C1 Const__(0x0200) /* condition bit 1 */ +#define SW_C0 Const__(0x0100) /* condition bit 0 */ +#define SW_Summary Const__(0x0080) /* exception summary */ +#define SW_Stack_Fault Const__(0x0040) /* stack fault */ +#define SW_Precision Const__(0x0020) /* loss of precision */ +#define SW_Underflow Const__(0x0010) /* underflow */ +#define SW_Overflow Const__(0x0008) /* overflow */ +#define SW_Zero_Div Const__(0x0004) /* divide by zero */ +#define SW_Denorm_Op Const__(0x0002) /* denormalized operand */ +#define SW_Invalid Const__(0x0001) /* invalid operation */ + +#define SW_Exc_Mask Const__(0x27f) /* Status word exception bit mask */ + +#ifndef LOCORE + +#define COMP_A_gt_B 1 +#define COMP_A_eq_B 2 +#define COMP_A_lt_B 3 +#define COMP_No_Comp 4 +#define COMP_Denormal 0x20 +#define COMP_NaN 0x40 +#define COMP_SNaN 0x80 + +#define setcc(cc) ({ \ + status_word &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \ + status_word |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); }) + +#endif /* LOCORE */ + +#endif /* _STATUS_H_ */ diff --git a/sys/gnu/i386/fpemul/version.h b/sys/gnu/i386/fpemul/version.h new file mode 100644 index 0000000..201de20 --- /dev/null +++ b/sys/gnu/i386/fpemul/version.h @@ -0,0 +1,48 @@ +/* + * version.h + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#define FPU_VERSION "wm-FPU-emu version BETA 1.4" diff --git a/sys/gnu/i386/fpemul/wm_shrx.s b/sys/gnu/i386/fpemul/wm_shrx.s new file mode 100644 index 0000000..08f6223 --- /dev/null +++ b/sys/gnu/i386/fpemul/wm_shrx.s @@ -0,0 +1,248 @@ + .file "wm_shrx.S" +/* + * wm_shrx.S + * + * 64 bit right shift functions + * + * Call from C as: + * unsigned shrx(void *arg1, unsigned arg2) + * and + * unsigned shrxs(void *arg1, unsigned arg2) + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "fpu_asm.h" + +.text + .align 2,144 + +/*---------------------------------------------------------------------------+ + | unsigned shrx(void *arg1, unsigned arg2) | + | | + | Extended shift right function. | + | Fastest for small shifts. | + | Shifts the 64 bit quantity pointed to by the first arg (arg1) | + | right by the number of bits specified by the second arg (arg2). | + | Forms a 96 bit quantity from the 64 bit arg and eax: | + | [ 64 bit arg ][ eax ] | + | shift right ---------> | + | The eax register is initialized to 0 before the shifting. | + | Results returned in the 64 bit arg and eax. | + +---------------------------------------------------------------------------*/ + + .globl _shrx + +_shrx: + push %ebp + movl %esp,%ebp + pushl %esi + movl PARAM2,%ecx + movl PARAM1,%esi + cmpl $32,%ecx /* shrd only works for 0..31 bits */ + jnc L_more_than_31 + +/* less than 32 bits */ + pushl %ebx + movl (%esi),%ebx /* lsl */ + movl 4(%esi),%edx /* msl */ + xorl %eax,%eax /* extension */ + shrd %cl,%ebx,%eax + shrd %cl,%edx,%ebx + shr %cl,%edx + movl %ebx,(%esi) + movl %edx,4(%esi) + popl %ebx + popl %esi + leave + ret + +L_more_than_31: + cmpl $64,%ecx + jnc L_more_than_63 + + subb $32,%cl + movl (%esi),%eax /* lsl */ + movl 4(%esi),%edx /* msl */ + shrd %cl,%edx,%eax + shr %cl,%edx + movl %edx,(%esi) + movl $0,4(%esi) + popl %esi + leave + ret + +L_more_than_63: + cmpl $96,%ecx + jnc L_more_than_95 + + subb $64,%cl + movl 4(%esi),%eax /* msl */ + shr %cl,%eax + xorl %edx,%edx + movl %edx,(%esi) + movl %edx,4(%esi) + popl %esi + leave + ret + +L_more_than_95: + xorl %eax,%eax + movl %eax,(%esi) + movl %eax,4(%esi) + popl %esi + leave + ret + + +/*---------------------------------------------------------------------------+ + | unsigned shrxs(void *arg1, unsigned arg2) | + | | + | Extended shift right function (optimized for small floating point | + | integers). | + | Shifts the 64 bit quantity pointed to by the first arg (arg1) | + | right by the number of bits specified by the second arg (arg2). | + | Forms a 96 bit quantity from the 64 bit arg and eax: | + | [ 64 bit arg ][ eax ] | + | shift right ---------> | + | The eax register is initialized to 0 before the shifting. | + | The lower 8 bits of eax are lost and replaced by a flag which is | + | set (to 0x01) if any bit, apart from the first one, is set in the | + | part which has been shifted out of the arg. | + | Results returned in the 64 bit arg and eax. | + +---------------------------------------------------------------------------*/ + .globl _shrxs +_shrxs: + push %ebp + movl %esp,%ebp + pushl %esi + pushl %ebx + movl PARAM2,%ecx + movl PARAM1,%esi + cmpl $64,%ecx /* shrd only works for 0..31 bits */ + jnc Ls_more_than_63 + + cmpl $32,%ecx /* shrd only works for 0..31 bits */ + jc Ls_less_than_32 + +/* We got here without jumps by assuming that the most common requirement + is for small integers */ +/* Shift by [32..63] bits */ + subb $32,%cl + movl (%esi),%eax /* lsl */ + movl 4(%esi),%edx /* msl */ + xorl %ebx,%ebx + shrd %cl,%eax,%ebx + shrd %cl,%edx,%eax + shr %cl,%edx + orl %ebx,%ebx /* test these 32 bits */ + setne %bl + test $0x7fffffff,%eax /* and 31 bits here */ + setne %bh + orw %bx,%bx /* Any of the 63 bit set ? */ + setne %al + movl %edx,(%esi) + movl $0,4(%esi) + popl %ebx + popl %esi + leave + ret + +/* Shift by [0..31] bits */ +Ls_less_than_32: + movl (%esi),%ebx /* lsl */ + movl 4(%esi),%edx /* msl */ + xorl %eax,%eax /* extension */ + shrd %cl,%ebx,%eax + shrd %cl,%edx,%ebx + shr %cl,%edx + test $0x7fffffff,%eax /* only need to look at eax here */ + setne %al + movl %ebx,(%esi) + movl %edx,4(%esi) + popl %ebx + popl %esi + leave + ret + +/* Shift by [64..95] bits */ +Ls_more_than_63: + cmpl $96,%ecx + jnc Ls_more_than_95 + + subb $64,%cl + movl (%esi),%ebx /* lsl */ + movl 4(%esi),%eax /* msl */ + xorl %edx,%edx /* extension */ + shrd %cl,%ebx,%edx + shrd %cl,%eax,%ebx + shr %cl,%eax + orl %ebx,%edx + setne %bl + test $0x7fffffff,%eax /* only need to look at eax here */ + setne %bh + orw %bx,%bx + setne %al + xorl %edx,%edx + movl %edx,(%esi) /* set to zero */ + movl %edx,4(%esi) /* set to zero */ + popl %ebx + popl %esi + leave + ret + +Ls_more_than_95: +/* Shift by [96..inf) bits */ + xorl %eax,%eax + movl (%esi),%ebx + orl 4(%esi),%ebx + setne %al + xorl %ebx,%ebx + movl %ebx,(%esi) + movl %ebx,4(%esi) + popl %ebx + popl %esi + leave + ret diff --git a/sys/gnu/i386/fpemul/wm_sqrt.s b/sys/gnu/i386/fpemul/wm_sqrt.s new file mode 100644 index 0000000..a28a92f --- /dev/null +++ b/sys/gnu/i386/fpemul/wm_sqrt.s @@ -0,0 +1,483 @@ + .file "wm_sqrt.S" +/* + * wm_sqrt.S + * + * Fixed point arithmetic square root evaluation. + * + * Call from C as: + * void wm_sqrt(FPU_REG *n, unsigned int control_word) + * + * + * Copyright (C) 1992, 1993 W. Metzenthen, 22 Parker St, Ormond, + * Vic 3163, Australia. + * E-mail apm233m@vaxc.cc.monash.edu.au + * All rights reserved. + * + * This copyright notice covers the redistribution and use of the + * FPU emulator developed by W. Metzenthen. It covers only its use + * in the 386BSD operating system. Any other use is not permitted + * under this copyright. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must include information specifying + * that source code for the emulator is freely available and include + * either: + * a) an offer to provide the source code for a nominal distribution + * fee, or + * b) list at least two alternative methods whereby the source + * can be obtained, e.g. a publically accessible bulletin board + * and an anonymous ftp site from which the software can be + * downloaded. + * 3. All advertising materials specifically mentioning features or use of + * this emulator must acknowledge that it was developed by W. Metzenthen. + * 4. The name of W. Metzenthen may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +/*---------------------------------------------------------------------------+ + | wm_sqrt(FPU_REG *n, unsigned int control_word) | + | returns the square root of n in n. | + | | + | Use Newton's method to compute the square root of a number, which must | + | be in the range [1.0 .. 4.0), to 64 bits accuracy. | + | Does not check the sign or tag of the argument. | + | Sets the exponent, but not the sign or tag of the result. | + | | + | The guess is kept in %esi:%edi | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "fpu_asm.h" + + +.data +/* + Local storage: + */ + .align 4,0 +accum_3: + .long 0 /* ms word */ +accum_2: + .long 0 +accum_1: + .long 0 +accum_0: + .long 0 + +/* The de-normalised argument: +// sq_2 sq_1 sq_0 +// b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0 +// ^ binary point here */ +fsqrt_arg_2: + .long 0 /* ms word */ +fsqrt_arg_1: + .long 0 +fsqrt_arg_0: + .long 0 /* ls word, at most the ms bit is set */ + +.text + .align 2,144 + +.globl _wm_sqrt + +_wm_sqrt: + pushl %ebp + movl %esp,%ebp + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi + + movl SIGH(%esi),%eax + movl SIGL(%esi),%ecx + xorl %edx,%edx + +/* We use a rough linear estimate for the first guess.. */ + + cmpl EXP_BIAS,EXP(%esi) + jnz sqrt_arg_ge_2 + + shrl $1,%eax /* arg is in the range [1.0 .. 2.0) */ + rcrl $1,%ecx + rcrl $1,%edx + +sqrt_arg_ge_2: +/* From here on, n is never accessed directly again until it is +// replaced by the answer. */ + + movl %eax,fsqrt_arg_2 /* ms word of n */ + movl %ecx,fsqrt_arg_1 + movl %edx,fsqrt_arg_0 + +/* Make a linear first estimate */ + shrl $1,%eax + addl $0x40000000,%eax + movl $0xaaaaaaaa,%ecx + mull %ecx + shll %edx /* max result was 7fff... */ + testl $0x80000000,%edx /* but min was 3fff... */ + jnz sqrt_prelim_no_adjust + + movl $0x80000000,%edx /* round up */ + +sqrt_prelim_no_adjust: + movl %edx,%esi /* Our first guess */ + +/* We have now computed (approx) (2 + x) / 3, which forms the basis + for a few iterations of Newton's method */ + + movl fsqrt_arg_2,%ecx /* ms word */ + +/* From our initial estimate, three iterations are enough to get us +// to 30 bits or so. This will then allow two iterations at better +// precision to complete the process. + +// Compute (g + n/g)/2 at each iteration (g is the guess). */ + shrl %ecx /* Doing this first will prevent a divide */ + /* overflow later. */ + + movl %ecx,%edx /* msw of the arg / 2 */ + divl %esi /* current estimate */ + shrl %esi /* divide by 2 */ + addl %eax,%esi /* the new estimate */ + + movl %ecx,%edx + divl %esi + shrl %esi + addl %eax,%esi + + movl %ecx,%edx + divl %esi + shrl %esi + addl %eax,%esi + +/* Now that an estimate accurate to about 30 bits has been obtained (in %esi), +// we improve it to 60 bits or so. + +// The strategy from now on is to compute new estimates from +// guess := guess + (n - guess^2) / (2 * guess) */ + +/* First, find the square of the guess */ + movl %esi,%eax + mull %esi +/* guess^2 now in %edx:%eax */ + + movl fsqrt_arg_1,%ecx + subl %ecx,%eax + movl fsqrt_arg_2,%ecx /* ms word of normalized n */ + sbbl %ecx,%edx + jnc sqrt_stage_2_positive +/* subtraction gives a negative result +// negate the result before division */ + notl %edx + notl %eax + addl $1,%eax + adcl $0,%edx + + divl %esi + movl %eax,%ecx + + movl %edx,%eax + divl %esi + jmp sqrt_stage_2_finish + +sqrt_stage_2_positive: + divl %esi + movl %eax,%ecx + + movl %edx,%eax + divl %esi + + notl %ecx + notl %eax + addl $1,%eax + adcl $0,%ecx + +sqrt_stage_2_finish: + sarl $1,%ecx /* divide by 2 */ + rcrl $1,%eax + + /* Form the new estimate in %esi:%edi */ + movl %eax,%edi + addl %ecx,%esi + + jnz sqrt_stage_2_done /* result should be [1..2) */ + +#ifdef PARANOID +/* It should be possible to get here only if the arg is ffff....ffff*/ + cmp $0xffffffff,fsqrt_arg_1 + jnz sqrt_stage_2_error +#endif PARANOID + +/* The best rounded result.*/ + xorl %eax,%eax + decl %eax + movl %eax,%edi + movl %eax,%esi + movl $0x7fffffff,%eax + jmp sqrt_round_result + +#ifdef PARANOID +sqrt_stage_2_error: + pushl EX_INTERNAL|0x213 + call EXCEPTION +#endif PARANOID + +sqrt_stage_2_done: + +/* Now the square root has been computed to better than 60 bits */ + +/* Find the square of the guess*/ + movl %edi,%eax /* ls word of guess*/ + mull %edi + movl %edx,accum_1 + + movl %esi,%eax + mull %esi + movl %edx,accum_3 + movl %eax,accum_2 + + movl %edi,%eax + mull %esi + addl %eax,accum_1 + adcl %edx,accum_2 + adcl $0,accum_3 + +/* movl %esi,%eax*/ +/* mull %edi*/ + addl %eax,accum_1 + adcl %edx,accum_2 + adcl $0,accum_3 + +/* guess^2 now in accum_3:accum_2:accum_1*/ + + movl fsqrt_arg_0,%eax /* get normalized n*/ + subl %eax,accum_1 + movl fsqrt_arg_1,%eax + sbbl %eax,accum_2 + movl fsqrt_arg_2,%eax /* ms word of normalized n*/ + sbbl %eax,accum_3 + jnc sqrt_stage_3_positive + +/* subtraction gives a negative result*/ +/* negate the result before division */ + notl accum_1 + notl accum_2 + notl accum_3 + addl $1,accum_1 + adcl $0,accum_2 + +#ifdef PARANOID + adcl $0,accum_3 /* This must be zero */ + jz sqrt_stage_3_no_error + +sqrt_stage_3_error: + pushl EX_INTERNAL|0x207 + call EXCEPTION + +sqrt_stage_3_no_error: +#endif PARANOID + + movl accum_2,%edx + movl accum_1,%eax + divl %esi + movl %eax,%ecx + + movl %edx,%eax + divl %esi + + sarl $1,%ecx / divide by 2*/ + rcrl $1,%eax + + /* prepare to round the result*/ + + addl %ecx,%edi + adcl $0,%esi + + jmp sqrt_stage_3_finished + +sqrt_stage_3_positive: + movl accum_2,%edx + movl accum_1,%eax + divl %esi + movl %eax,%ecx + + movl %edx,%eax + divl %esi + + sarl $1,%ecx /* divide by 2*/ + rcrl $1,%eax + + /* prepare to round the result*/ + + notl %eax /* Negate the correction term*/ + notl %ecx + addl $1,%eax + adcl $0,%ecx /* carry here ==> correction == 0*/ + adcl $0xffffffff,%esi + + addl %ecx,%edi + adcl $0,%esi + +sqrt_stage_3_finished: + +/* The result in %esi:%edi:%esi should be good to about 90 bits here, +// and the rounding information here does not have sufficient accuracy +// in a few rare cases. */ + cmpl $0xffffffe0,%eax + ja sqrt_near_exact_x + + cmpl $0x00000020,%eax + jb sqrt_near_exact + + cmpl $0x7fffffe0,%eax + jb sqrt_round_result + + cmpl $0x80000020,%eax + jb sqrt_get_more_precision + +sqrt_round_result: +/* Set up for rounding operations*/ + movl %eax,%edx + movl %esi,%eax + movl %edi,%ebx + movl PARAM1,%edi + movl EXP_BIAS,EXP(%edi) /* Result is in [1.0 .. 2.0)*/ + movl PARAM2,%ecx + jmp FPU_round_sqrt + + +sqrt_near_exact_x: +/* First, the estimate must be rounded up.*/ + addl $1,%edi + adcl $0,%esi + +sqrt_near_exact: +/* This is an easy case because x^1/2 is monotonic. +// We need just find the square of our estimate, compare it +// with the argument, and deduce whether our estimate is +// above, below, or exact. We use the fact that the estimate +// is known to be accurate to about 90 bits. */ + movl %edi,%eax /* ls word of guess*/ + mull %edi + movl %edx,%ebx /* 2nd ls word of square*/ + movl %eax,%ecx /* ls word of square*/ + + movl %edi,%eax + mull %esi + addl %eax,%ebx + addl %eax,%ebx + +#ifdef PARANOID + cmp $0xffffffb0,%ebx + jb sqrt_near_exact_ok + + cmp $0x00000050,%ebx + ja sqrt_near_exact_ok + + pushl EX_INTERNAL|0x214 + call EXCEPTION + +sqrt_near_exact_ok: +#endif PARANOID + + or %ebx,%ebx + js sqrt_near_exact_small + + jnz sqrt_near_exact_large + + or %ebx,%edx + jnz sqrt_near_exact_large + +/* Our estimate is exactly the right answer*/ + xorl %eax,%eax + jmp sqrt_round_result + +sqrt_near_exact_small: +/* Our estimate is too small*/ + movl $0x000000ff,%eax + jmp sqrt_round_result + +sqrt_near_exact_large: +/* Our estimate is too large, we need to decrement it*/ + subl $1,%edi + sbbl $0,%esi + movl $0xffffff00,%eax + jmp sqrt_round_result + + +sqrt_get_more_precision: +/* This case is almost the same as the above, except we start*/ +/* with an extra bit of precision in the estimate.*/ + stc /* The extra bit.*/ + rcll $1,%edi /* Shift the estimate left one bit*/ + rcll $1,%esi + + movl %edi,%eax /* ls word of guess*/ + mull %edi + movl %edx,%ebx /* 2nd ls word of square*/ + movl %eax,%ecx /* ls word of square*/ + + movl %edi,%eax + mull %esi + addl %eax,%ebx + addl %eax,%ebx + +/* Put our estimate back to its original value*/ + stc /* The ms bit.*/ + rcrl $1,%esi /* Shift the estimate left one bit*/ + rcrl $1,%edi + +#ifdef PARANOID + cmp $0xffffff60,%ebx + jb sqrt_more_prec_ok + + cmp $0x000000a0,%ebx + ja sqrt_more_prec_ok + + pushl EX_INTERNAL|0x215 + call EXCEPTION + +sqrt_more_prec_ok: +#endif PARANOID + + or %ebx,%ebx + js sqrt_more_prec_small + + jnz sqrt_more_prec_large + + or %ebx,%ecx + jnz sqrt_more_prec_large + +/* Our estimate is exactly the right answer*/ + movl $0x80000000,%eax + jmp sqrt_round_result + +sqrt_more_prec_small: +/* Our estimate is too small*/ + movl $0x800000ff,%eax + jmp sqrt_round_result + +sqrt_more_prec_large: +/* Our estimate is too large*/ + movl $0x7fffff00,%eax + jmp sqrt_round_result |