diff -u --recursive --new-file v1.1.76/linux/Makefile linux/Makefile --- v1.1.76/linux/Makefile Sun Jan 1 19:55:21 1995 +++ linux/Makefile Wed Jan 4 21:17:40 1995 @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 1 -SUBLEVEL = 76 +SUBLEVEL = 77 ARCH = i386 @@ -83,16 +83,13 @@ # Include the make variables (CC, etc...) # -include arch/$(ARCH)/Makefile - ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o net/net.o ipc/ipc.o FILESYSTEMS =fs/filesystems.a DRIVERS =drivers/block/block.a \ drivers/char/char.a \ - drivers/net/net.a \ - ibcs/ibcs.o + drivers/net/net.a LIBS =lib/lib.a -SUBDIRS =kernel drivers mm fs net ipc ibcs lib +SUBDIRS =kernel drivers mm fs net ipc lib ifdef CONFIG_SCSI DRIVERS := $(DRIVERS) drivers/scsi/scsi.a @@ -102,9 +99,7 @@ DRIVERS := $(DRIVERS) drivers/sound/sound.a endif -ifdef CONFIG_MATH_EMULATION -DRIVERS := $(DRIVERS) drivers/FPU-emu/math.a -endif +include arch/$(ARCH)/Makefile .c.s: $(CC) $(CFLAGS) -S -o $*.s $< @@ -112,6 +107,10 @@ $(AS) -o $*.o $< .c.o: $(CC) $(CFLAGS) -c -o $*.o $< +.S.s: + $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $< +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< Version: dummy rm -f include/linux/version.h @@ -119,8 +118,8 @@ boot: vmlinux @$(MAKE) -C arch/$(ARCH)/boot -vmlinux: $(CONFIGURATION) kernel/head.o init/main.o init/version.o linuxsubdirs - $(LD) $(LINKFLAGS) kernel/head.o init/main.o init/version.o \ +vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs + $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ $(ARCHIVES) \ $(FILESYSTEMS) \ $(DRIVERS) \ @@ -130,9 +129,6 @@ symlinks: rm -f include/asm ( cd include ; ln -sf asm-$(ARCH) asm) - ln -sf ../arch/$(ARCH)/traps.c kernel/traps.c - ln -sf ../arch/$(ARCH)/entry.S kernel/entry.S - ln -sf ../arch/$(ARCH)/head.S kernel/head.S oldconfig: symlinks $(CONFIG_SHELL) Configure -d arch/$(ARCH)/config.in @@ -169,11 +165,6 @@ fi >> include/linux/version.h @echo \#define LINUX_COMPILER \"`$(HOSTCC) -v 2>&1 | tail -1`\" >> include/linux/version.h -kernel/head.o: kernel/head.s - -kernel/head.s: kernel/head.S include/linux/tasks.h - $(CPP) -traditional -o $*.s $< - init/version.o: init/version.c include/linux/version.h $(CC) $(CFLAGS) -DUTS_MACHINE='"$(ARCH)"' -c -o init/version.o init/version.c @@ -201,6 +192,10 @@ net: dummy $(MAKE) linuxsubdirs SUBDIRS=net +modules: dummy + @set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i modules; done + + clean: archclean rm -f kernel/ksyms.lst rm -f core `find . -name '*.[oas]' -print` @@ -212,7 +207,7 @@ rm -f include/linux/autoconf.h include/linux/version.h rm -f drivers/sound/local.h rm -f .version .config* config.in config.old - rm -f include/asm kernel/entry.S kernel/head.S kernel/traps.c + rm -f include/asm rm -f .depend `find . -name .depend -print` distclean: mrproper diff -u --recursive --new-file v1.1.76/linux/arch/alpha/Makefile linux/arch/alpha/Makefile --- v1.1.76/linux/arch/alpha/Makefile Thu Dec 29 19:58:40 1994 +++ linux/arch/alpha/Makefile Wed Jan 4 21:16:04 1995 @@ -11,6 +11,11 @@ LINKFLAGS = -non_shared -T 0xfffffc0000304000 -N CFLAGS := $(CFLAGS) -mno-fp-regs +HEAD := arch/alpha/kernel/head.o + +SUBDIRS := $(SUBDIRS) arch/alpha/kernel +ARCHIVES := arch/alpha/kernel/kernel.o $(ARCHIVES) + archclean: archdep: diff -u --recursive --new-file v1.1.76/linux/arch/alpha/entry.S linux/arch/alpha/entry.S --- v1.1.76/linux/arch/alpha/entry.S Thu Dec 29 19:58:40 1994 +++ linux/arch/alpha/entry.S Thu Jan 1 02:00:00 1970 @@ -1,61 +0,0 @@ -/* - * alpha/entry.S - * - * kernel entry-points - */ - -#define __ASSEMBLY__ -#include - -#define halt .long PAL_halt -#define rti .long RAL_rti - -.text - .set noat - .align 6 - .ent entInt -entInt: - subq $30,144,$30 - stq $0,0($30) - stq $1,8($30) - stq $2,16($30) - stq $3,24($30) - stq $4,32($30) - stq $5,40($30) - stq $6,48($30) - stq $7,56($30) - stq $8,64($30) - stq $19,64($30) - stq $20,72($30) - stq $21,80($30) - stq $22,88($30) - stq $23,96($30) - stq $24,104($30) - stq $25,112($30) - stq $26,120($30) - stq $27,128($30) - stq $28,136($30) - lda $27,do_hw_interrupt - jsr $26,($27),do_hw_interrupt - ldq $0,0($30) - ldq $1,8($30) - ldq $2,16($30) - ldq $3,24($30) - ldq $4,32($30) - ldq $5,40($30) - ldq $6,48($30) - ldq $7,56($30) - ldq $8,64($30) - ldq $19,64($30) - ldq $20,72($30) - ldq $21,80($30) - ldq $22,88($30) - ldq $23,96($30) - ldq $24,104($30) - ldq $25,112($30) - ldq $26,120($30) - ldq $27,128($30) - ldq $28,136($30) - addq $30,144,$30 - rti - .end entInt diff -u --recursive --new-file v1.1.76/linux/arch/alpha/head.S linux/arch/alpha/head.S --- v1.1.76/linux/arch/alpha/head.S Thu Dec 29 19:58:40 1994 +++ linux/arch/alpha/head.S Thu Jan 1 02:00:00 1970 @@ -1,112 +0,0 @@ -/* - * alpha/boot/head.S - * - * initial boot stuff.. - */ - -#define __ASSEMBLY__ -#include - -#define halt .long PAL_halt - -/* - * NOTE! The console bootstrap will load us at 0x20000000, but this image - * is linked to run at START_ADDR, so the first thing we do is to move - * ourself up to the right address.. We'd better be position-independent - * at that stage :-) - */ - .set noreorder - .globl __start - .ent __start -__start: - bis $31,$31,$31 - br $1,$200 - .long START_ADDR, START_ADDR >> 32 /* strange bug in the assembler.. duh */ - .long START_SIZE, START_SIZE >> 32 -$200: ldq $30,0($1) /* new stack - below this */ - lda $2,-8($1) /* __start */ - bis $30,$30,$3 /* new address */ - subq $3,$2,$6 /* difference */ - ldq $4,8($1) /* size */ -$201: subq $4,8,$4 - ldq $5,0($2) - addq $2,8,$2 - stq $5,0($3) - addq $3,8,$3 - bne $4,$201 - br $1,$202 -$202: addq $1,$6,$1 - addq $1,12,$1 /* $203 in the new address space */ - jmp $31,($1),$203 -$203: br $27,$100 -$100: ldgp $29,0($27) - lda $27,start_kernel - jsr $26,($27),start_kernel - halt - .end __start - - .align 5 - .globl wrent - .ent wrent -wrent: - .long PAL_wrent - ret ($26) - .end wrent - - .align 5 - .globl wrkgp - .ent wrkgp -wrkgp: - .long PAL_wrkgp - ret ($26) - .end wrkgp - - .align 5 - .globl switch_to_osf_pal - .ent switch_to_osf_pal -switch_to_osf_pal: - subq $30,128,$30 - stq $26,0($30) - stq $1,8($30) - stq $2,16($30) - stq $3,24($30) - stq $4,32($30) - stq $5,40($30) - stq $6,48($30) - stq $7,56($30) - stq $8,64($30) - stq $9,72($30) - stq $10,80($30) - stq $11,88($30) - stq $12,96($30) - stq $13,104($30) - stq $14,112($30) - stq $15,120($30) - - stq $30,0($17) /* save KSP in PCB */ - - bis $30,$30,$20 /* a4 = KSP */ - br $17,__do_swppal - - ldq $26,0($30) - ldq $1,8($30) - ldq $2,16($30) - ldq $3,24($30) - ldq $4,32($30) - ldq $5,40($30) - ldq $6,48($30) - ldq $7,56($30) - ldq $8,64($30) - ldq $9,72($30) - ldq $10,80($30) - ldq $11,88($30) - ldq $12,96($30) - ldq $13,104($30) - ldq $14,112($30) - ldq $15,120($30) - addq $30,128,$30 - ret ($26) - -__do_swppal: - .long PAL_swppal - .end switch_to_osf_pal diff -u --recursive --new-file v1.1.76/linux/arch/alpha/kernel/Makefile linux/arch/alpha/kernel/Makefile --- v1.1.76/linux/arch/alpha/kernel/Makefile Thu Jan 1 02:00:00 1970 +++ linux/arch/alpha/kernel/Makefile Wed Jan 4 21:16:04 1995 @@ -0,0 +1,46 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o + +OBJS = entry.o traps.o + +all: kernel.o head.o + +head.o: head.s + +head.s: head.S $(TOPDIR)/include/asm-alpha/system.h + $(CPP) -traditional -o $*.s $< + +kernel.o: $(OBJS) + $(LD) -r -o kernel.o $(OBJS) + sync + +dep: + $(CPP) -M *.c > .depend + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + + diff -u --recursive --new-file v1.1.76/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S --- v1.1.76/linux/arch/alpha/kernel/entry.S Thu Jan 1 02:00:00 1970 +++ linux/arch/alpha/kernel/entry.S Wed Jan 4 21:16:04 1995 @@ -0,0 +1,60 @@ +/* + * alpha/entry.S + * + * kernel entry-points + */ + +#include + +#define halt .long PAL_halt +#define rti .long RAL_rti + +.text + .set noat + .align 6 + .ent entInt +entInt: + subq $30,144,$30 + stq $0,0($30) + stq $1,8($30) + stq $2,16($30) + stq $3,24($30) + stq $4,32($30) + stq $5,40($30) + stq $6,48($30) + stq $7,56($30) + stq $8,64($30) + stq $19,64($30) + stq $20,72($30) + stq $21,80($30) + stq $22,88($30) + stq $23,96($30) + stq $24,104($30) + stq $25,112($30) + stq $26,120($30) + stq $27,128($30) + stq $28,136($30) + lda $27,do_hw_interrupt + jsr $26,($27),do_hw_interrupt + ldq $0,0($30) + ldq $1,8($30) + ldq $2,16($30) + ldq $3,24($30) + ldq $4,32($30) + ldq $5,40($30) + ldq $6,48($30) + ldq $7,56($30) + ldq $8,64($30) + ldq $19,64($30) + ldq $20,72($30) + ldq $21,80($30) + ldq $22,88($30) + ldq $23,96($30) + ldq $24,104($30) + ldq $25,112($30) + ldq $26,120($30) + ldq $27,128($30) + ldq $28,136($30) + addq $30,144,$30 + rti + .end entInt diff -u --recursive --new-file v1.1.76/linux/arch/alpha/kernel/head.S linux/arch/alpha/kernel/head.S --- v1.1.76/linux/arch/alpha/kernel/head.S Thu Jan 1 02:00:00 1970 +++ linux/arch/alpha/kernel/head.S Wed Jan 4 21:16:04 1995 @@ -0,0 +1,112 @@ +/* + * alpha/boot/head.S + * + * initial boot stuff.. + */ + +#define __ASSEMBLY__ +#include + +#define halt .long PAL_halt + +/* + * NOTE! The console bootstrap will load us at 0x20000000, but this image + * is linked to run at START_ADDR, so the first thing we do is to move + * ourself up to the right address.. We'd better be position-independent + * at that stage :-) + */ + .set noreorder + .globl __start + .ent __start +__start: + bis $31,$31,$31 + br $1,$200 + .long START_ADDR, START_ADDR >> 32 /* strange bug in the assembler.. duh */ + .long START_SIZE, START_SIZE >> 32 +$200: ldq $30,0($1) /* new stack - below this */ + lda $2,-8($1) /* __start */ + bis $30,$30,$3 /* new address */ + subq $3,$2,$6 /* difference */ + ldq $4,8($1) /* size */ +$201: subq $4,8,$4 + ldq $5,0($2) + addq $2,8,$2 + stq $5,0($3) + addq $3,8,$3 + bne $4,$201 + br $1,$202 +$202: addq $1,$6,$1 + addq $1,12,$1 /* $203 in the new address space */ + jmp $31,($1),$203 +$203: br $27,$100 +$100: ldgp $29,0($27) + lda $27,start_kernel + jsr $26,($27),start_kernel + halt + .end __start + + .align 5 + .globl wrent + .ent wrent +wrent: + .long PAL_wrent + ret ($26) + .end wrent + + .align 5 + .globl wrkgp + .ent wrkgp +wrkgp: + .long PAL_wrkgp + ret ($26) + .end wrkgp + + .align 5 + .globl switch_to_osf_pal + .ent switch_to_osf_pal +switch_to_osf_pal: + subq $30,128,$30 + stq $26,0($30) + stq $1,8($30) + stq $2,16($30) + stq $3,24($30) + stq $4,32($30) + stq $5,40($30) + stq $6,48($30) + stq $7,56($30) + stq $8,64($30) + stq $9,72($30) + stq $10,80($30) + stq $11,88($30) + stq $12,96($30) + stq $13,104($30) + stq $14,112($30) + stq $15,120($30) + + stq $30,0($17) /* save KSP in PCB */ + + bis $30,$30,$20 /* a4 = KSP */ + br $17,__do_swppal + + ldq $26,0($30) + ldq $1,8($30) + ldq $2,16($30) + ldq $3,24($30) + ldq $4,32($30) + ldq $5,40($30) + ldq $6,48($30) + ldq $7,56($30) + ldq $8,64($30) + ldq $9,72($30) + ldq $10,80($30) + ldq $11,88($30) + ldq $12,96($30) + ldq $13,104($30) + ldq $14,112($30) + ldq $15,120($30) + addq $30,128,$30 + ret ($26) + +__do_swppal: + .long PAL_swppal + .end switch_to_osf_pal diff -u --recursive --new-file v1.1.76/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c --- v1.1.76/linux/arch/alpha/kernel/traps.c Thu Jan 1 02:00:00 1970 +++ linux/arch/alpha/kernel/traps.c Wed Jan 4 21:16:04 1995 @@ -0,0 +1,58 @@ +/* + * kernel/traps.c + * + * (C) Copyright 1994 Linus Torvalds + */ + +/* + * This file initializes the trap entry points + */ + +#include + +#include +#include + +extern asmlinkage void entInt(void); +void keyboard_interrupt(void); + +void do_hw_interrupt(unsigned long type, unsigned long vector) +{ + if (type == 1) { + jiffies++; + return; + } + /* keyboard or mouse */ + if (type == 3) { + if (vector == 0x980) { + keyboard_interrupt(); + return; + } else { + unsigned char c = inb_local(0x64); + printk("IO device interrupt, vector = %lx\n", vector); + if (!(c & 1)) { + int i; + printk("Hmm. Keyboard interrupt, status = %02x\n", c); + for (i = 0; i < 10000000 ; i++) + /* nothing */; + printk("Serial line interrupt status: %02x\n", inb_local(0x3fa)); + } else { + c = inb_local(0x60); + printk("#%02x# ", c); + } + return; + } + } + printk("Hardware intr %ld %ld\n", type, vector); +} + +void trap_init(void) +{ + unsigned long gptr; + + __asm__("br %0,___tmp\n" + "___tmp:\tldgp %0,0(%0)" + : "=r" (gptr)); + wrkgp(gptr); + wrent(entInt, 0); +} diff -u --recursive --new-file v1.1.76/linux/arch/alpha/traps.c linux/arch/alpha/traps.c --- v1.1.76/linux/arch/alpha/traps.c Thu Dec 29 19:58:40 1994 +++ linux/arch/alpha/traps.c Thu Jan 1 02:00:00 1970 @@ -1,58 +0,0 @@ -/* - * kernel/traps.c - * - * (C) Copyright 1994 Linus Torvalds - */ - -/* - * This file initializes the trap entry points - */ - -#include - -#include -#include - -extern asmlinkage void entInt(void); -void keyboard_interrupt(void); - -void do_hw_interrupt(unsigned long type, unsigned long vector) -{ - if (type == 1) { - jiffies++; - return; - } - /* keyboard or mouse */ - if (type == 3) { - if (vector == 0x980) { - keyboard_interrupt(); - return; - } else { - unsigned char c = inb_local(0x64); - printk("IO device interrupt, vector = %lx\n", vector); - if (!(c & 1)) { - int i; - printk("Hmm. Keyboard interrupt, status = %02x\n", c); - for (i = 0; i < 10000000 ; i++) - /* nothing */; - printk("Serial line interrupt status: %02x\n", inb_local(0x3fa)); - } else { - c = inb_local(0x60); - printk("#%02x# ", c); - } - return; - } - } - printk("Hardware intr %ld %ld\n", type, vector); -} - -void trap_init(void) -{ - unsigned long gptr; - - __asm__("br %0,___tmp\n" - "___tmp:\tldgp %0,0(%0)" - : "=r" (gptr)); - wrkgp(gptr); - wrent(entInt, 0); -} diff -u --recursive --new-file v1.1.76/linux/arch/i386/Makefile linux/arch/i386/Makefile --- v1.1.76/linux/arch/i386/Makefile Mon Jan 2 09:25:17 1995 +++ linux/arch/i386/Makefile Wed Jan 4 21:16:04 1995 @@ -37,6 +37,21 @@ endif endif +HEAD := arch/i386/kernel/head.o + +SUBDIRS := $(SUBDIRS) arch/i386/kernel +ARCHIVES := arch/i386/kernel/kernel.o $(ARCHIVES) + +ifdef CONFIG_IBCS +SUBDIRS := $(SUBDIRS) arch/i386/ibcs +DRIVERS := $(DRIVERS) arch/i386/ibcs/ibcs.o +endif + +ifdef CONFIG_MATH_EMULATION +SUBDIRS := $(SUBDIRS) arch/i386/math-emu +DRIVERS := $(DRIVERS) arch/i386/math-emu/math.a +endif + MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot zImage: vmlinux @@ -46,6 +61,12 @@ zlilo: vmlinux @$(MAKEBOOT) zlilo + +zdisk: vmlinux + @$(MAKEBOOT) zdisk + +install: vmlinux + @$(MAKEBOOT) install archclean: @$(MAKEBOOT) clean diff -u --recursive --new-file v1.1.76/linux/arch/i386/boot/Makefile linux/arch/i386/boot/Makefile --- v1.1.76/linux/arch/i386/boot/Makefile Mon Jan 2 09:23:11 1995 +++ linux/arch/i386/boot/Makefile Tue Jan 3 13:57:26 1995 @@ -28,6 +28,9 @@ cp $(TOPDIR)/System.map $(INSTALL_PATH)/ if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi +install: $(CONFIGURE) zImage + sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)" + tools/build: tools/build.c $(HOSTCC) $(CFLAGS) -o $@ $< -I$(TOPDIR)/include diff -u --recursive --new-file v1.1.76/linux/arch/i386/boot/compressed/Makefile linux/arch/i386/boot/compressed/Makefile --- v1.1.76/linux/arch/i386/boot/compressed/Makefile Thu Dec 29 19:58:41 1994 +++ linux/arch/i386/boot/compressed/Makefile Wed Jan 4 19:11:02 1995 @@ -1,5 +1,5 @@ # -# linux/archi/i386/boot/compressed/Makefile +# linux/arch/i386/boot/compressed/Makefile # # create a compressed vmlinux image from the original vmlinux # diff -u --recursive --new-file v1.1.76/linux/arch/i386/boot/compressed/xtract.c linux/arch/i386/boot/compressed/xtract.c --- v1.1.76/linux/arch/i386/boot/compressed/xtract.c Thu Dec 29 19:58:41 1994 +++ linux/arch/i386/boot/compressed/xtract.c Thu Jan 5 13:55:40 1995 @@ -16,7 +16,6 @@ #include /* contains read/write */ #include #include -#include #define N_MAGIC_OFFSET 1024 diff -u --recursive --new-file v1.1.76/linux/arch/i386/boot/install.sh linux/arch/i386/boot/install.sh --- v1.1.76/linux/arch/i386/boot/install.sh Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/boot/install.sh Tue Jan 3 13:57:26 1995 @@ -0,0 +1,39 @@ +#!/bin/sh +# +# arch/i386/boot/install.sh +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1995 by Linus Torvalds +# +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin +# +# "make install" script for i386 architecture +# +# Arguments: +# $1 - kernel version +# $2 - kernel image file +# $3 - kernel map file +# $4 - default install path (blank if root directory) +# + +# User may have a custom install script + +if [ -x /sbin/installkernel ]; then exec /sbin/installkernel "$@"; fi + +# Default install - same as make zlilo + +if [ -f $4/vmlinuz ]; then + mv $4/vmlinuz $4/vmlinuz.old +fi + +if [ -f $4/System.map ]; then + mv $4/System.map $4/System.old +fi + +cat $2 > $4/vmlinuz +cp $3 $4/System.map + +if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi diff -u --recursive --new-file v1.1.76/linux/arch/i386/boot/tools/build.c linux/arch/i386/boot/tools/build.c --- v1.1.76/linux/arch/i386/boot/tools/build.c Thu Dec 29 19:58:41 1994 +++ linux/arch/i386/boot/tools/build.c Thu Jan 5 17:29:01 1995 @@ -28,8 +28,8 @@ #include #include /* contains read/write */ #include -#include #include +#include #define MINIX_HEADER 32 diff -u --recursive --new-file v1.1.76/linux/arch/i386/config.in linux/arch/i386/config.in --- v1.1.76/linux/arch/i386/config.in Sun Jan 1 19:49:19 1995 +++ linux/arch/i386/config.in Sat Jan 7 12:58:21 1995 @@ -16,7 +16,7 @@ bool ' Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE y fi if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then - bool ' Include support for IDE CDROM (ATAPI)' CONFIG_BLK_DEV_IDECD n + bool ' Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD n fi fi @@ -169,10 +169,10 @@ fi fi -comment 'CD-ROM drivers' +comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n -bool 'Mitsumi CDROM driver support' CONFIG_MCD n +bool 'Mitsumi (not IDE/ATAPI) CDROM driver support' CONFIG_MCD n bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n if [ "$CONFIG_SBPCD" = "y" ]; then bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n diff -u --recursive --new-file v1.1.76/linux/arch/i386/entry.S linux/arch/i386/entry.S --- v1.1.76/linux/arch/i386/entry.S Sun Nov 27 20:19:52 1994 +++ linux/arch/i386/entry.S Thu Jan 1 02:00:00 1970 @@ -1,545 +0,0 @@ -/* - * linux/arch/i386/entry.S - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * entry.S contains the system-call and fault low-level handling routines. - * This also contains the timer-interrupt handler, as well as all interrupts - * and faults that can result in a task-switch. - * - * NOTE: This code handles signal-recognition, which happens every time - * after a timer-interrupt and after each system call. - * - * I changed all the .align's to 4 (16 byte alignment), as that's faster - * on a 486. - * - * Stack layout in 'ret_from_system_call': - * ptrace needs to have all regs on the stack. - * if the order here is changed, it needs to be - * updated in fork.c:copy_process, signal.c:do_signal, - * ptrace.c and ptrace.h - * - * 0(%esp) - %ebx - * 4(%esp) - %ecx - * 8(%esp) - %edx - * C(%esp) - %esi - * 10(%esp) - %edi - * 14(%esp) - %ebp - * 18(%esp) - %eax - * 1C(%esp) - %ds - * 20(%esp) - %es - * 24(%esp) - %fs - * 28(%esp) - %gs - * 2C(%esp) - orig_eax - * 30(%esp) - %eip - * 34(%esp) - %cs - * 38(%esp) - %eflags - * 3C(%esp) - %oldesp - * 40(%esp) - %oldss - */ - -#define __ASSEMBLY__ -#include -#include - -EBX = 0x00 -ECX = 0x04 -EDX = 0x08 -ESI = 0x0C -EDI = 0x10 -EBP = 0x14 -EAX = 0x18 -DS = 0x1C -ES = 0x20 -FS = 0x24 -GS = 0x28 -ORIG_EAX = 0x2C -EIP = 0x30 -CS = 0x34 -EFLAGS = 0x38 -OLDESP = 0x3C -OLDSS = 0x40 - -CF_MASK = 0x00000001 -IF_MASK = 0x00000200 -NT_MASK = 0x00004000 -VM_MASK = 0x00020000 - -/* - * these are offsets into the task-struct. - */ -state = 0 -counter = 4 -priority = 8 -signal = 12 -blocked = 16 -flags = 20 -errno = 24 -dbgreg6 = 52 -dbgreg7 = 56 -exec_domain = 60 - -ENOSYS = 38 - -.globl _system_call,_lcall7 -.globl _device_not_available, _coprocessor_error -.globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op -.globl _double_fault,_coprocessor_segment_overrun -.globl _invalid_TSS,_segment_not_present,_stack_segment -.globl _general_protection,_reserved -.globl _alignment_check,_page_fault -.globl ret_from_sys_call, _sys_call_table - -#define SAVE_ALL \ - cld; \ - push %gs; \ - push %fs; \ - push %es; \ - push %ds; \ - pushl %eax; \ - pushl %ebp; \ - pushl %edi; \ - pushl %esi; \ - pushl %edx; \ - pushl %ecx; \ - pushl %ebx; \ - movl $(KERNEL_DS),%edx; \ - mov %dx,%ds; \ - mov %dx,%es; \ - movl $(USER_DS),%edx; \ - mov %dx,%fs; - -#define RESTORE_ALL \ - cmpw $(KERNEL_CS),CS(%esp); \ - je 1f; \ - movl _current,%eax; \ - movl dbgreg7(%eax),%ebx; \ - movl %ebx,%db7; \ -1: popl %ebx; \ - popl %ecx; \ - popl %edx; \ - popl %esi; \ - popl %edi; \ - popl %ebp; \ - popl %eax; \ - pop %ds; \ - pop %es; \ - pop %fs; \ - pop %gs; \ - addl $4,%esp; \ - iret - -.align 4 -_lcall7: - pushfl # We get a different stack layout with call gates, - pushl %eax # which has to be cleaned up later.. - SAVE_ALL - movl EIP(%esp),%eax # due to call gates, this is eflags, not eip.. - movl CS(%esp),%edx # this is eip.. - movl EFLAGS(%esp),%ecx # and this is cs.. - movl %eax,EFLAGS(%esp) # - movl %edx,EIP(%esp) # Now we move them to their "normal" places - movl %ecx,CS(%esp) # - movl %esp,%eax - movl _current,%edx - pushl %eax - movl exec_domain(%edx),%edx # Get the execution domain - movl 4(%edx),%edx # Get the lcall7 handler for the domain - call *%edx - popl %eax - jmp ret_from_sys_call - -.align 4 -handle_bottom_half: - pushfl - incl _intr_count - sti - call _do_bottom_half - popfl - decl _intr_count - jmp 9f -.align 4 -reschedule: - pushl $ret_from_sys_call - jmp _schedule -.align 4 -_system_call: - pushl %eax # save orig_eax - SAVE_ALL - movl $-ENOSYS,EAX(%esp) - cmpl $(NR_syscalls),%eax - jae ret_from_sys_call - movl _sys_call_table(,%eax,4),%eax - testl %eax,%eax - je ret_from_sys_call - movl _current,%ebx - andl $~CF_MASK,EFLAGS(%esp) # clear carry - assume no errors - movl $0,errno(%ebx) - movl %db6,%edx - movl %edx,dbgreg6(%ebx) # save current hardware debugging status - testb $0x20,flags(%ebx) # PF_TRACESYS - jne 1f - call *%eax - movl %eax,EAX(%esp) # save the return value - movl errno(%ebx),%edx - negl %edx - je ret_from_sys_call - movl %edx,EAX(%esp) - orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error - jmp ret_from_sys_call -.align 4 -1: call _syscall_trace - movl ORIG_EAX(%esp),%eax - call _sys_call_table(,%eax,4) - movl %eax,EAX(%esp) # save the return value - movl _current,%eax - movl errno(%eax),%edx - negl %edx - je 1f - movl %edx,EAX(%esp) - orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error -1: call _syscall_trace - - .align 4,0x90 -ret_from_sys_call: - cmpl $0,_intr_count - jne 2f -9: movl _bh_mask,%eax - andl _bh_active,%eax - jne handle_bottom_half - movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are - testl $(VM_MASK),%eax # different then - jne 1f - cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ? - je 2f -1: sti - orl $(IF_MASK),%eax # these just try to make sure - andl $~NT_MASK,%eax # the program doesn't do anything - movl %eax,EFLAGS(%esp) # stupid - cmpl $0,_need_resched - jne reschedule - movl _current,%eax - cmpl _task,%eax # task[0] cannot have signals - je 2f - cmpl $0,state(%eax) # state - jne reschedule - cmpl $0,counter(%eax) # counter - je reschedule - movl blocked(%eax),%ecx - movl %ecx,%ebx # save blocked in %ebx for signal handling - notl %ecx - andl signal(%eax),%ecx - jne signal_return -2: RESTORE_ALL -.align 4 -signal_return: - movl %esp,%ecx - pushl %ecx - testl $(VM_MASK),EFLAGS(%ecx) - jne v86_signal_return - pushl %ebx - call _do_signal - popl %ebx - popl %ebx - RESTORE_ALL -.align 4 -v86_signal_return: - call _save_v86_state - movl %eax,%esp - pushl %eax - pushl %ebx - call _do_signal - popl %ebx - popl %ebx - RESTORE_ALL - -.align 4 -_divide_error: - pushl $0 # no error code - pushl $_do_divide_error -.align 4,0x90 -error_code: - push %fs - push %es - push %ds - pushl %eax - pushl %ebp - pushl %edi - pushl %esi - pushl %edx - pushl %ecx - pushl %ebx - movl $0,%eax - movl %eax,%db7 # disable hardware debugging... - cld - movl $-1, %eax - xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. ) - xorl %ebx,%ebx # zero ebx - mov %gs,%bx # get the lower order bits of gs - xchgl %ebx, GS(%esp) # get the address and save gs. - pushl %eax # push the error code - lea 4(%esp),%edx - pushl %edx - movl $(KERNEL_DS),%edx - mov %dx,%ds - mov %dx,%es - movl $(USER_DS),%edx - mov %dx,%fs - pushl %eax - movl _current,%eax - movl %db6,%edx - movl %edx,dbgreg6(%eax) # save current hardware debugging status - popl %eax - call *%ebx - addl $8,%esp - jmp ret_from_sys_call - -.align 4 -_coprocessor_error: - pushl $0 - pushl $_do_coprocessor_error - jmp error_code - -.align 4 -_device_not_available: - pushl $-1 # mark this as an int - SAVE_ALL - pushl $ret_from_sys_call - movl %cr0,%eax - testl $0x4,%eax # EM (math emulation bit) - je _math_state_restore - pushl $0 # temporary storage for ORIG_EIP - call _math_emulate - addl $4,%esp - ret - -.align 4 -_debug: - pushl $0 - pushl $_do_debug - jmp error_code - -.align 4 -_nmi: - pushl $0 - pushl $_do_nmi - jmp error_code - -.align 4 -_int3: - pushl $0 - pushl $_do_int3 - jmp error_code - -.align 4 -_overflow: - pushl $0 - pushl $_do_overflow - jmp error_code - -.align 4 -_bounds: - pushl $0 - pushl $_do_bounds - jmp error_code - -.align 4 -_invalid_op: - pushl $0 - pushl $_do_invalid_op - jmp error_code - -.align 4 -_coprocessor_segment_overrun: - pushl $0 - pushl $_do_coprocessor_segment_overrun - jmp error_code - -.align 4 -_reserved: - pushl $0 - pushl $_do_reserved - jmp error_code - -.align 4 -_double_fault: - pushl $_do_double_fault - jmp error_code - -.align 4 -_invalid_TSS: - pushl $_do_invalid_TSS - jmp error_code - -.align 4 -_segment_not_present: - pushl $_do_segment_not_present - jmp error_code - -.align 4 -_stack_segment: - pushl $_do_stack_segment - jmp error_code - -.align 4 -_general_protection: - pushl $_do_general_protection - jmp error_code - -.align 4 -_alignment_check: - pushl $_do_alignment_check - jmp error_code - -.align 4 -_page_fault: - pushl $_do_page_fault - jmp error_code - -.data -.align 4 -_sys_call_table: - .long _sys_setup /* 0 */ - .long _sys_exit - .long _sys_fork - .long _sys_read - .long _sys_write - .long _sys_open /* 5 */ - .long _sys_close - .long _sys_waitpid - .long _sys_creat - .long _sys_link - .long _sys_unlink /* 10 */ - .long _sys_execve - .long _sys_chdir - .long _sys_time - .long _sys_mknod - .long _sys_chmod /* 15 */ - .long _sys_chown - .long _sys_break - .long _sys_stat - .long _sys_lseek - .long _sys_getpid /* 20 */ - .long _sys_mount - .long _sys_umount - .long _sys_setuid - .long _sys_getuid - .long _sys_stime /* 25 */ - .long _sys_ptrace - .long _sys_alarm - .long _sys_fstat - .long _sys_pause - .long _sys_utime /* 30 */ - .long _sys_stty - .long _sys_gtty - .long _sys_access - .long _sys_nice - .long _sys_ftime /* 35 */ - .long _sys_sync - .long _sys_kill - .long _sys_rename - .long _sys_mkdir - .long _sys_rmdir /* 40 */ - .long _sys_dup - .long _sys_pipe - .long _sys_times - .long _sys_prof - .long _sys_brk /* 45 */ - .long _sys_setgid - .long _sys_getgid - .long _sys_signal - .long _sys_geteuid - .long _sys_getegid /* 50 */ - .long _sys_acct - .long _sys_phys - .long _sys_lock - .long _sys_ioctl - .long _sys_fcntl /* 55 */ - .long _sys_mpx - .long _sys_setpgid - .long _sys_ulimit - .long _sys_olduname - .long _sys_umask /* 60 */ - .long _sys_chroot - .long _sys_ustat - .long _sys_dup2 - .long _sys_getppid - .long _sys_getpgrp /* 65 */ - .long _sys_setsid - .long _sys_sigaction - .long _sys_sgetmask - .long _sys_ssetmask - .long _sys_setreuid /* 70 */ - .long _sys_setregid - .long _sys_sigsuspend - .long _sys_sigpending - .long _sys_sethostname - .long _sys_setrlimit /* 75 */ - .long _sys_getrlimit - .long _sys_getrusage - .long _sys_gettimeofday - .long _sys_settimeofday - .long _sys_getgroups /* 80 */ - .long _sys_setgroups - .long _sys_select - .long _sys_symlink - .long _sys_lstat - .long _sys_readlink /* 85 */ - .long _sys_uselib - .long _sys_swapon - .long _sys_reboot - .long _sys_readdir - .long _sys_mmap /* 90 */ - .long _sys_munmap - .long _sys_truncate - .long _sys_ftruncate - .long _sys_fchmod - .long _sys_fchown /* 95 */ - .long _sys_getpriority - .long _sys_setpriority - .long _sys_profil - .long _sys_statfs - .long _sys_fstatfs /* 100 */ - .long _sys_ioperm - .long _sys_socketcall - .long _sys_syslog - .long _sys_setitimer - .long _sys_getitimer /* 105 */ - .long _sys_newstat - .long _sys_newlstat - .long _sys_newfstat - .long _sys_uname - .long _sys_iopl /* 110 */ - .long _sys_vhangup - .long _sys_idle - .long _sys_vm86 - .long _sys_wait4 - .long _sys_swapoff /* 115 */ - .long _sys_sysinfo - .long _sys_ipc - .long _sys_fsync - .long _sys_sigreturn - .long _sys_clone /* 120 */ - .long _sys_setdomainname - .long _sys_newuname - .long _sys_modify_ldt - .long _sys_adjtimex - .long _sys_mprotect /* 125 */ - .long _sys_sigprocmask - .long _sys_create_module - .long _sys_init_module - .long _sys_delete_module - .long _sys_get_kernel_syms /* 130 */ - .long _sys_quotactl - .long _sys_getpgid - .long _sys_fchdir - .long _sys_bdflush - .long _sys_sysfs /* 135 */ - .long _sys_personality - .long 0 /* for afs_syscall */ - .long _sys_setfsuid - .long _sys_setfsgid - .long _sys_llseek /* 140 */ - .space (NR_syscalls-140)*4 diff -u --recursive --new-file v1.1.76/linux/arch/i386/head.S linux/arch/i386/head.S --- v1.1.76/linux/arch/i386/head.S Mon Jan 2 15:19:59 1995 +++ linux/arch/i386/head.S Thu Jan 1 02:00:00 1970 @@ -1,362 +0,0 @@ -/* - * linux/arch/i386/head.S - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * head.S contains the 32-bit startup code. - */ - -.text -.globl _idt,_gdt, -.globl _swapper_pg_dir,_pg0 -.globl _empty_bad_page -.globl _empty_bad_page_table -.globl _empty_zero_page -.globl _floppy_track_buffer - -#define __ASSEMBLY__ -#include -#include -#include - -#define CL_MAGIC_ADDR 0x90020 -#define CL_MAGIC 0xA33F -#define CL_BASE_ADDR 0x90000 -#define CL_OFFSET 0x90022 - -/* - * swapper_pg_dir is the main page directory, address 0x00001000 (or at - * address 0x00101000 for a compressed boot). - */ -startup_32: - cld - movl $(KERNEL_DS),%eax - mov %ax,%ds - mov %ax,%es - mov %ax,%fs - mov %ax,%gs - lss stack_start,%esp -/* - * Clear BSS first so that there are no surprises... - */ - xorl %eax,%eax - movl $__edata,%edi - movl $__end,%ecx - subl %edi,%ecx - cld - rep - stosb -/* - * start system 32-bit setup. We need to re-do some of the things done - * in 16-bit mode for the "real" operations. - */ - call setup_idt - xorl %eax,%eax -1: incl %eax # check that A20 really IS enabled - movl %eax,0x000000 # loop forever if it isn't - cmpl %eax,0x100000 - je 1b -/* - * Initialize eflags. Some BIOS's leave bits like NT set. This would - * confuse the debugger if this code is traced. - * XXX - best to initialize before switching to protected mode. - */ - pushl $0 - popfl -/* - * Copy bootup parameters out of the way. First 2kB of - * _empty_zero_page is for boot parameters, second 2kB - * is for the command line. - */ - movl $0x90000,%esi - movl $_empty_zero_page,%edi - movl $512,%ecx - cld - rep - movsl - xorl %eax,%eax - movl $512,%ecx - rep - stosl - cmpw $(CL_MAGIC),CL_MAGIC_ADDR - jne 1f - movl $_empty_zero_page+2048,%edi - movzwl CL_OFFSET,%esi - addl $(CL_BASE_ADDR),%esi - movl $2048,%ecx - rep - movsb -1: -/* check if it is 486 or 386. */ -/* - * XXX - this does a lot of unnecessary setup. Alignment checks don't - * apply at our cpl of 0 and the stack ought to be aligned already, and - * we don't need to preserve eflags. - */ - movl $3,_x86 - pushfl # push EFLAGS - popl %eax # get EFLAGS - movl %eax,%ecx # save original EFLAGS - xorl $0x40000,%eax # flip AC bit in EFLAGS - pushl %eax # copy to EFLAGS - popfl # set EFLAGS - pushfl # get new EFLAGS - popl %eax # put it in eax - xorl %ecx,%eax # change in flags - andl $0x40000,%eax # check if AC bit changed - je is386 - movl $4,_x86 - movl %ecx,%eax - xorl $0x200000,%eax # check ID flag - pushl %eax - popfl # if we are on a straight 486DX, SX, or - pushfl # 487SX we can't change it - popl %eax - xorl %ecx,%eax - andl $0x200000,%eax - je is486 -isnew: pushl %ecx # restore original EFLAGS - popfl - /* get processor type */ - movl $1, %eax # Use the CPUID instruction to - .byte 0x0f, 0xa2 # check the processor type - movb %al, %cl # save reg for future use - andb $0x0f,%ah # mask processor family - movb %ah, _x86 - andb $0xf0, %eax # mask model - shrb $4, %al - movb %al, _x86_model - andb $0x0f, %cl # mask mask revision - movb %cl, _x86_mask - movl %edx, _x86_capability - /* get vendor info */ - xorl %eax, %eax # call CPUID with 0 -> return vendor ID - .byte 0x0f, 0xa2 # CPUID - movl %ebx, _x86_vendor_id # lo 4 chars - movl %edx, _x86_vendor_id+4 # next 4 chars - movl %ecx, _x86_vendor_id+8 # last 4 chars - - movl %cr0,%eax # 486+ - andl $0x80000011,%eax # Save PG,PE,ET - orl $0x50022,%eax # set AM, WP, NE and MP - jmp 2f -is486: pushl %ecx # restore original EFLAGS - popfl - movl %cr0,%eax # 486 - andl $0x80000011,%eax # Save PG,PE,ET - orl $0x50022,%eax # set AM, WP, NE and MP - jmp 2f -is386: pushl %ecx # restore original EFLAGS - popfl - movl %cr0,%eax # 386 - andl $0x80000011,%eax # Save PG,PE,ET - orl $2,%eax # set MP -2: movl %eax,%cr0 - call check_x87 - call setup_paging - lgdt gdt_descr - lidt idt_descr - ljmp $(KERNEL_CS),$1f -1: movl $(KERNEL_DS),%eax # reload all the segment registers - mov %ax,%ds # after changing gdt. - mov %ax,%es - mov %ax,%fs - mov %ax,%gs - lss stack_start,%esp - xorl %eax,%eax - lldt %ax - pushl %eax # These are the parameters to main :-) - pushl %eax - pushl %eax - cld # gcc2 wants the direction flag cleared at all times - call _start_kernel -L6: - jmp L6 # main should never return here, but - # just in case, we know what happens. - -/* - * We depend on ET to be correct. This checks for 287/387. - */ -check_x87: - movb $0,_hard_math - clts - fninit - fstsw %ax - cmpb $0,%al - je 1f - movl %cr0,%eax /* no coprocessor: have to set bits */ - xorl $4,%eax /* set EM */ - movl %eax,%cr0 - ret -.align 2 -1: movb $1,_hard_math - .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ - ret - -/* - * setup_idt - * - * sets up a idt with 256 entries pointing to - * ignore_int, interrupt gates. It doesn't actually load - * idt - that can be done only after paging has been enabled - * and the kernel moved to 0xC0000000. Interrupts - * are enabled elsewhere, when we can be relatively - * sure everything is ok. - */ -setup_idt: - lea ignore_int,%edx - movl $(KERNEL_CS << 16),%eax - movw %dx,%ax /* selector = 0x0010 = cs */ - movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ - - lea _idt,%edi - mov $256,%ecx -rp_sidt: - movl %eax,(%edi) - movl %edx,4(%edi) - addl $8,%edi - dec %ecx - jne rp_sidt - ret - - -/* - * Setup_paging - * - * This routine sets up paging by setting the page bit - * in cr0. The page tables are set up, identity-mapping - * the first 4MB. The rest are initialized later. - * - * (ref: added support for up to 32mb, 17Apr92) -- Rik Faith - * (ref: update, 25Sept92) -- croutons@crunchy.uucp - * (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit) - */ -.align 2 -setup_paging: - movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */ - xorl %eax,%eax - movl $_swapper_pg_dir,%edi /* swapper_pg_dir is at 0x1000 */ - cld;rep;stosl -/* Identity-map the kernel in low 4MB memory for ease of transition */ - movl $_pg0+7,_swapper_pg_dir /* set present bit/user r/w */ -/* But the real place is at 0xC0000000 */ - movl $_pg0+7,_swapper_pg_dir+3072 /* set present bit/user r/w */ - movl $_pg0+4092,%edi - movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */ - std -1: stosl /* fill the page backwards - more efficient :-) */ - subl $0x1000,%eax - jge 1b - cld - movl $_swapper_pg_dir,%eax - movl %eax,%cr3 /* cr3 - page directory start */ - movl %cr0,%eax - orl $0x80000000,%eax - movl %eax,%cr0 /* set paging (PG) bit */ - ret /* this also flushes the prefetch-queue */ - -/* - * page 0 is made non-existent, so that kernel NULL pointer references get - * caught. Thus the swapper page directory has been moved to 0x1000 - * - * XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte, - * with the introduction of the compressed boot code. Theoretically, - * the original design of overlaying the startup code with the swapper - * page directory is still possible --- it would reduce the size of the kernel - * by 2-3k. This would be a good thing to do at some point..... - */ -.org 0x1000 -_swapper_pg_dir: -/* - * The page tables are initialized to only 4MB here - the final page - * tables are set up later depending on memory size. - */ -.org 0x2000 -_pg0: - -.org 0x3000 -_empty_bad_page: - -.org 0x4000 -_empty_bad_page_table: - -.org 0x5000 -_empty_zero_page: - -.org 0x6000 -/* - * floppy_track_buffer is used to buffer one track of floppy data: it - * has to be separate from the tmp_floppy area, as otherwise a single- - * sector read/write can mess it up. It can contain one full cylinder (sic) of - * data (36*2*512 bytes). - */ -_floppy_track_buffer: - .fill 512*2*MAX_BUFFER_SECTORS,1,0 - -stack_start: - .long _init_user_stack+4096 - .long KERNEL_DS - -/* This is the default interrupt "handler" :-) */ -int_msg: - .asciz "Unknown interrupt\n" -.align 2 -ignore_int: - cld - pushl %eax - pushl %ecx - pushl %edx - push %ds - push %es - push %fs - movl $(KERNEL_DS),%eax - mov %ax,%ds - mov %ax,%es - mov %ax,%fs - pushl $int_msg - call _printk - popl %eax - pop %fs - pop %es - pop %ds - popl %edx - popl %ecx - popl %eax - iret - -/* - * The interrupt descriptor table has room for 256 idt's - */ -.align 4 -.word 0 -idt_descr: - .word 256*8-1 # idt contains 256 entries - .long 0xc0000000+_idt - -.align 4 -_idt: - .fill 256,8,0 # idt is uninitialized - -.align 4 -.word 0 -gdt_descr: - .word (8+2*NR_TASKS)*8-1 - .long 0xc0000000+_gdt - -/* - * This gdt setup gives the kernel a 1GB address space at virtual - * address 0xC0000000 - space enough for expansion, I hope. - */ -.align 4 -_gdt: - .quad 0x0000000000000000 /* NULL descriptor */ - .quad 0x0000000000000000 /* not used */ - .quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */ - .quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */ - .quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */ - .quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */ - .quad 0x0000000000000000 /* not used */ - .quad 0x0000000000000000 /* not used */ - .fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */ diff -u --recursive --new-file v1.1.76/linux/arch/i386/ibcs/Makefile linux/arch/i386/ibcs/Makefile --- v1.1.76/linux/arch/i386/ibcs/Makefile Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/ibcs/Makefile Sun Nov 27 20:19:53 1994 @@ -0,0 +1,37 @@ +# +# Makefile for the iBCS emulator files +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.s: + $(CPP) -traditional $< -o $*.s +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) -c -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< + +SUBDIRS = + +OBJS = emulate.o + +ibcs.o: $(OBJS) + $(LD) -r -o ibcs.o $(OBJS) + sync + +dep: + $(CPP) -M *.c > .depend + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff -u --recursive --new-file v1.1.76/linux/arch/i386/ibcs/binfmt_coff.c linux/arch/i386/ibcs/binfmt_coff.c --- v1.1.76/linux/arch/i386/ibcs/binfmt_coff.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/ibcs/binfmt_coff.c Tue Nov 22 15:40:12 1994 @@ -0,0 +1,784 @@ +/* + * These are the functions used to load COFF IBSC style executables. + * Information on COFF format may be obtained in either the Intel Binary + * Compatibility Specification 2 or O'Rilley's book on COFF. The shared + * libraries are defined only the in the Intel book. + * + * This file is based upon code written by Eric Youngdale for the ELF object + * file format. + * + * Author: Al Longyear (longyear@sii.com) + * + * Latest Revision: + * 3 February 1994 + * Al Longyear (longyear@sii.com) + * Cleared first page of bss section using put_fs_byte. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +asmlinkage int sys_exit (int exit_code); +asmlinkage int sys_close (unsigned fd); +asmlinkage int sys_open (const char *, int, int); +asmlinkage int sys_uselib(const char * library); + +static int preload_library (struct linux_binprm *exe_bprm, + COFF_SCNHDR * sect, + struct file *fp); + +static int load_object (struct linux_binprm *bprm, + struct pt_regs *regs, + int lib_ok); + +/* + * Small procedure to test for the proper file alignment. + */ + +static inline int +is_properly_aligned (COFF_SCNHDR *sect) +{ + long scnptr = COFF_LONG (sect->s_scnptr); + long vaddr = COFF_LONG (sect->s_vaddr); +/* + * Print the section information if needed + */ + +#ifdef COFF_DEBUG + printk ("%s, scnptr = %d, vaddr = %d\n", + sect->s_name, + scnptr, vaddr); +#endif + +/* + * Return the error code if the section is not properly aligned. + */ + +#ifdef COFF_DEBUG + if (((vaddr - scnptr) & ~PAGE_MASK) != 0) + printk ("bad alignment in %s\n", sect->s_name); +#endif + return ((((vaddr - scnptr) & ~PAGE_MASK) != 0) ? -ENOEXEC : 0); +} + +/* + * Clear the bytes in the last page of data. + */ + +static +int clear_memory (unsigned long addr, unsigned long size) +{ + int status; + + size = (PAGE_SIZE - (addr & ~PAGE_MASK)) & ~PAGE_MASK; + if (size == 0) + status = 0; + else { + +#ifdef COFF_DEBUG + printk ("un-initialized storage in last page %d\n", size); +#endif + + status = verify_area (VERIFY_WRITE, + (void *) addr, size); +#ifdef COFF_DEBUG + printk ("result from verify_area = %d\n", status); +#endif + + if (status >= 0) + while (size-- != 0) + put_fs_byte (0, addr++); + } + return status; +} + +/* + * Helper function to process the load operation. + */ + +static int +load_object (struct linux_binprm * bprm, struct pt_regs *regs, int lib_ok) +{ + COFF_FILHDR *coff_hdr = (COFF_FILHDR *) bprm->buf; /* COFF Header */ + COFF_SCNHDR *sect_bufr; /* Pointer to section table */ + COFF_SCNHDR *text_sect; /* Pointer to the text section */ + COFF_SCNHDR *data_sect; /* Pointer to the data section */ + COFF_SCNHDR *bss_sect; /* Pointer to the bss section */ + int text_count; /* Number of text sections */ + int data_count; /* Number of data sections */ + int bss_count; /* Number of bss sections */ + int lib_count; /* Number of lib sections */ + unsigned int start_addr = 0;/* Starting location for program */ + int status = 0; /* Result status register */ + int fd = -1; /* Open file descriptor */ + struct file *fp = NULL; /* Pointer to the file at "fd" */ + short int sections = 0; /* Number of sections in the file */ + short int aout_size = 0; /* Size of the a.out header area */ + short int flags; /* Flag bits from the COFF header */ + +#ifdef COFF_DEBUG + printk ("binfmt_coff entry: %s\n", bprm->filename); +#endif + +/* + * Validate the magic value for the object file. + */ + do { + if (COFF_I386BADMAG (*coff_hdr)) { +#ifdef COFF_DEBUG + printk ("bad filehdr magic\n"); +#endif + status = -ENOEXEC; + break; + } +/* + * The object file should have 32 BIT little endian format. Do not allow + * it to have the 16 bit object file flag set as Linux is not able to run + * on the 80286/80186/8086. + */ + flags = COFF_SHORT (coff_hdr->f_flags); + if ((flags & (COFF_F_AR32WR | COFF_F_AR16WR)) != COFF_F_AR32WR) { +#ifdef COFF_DEBUG + printk ("invalid f_flags bits\n"); +#endif + status = -ENOEXEC; + break; + } +/* + * Extract the header information which we need. + */ + sections = COFF_SHORT (coff_hdr->f_nscns); /* Number of sections */ + aout_size = COFF_SHORT (coff_hdr->f_opthdr); /* Size of opt. headr */ +/* + * If the file is not executable then reject the execution. This means + * that there must not be external references. + */ + if ((flags & COFF_F_EXEC) == 0) { +#ifdef COFF_DEBUG + printk ("not executable bit\n"); +#endif + status = -ENOEXEC; + break; + } +/* + * There must be at least one section. + */ + if (sections == 0) { +#ifdef COFF_DEBUG + printk ("no sections\n"); +#endif + status = -ENOEXEC; + break; + } +/* + * Do some additional consistency checks. + * The system requires mapping for this loader. If you try + * to use a file system with no mapping, the format is not valid. + */ + if (!bprm->inode->i_op || + !bprm->inode->i_op->default_file_ops->mmap) { +#ifdef COFF_DEBUG + printk ("no mmap in fs\n"); +#endif + status = -ENOEXEC; + } + } + while (0); +/* + * Allocate a buffer to hold the entire coff section list. + */ + if (status >= 0) { + int nbytes = sections * COFF_SCNHSZ; + + sect_bufr = (COFF_SCNHDR *) kmalloc (nbytes, GFP_KERNEL); + if (0 == sect_bufr) { +#ifdef COFF_DEBUG + printk ("kmalloc failed\n"); +#endif + status = -ENOEXEC; + } +/* + * Read the section list from the disk file. + */ + else { + int old_fs = get_fs (); + set_fs (get_ds ()); /* Make it point to the proper location */ + status = read_exec (bprm->inode, /* INODE for file */ + aout_size + COFF_FILHSZ, /* Offset in the file */ + (char *) sect_bufr, /* Buffer for read */ + nbytes); /* Byte count reqd. */ + set_fs (old_fs); /* Restore the selector */ +#ifdef COFF_DEBUG + if (status < 0) + printk ("read aout hdr, status = %d\n", status); +#endif + } + } + else + sect_bufr = NULL; /* Errors do not have a section buffer */ +/* + * Count the number of sections for the required types and store the location + * of the last section for the three primary types. + */ + text_count = 0; + data_count = 0; + bss_count = 0; + lib_count = 0; + + text_sect = NULL; + data_sect = NULL; + bss_sect = NULL; +/* + * Loop through the sections and find the various types + */ + if (status >= 0) { + int nIndex; + COFF_SCNHDR *sect_ptr = sect_bufr; + + for (nIndex = 0; nIndex < sections; ++nIndex) { + long int sect_flags = COFF_LONG (sect_ptr->s_flags); + + switch (sect_flags) { + case COFF_STYP_TEXT: + text_sect = sect_ptr; + ++text_count; + status = is_properly_aligned (sect_ptr); + break; + + case COFF_STYP_DATA: + data_sect = sect_ptr; + ++data_count; + status = is_properly_aligned (sect_ptr); + break; + + case COFF_STYP_BSS: + bss_sect = sect_ptr; + ++bss_count; + break; + + case COFF_STYP_LIB: +#ifdef COFF_DEBUG + printk (".lib section found\n"); +#endif + ++lib_count; + break; + + default: + break; + } + sect_ptr = (COFF_SCNHDR *) & ((char *) sect_ptr)[COFF_SCNHSZ]; + } +/* + * Ensure that there are the required sections. There must be one text + * sections and one each of the data and bss sections for an executable. + * A library may or may not have a data / bss section. + */ + if (text_count != 1) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("no text sections\n"); +#endif + } + else { + if (lib_ok) { + if (data_count != 1 || bss_count != 1) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("no .data nor .bss sections\n"); +#endif + } + } + } + } +/* + * If there is no additional header then assume the file starts at + * the first byte of the text section. This may not be the proper place, + * so the best solution is to include the optional header. A shared library + * __MUST__ have an optional header to indicate that it is a shared library. + */ + if (status >= 0) { + if (aout_size == 0) { + if (!lib_ok) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("no header in library\n"); +#endif + } + start_addr = COFF_LONG (text_sect->s_vaddr); + } +/* + * There is some header. Ensure that it is sufficient. + */ + else { + if (aout_size < COFF_AOUTSZ) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("header too small\n"); +#endif + } + else { + COFF_AOUTHDR *aout_hdr = /* Pointer to a.out header */ + (COFF_AOUTHDR *) & ((char *) coff_hdr)[COFF_FILHSZ]; + short int aout_magic = COFF_SHORT (aout_hdr->magic); /* id */ +/* + * Validate the magic number in the a.out header. If it is valid then + * update the starting symbol location. Do not accept these file formats + * when loading a shared library. + */ + switch (aout_magic) { + case COFF_OMAGIC: + case COFF_ZMAGIC: + case COFF_STMAGIC: + if (!lib_ok) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("wrong a.out header magic\n"); +#endif + } + start_addr = (unsigned int) COFF_LONG (aout_hdr->entry); + break; +/* + * Magic value for a shared library. This is valid only when loading a + * shared library. (There is no need for a start_addr. It won't be used.) + */ + case COFF_SHMAGIC: + if (lib_ok) { +#ifdef COFF_DEBUG + printk ("wrong a.out header magic\n"); +#endif + status = -ENOEXEC; + } + break; + + default: +#ifdef COFF_DEBUG + printk ("wrong a.out header magic\n"); +#endif + status = -ENOEXEC; + break; + } + } + } + } +/* + * Fetch a file pointer to the executable. + */ + if (status >= 0) { + fd = open_inode (bprm->inode, O_RDONLY); + if (fd < 0) { +#ifdef COFF_DEBUG + printk ("can not open inode, result = %d\n", fd); +#endif + status = fd; + } + else + fp = current->files->fd[fd]; + } + else + fd = -1; /* Invalidate the open file descriptor */ +/* + * Generate the proper values for the text fields + * + * THIS IS THE POINT OF NO RETURN. THE NEW PROCESS WILL TRAP OUT SHOULD + * SOMETHING FAIL IN THE LOAD SEQUENCE FROM THIS POINT ONWARD. + */ + if (status >= 0) { + long text_scnptr = COFF_LONG (text_sect->s_scnptr); + long text_size = COFF_LONG (text_sect->s_size); + long text_vaddr = COFF_LONG (text_sect->s_vaddr); + + long data_scnptr; + long data_size; + long data_vaddr; + + long bss_size; + long bss_vaddr; +/* + * Generate the proper values for the data fields + */ + if (data_sect != NULL) { + data_scnptr = COFF_LONG (data_sect->s_scnptr); + data_size = COFF_LONG (data_sect->s_size); + data_vaddr = COFF_LONG (data_sect->s_vaddr); + } + else { + data_scnptr = 0; + data_size = 0; + data_vaddr = 0; + } +/* + * Generate the proper values for the bss fields + */ + if (bss_sect != NULL) { + bss_size = COFF_LONG (bss_sect->s_size); + bss_vaddr = COFF_LONG (bss_sect->s_vaddr); + } + else { + bss_size = 0; + bss_vaddr = 0; + } +/* + * Flush the executable from memory. At this point the executable is + * committed to being defined or a segmentation violation will occur. + */ + if (lib_ok) { +#ifdef COFF_DEBUG + printk ("flushing executable\n"); +#endif + flush_old_exec (bprm); +/* + * Define the initial locations for the various items in the new process + */ + current->mm->mmap = NULL; + current->mm->rss = 0; +/* + * Construct the parameter and environment string table entries. + */ + bprm->p += change_ldt (0, bprm->page); + bprm->p -= MAX_ARG_PAGES*PAGE_SIZE; + bprm->p = (unsigned long) create_tables ((char *) bprm->p, + bprm->argc, + bprm->envc, + 1); +/* + * Do the end processing once the stack has been constructed + */ + current->mm->start_code = text_vaddr & PAGE_MASK; + current->mm->end_code = text_vaddr + text_size; + current->mm->end_data = data_vaddr + data_size; + current->mm->start_brk = + current->mm->brk = bss_vaddr + bss_size; + current->suid = + current->euid = bprm->e_uid; + current->sgid = + current->egid = bprm->e_gid; + current->executable = bprm->inode; /* Store inode for file */ + ++bprm->inode->i_count; /* Count the open inode */ + regs->eip = start_addr; /* Current EIP register */ + regs->esp = + current->mm->start_stack = bprm->p; + } +/* + * Map the text pages + */ + +#ifdef COFF_DEBUG + printk (".text: vaddr = %d, size = %d, scnptr = %d\n", + text_vaddr, + text_size, + text_scnptr); +#endif + status = do_mmap (fp, + text_vaddr & PAGE_MASK, + text_size + (text_vaddr & ~PAGE_MASK), + PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_SHARED, + text_scnptr & PAGE_MASK); + + status = (status == (text_vaddr & PAGE_MASK)) ? 0 : -ENOEXEC; +/* + * Map the data pages + */ + if (status >= 0 && data_size != 0) { +#ifdef COFF_DEBUG + printk (".data: vaddr = %d, size = %d, scnptr = %d\n", + data_vaddr, + data_size, + data_scnptr); +#endif + status = do_mmap (fp, + data_vaddr & PAGE_MASK, + data_size + (data_vaddr & ~PAGE_MASK), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, + data_scnptr & PAGE_MASK); + + status = (status == (data_vaddr & PAGE_MASK)) ? 0 : -ENOEXEC; + } +/* + * Construct the bss data for the process. The bss ranges from the + * end of the data (which may not be on a page boundary) to the end + * of the bss section. Allocate any necessary pages for the data. + */ + if (status >= 0 && bss_size != 0) { +#ifdef COFF_DEBUG + printk (".bss: vaddr = %d, size = %d\n", + bss_vaddr, + bss_size); +#endif + zeromap_page_range (PAGE_ALIGN (bss_vaddr), + PAGE_ALIGN (bss_size), + PAGE_COPY); + + status = clear_memory (bss_vaddr, bss_size); + } +/* + * Load any shared library for the executable. + */ + if (status >= 0 && lib_ok && lib_count != 0) { + int nIndex; + COFF_SCNHDR *sect_ptr = sect_bufr; +/* + * Find the library sections. (There should be at least one. It was counted + * earlier.) This will eventually recurse to our code and load the shared + * library with our own procedures. + */ + for (nIndex = 0; nIndex < sections; ++nIndex) { + long int sect_flags = COFF_LONG (sect_ptr->s_flags); + if (sect_flags == COFF_STYP_LIB) { + status = preload_library (bprm, sect_ptr, fp); + if (status != 0) + break; + } + sect_ptr = (COFF_SCNHDR *) &((char *) sect_ptr) [COFF_SCNHSZ]; + } + } +/* + * Generate any needed trap for this process. If an error occurred then + * generate a segmentation violation. If the process is being debugged + * then generate the load trap. (Note: If this is a library load then + * do not generate the trap here. Pass the error to the caller who + * will do it for the process in the outer lay of this procedure call.) + */ + if (lib_ok) { + if (status < 0) + send_sig (SIGSEGV, current, 0); /* Generate the error trap */ + else { + if (current->flags & PF_PTRACED) + send_sig (SIGTRAP, current, 0); + } + status = 0; /* We are committed. It can't fail */ + } + } +/* + * Do any cleanup processing + */ + if (fd >= 0) + sys_close (fd); /* Close unused code file */ + + if (sect_bufr != NULL) + kfree (sect_bufr); /* Release section list buffer */ +/* + * Return the completion status. + */ +#ifdef COFF_DEBUG + printk ("binfmt_coff: result = %d\n", status); +#endif + return (status); +} + +/* + * This procedure will load the library listed in the file name given + * as the parameter. The result will be non-zero should something fail + * to load. + */ + +static int +preload_this_library (struct linux_binprm *exe_bprm, char *lib_name) +{ + int status; + int old_fs = get_fs(); +/* + * If debugging then print "we have arrived" + */ +#ifdef COFF_DEBUG + printk ("%s loading shared library %s\n", + exe_bprm->filename, + lib_name); +#endif +/* + * Change the FS register to the proper kernel address space and attempt + * to load the library. The library name is allocated from the kernel + * pool. + */ + set_fs (get_ds ()); + status = sys_uselib (lib_name); + set_fs (old_fs); +/* + * Return the success/failure to the caller. + */ + return (status); +} + +/* + * This procedure is called to load a library section. The various + * libraries are loaded from the list given in the section data. + */ + +static int +preload_library (struct linux_binprm *exe_bprm, + COFF_SCNHDR * sect, struct file *fp) +{ + int status = 0; /* Completion status */ + long nbytes; /* Count of bytes in the header area */ +/* + * Fetch the size of the section. There must be enough room for at least + * one entry. + */ + nbytes = COFF_LONG (sect->s_size); + if (nbytes < COFF_SLIBSZ) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("library section too small\n"); +#endif + } +/* + * Allocate a buffer to hold the section data + */ + else { + COFF_SLIBHD *phdr; + char *buffer = (char *) kmalloc (nbytes, GFP_KERNEL); + + if (0 == buffer) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("kmalloc failed\n"); +#endif + } + else { + int old_fs = get_fs (); +/* + * Read the section data from the disk file. + */ + set_fs (get_ds ()); /* Make it point to the proper location */ + status = read_exec (exe_bprm->inode, /* INODE for file */ + COFF_LONG (sect->s_scnptr), /* Disk location */ + buffer, /* Buffer for read */ + nbytes); /* Byte count reqd. */ + set_fs (old_fs); /* Restore the selector */ +/* + * Check the result. The value returned is the byte count actually read. + */ + if (status >= 0 && status != nbytes) { +#ifdef COFF_DEBUG + printk ("read of lib section was short\n"); +#endif + status = -ENOEXEC; + } + } +/* + * At this point, go through the list of libraries in the data area. + */ + phdr = (COFF_SLIBHD *) buffer; + while (status >= 0 && nbytes > COFF_SLIBSZ) { + int entry_size = COFF_LONG (phdr->sl_entsz) * sizeof (long); + int header_size = COFF_LONG (phdr->sl_pathndx) * sizeof (long); +/* + * Validate the sizes of the various items. I don't trust the linker!! + */ + if ((unsigned) header_size >= (unsigned) nbytes || + entry_size <= 0 || + (unsigned) entry_size <= (unsigned) header_size) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("header count is invalid\n"); +#endif + } +/* + * Load the library. Stop the load process on the first error. + */ + else { + status = preload_this_library (exe_bprm, + &((char *) phdr)[header_size]); +#ifdef COFF_DEBUG + printk ("preload_this_library result = %d\n", status); +#endif + } +/* + * Point to the next library in the section data. + */ + nbytes -= entry_size; + phdr = (COFF_SLIBHD *) &((char *) phdr)[entry_size]; + } +/* + * Release the space for the library list. + */ + if (buffer != NULL) + kfree (buffer); + } +/* + * Return the resulting status to the caller. + */ + return (status); +} + +/* + * This procedure is called by the main load sequence. It will load + * the executable and prepare it for execution. It provides the additional + * parameters used by the recursive coff loader and tells the loader that + * this is the main executable. How simple it is . . . . + */ + +int +load_coff_binary (struct linux_binprm *bprm, struct pt_regs *regs) +{ + return (load_object (bprm, regs, 1)); +} + +/* + * Load the image for any shared library. + * + * This is called when we need to load a library based upon a file name. + */ + +int +load_coff_library (int fd) +{ + struct linux_binprm *bprm; /* Parameters for the load operation */ + int status; /* Status of the request */ +/* + * Read the first portion of the file. + */ + bprm = (struct linux_binprm *) kmalloc (sizeof (struct linux_binprm), + GFP_KERNEL); + if (0 == bprm) { +#ifdef COFF_DEBUG + printk ("kmalloc failed\n"); +#endif + status = -ENOEXEC; + } + else { + struct file *file; /* Pointer to the file table */ + struct pt_regs regs; /* Register work area */ + int old_fs = get_fs (); /* Previous FS register value */ + + memset (bprm, '\0', sizeof (struct linux_binprm)); + + file = current->files->fd[fd]; + bprm->inode = file->f_inode; /* The only item _really_ needed */ + bprm->filename = ""; /* Make it a legal string */ +/* + * Read the section list from the disk file. + */ + set_fs (get_ds ()); /* Make it point to the proper location */ + status = read_exec (bprm->inode, /* INODE for file */ + 0L, /* Offset in the file */ + bprm->buf, /* Buffer for read */ + sizeof (bprm->buf)); /* Size of the buffer */ + set_fs (old_fs); /* Restore the selector */ +/* + * Try to load the library. + */ + status = load_object (bprm, ®s, 0); +/* + * Release the work buffer and return the result. + */ + kfree (bprm); /* Release the buffer area */ + } +/* + * Return the result of the load operation + */ + return (status); +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/ibcs/binfmt_elf.c linux/arch/i386/ibcs/binfmt_elf.c --- v1.1.76/linux/arch/i386/ibcs/binfmt_elf.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/ibcs/binfmt_elf.c Wed Nov 30 17:17:39 1994 @@ -0,0 +1,655 @@ +/* + * linux/fs/binfmt_elf.c + * + * These are the functions used to load ELF format executables as used + * on SVr4 machines. Information on the format may be found in the book + * "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support + * Tools". + * + * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +asmlinkage int sys_exit(int exit_code); +asmlinkage int sys_close(unsigned fd); +asmlinkage int sys_open(const char *, int, int); +asmlinkage int sys_brk(unsigned long); + +#define DLINFO_ITEMS 8 + +#include + +/* We need to explicitly zero any fractional pages + after the data section (i.e. bss). This would + contain the junk from the file that should not + be in memory */ + +static void padzero(int elf_bss){ + unsigned int fpnt, nbyte; + + if(elf_bss & 0xfff) { + + nbyte = (PAGE_SIZE - (elf_bss & 0xfff)) & 0xfff; + if(nbyte){ + verify_area(VERIFY_WRITE, (void *) elf_bss, nbyte); + + fpnt = elf_bss; + while(fpnt & 0xfff) put_fs_byte(0, fpnt++); + }; + }; +} + +unsigned long * create_elf_tables(char * p,int argc,int envc,struct elfhdr * exec, unsigned int load_addr, int ibcs) +{ + unsigned long *argv,*envp, *dlinfo; + unsigned long * sp; + struct vm_area_struct *mpnt; + + mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); + if (mpnt) { + mpnt->vm_task = current; + mpnt->vm_start = PAGE_MASK & (unsigned long) p; + mpnt->vm_end = TASK_SIZE; + mpnt->vm_page_prot = PAGE_PRIVATE|PAGE_DIRTY; + mpnt->vm_flags = VM_STACK_FLAGS; + mpnt->vm_ops = NULL; + mpnt->vm_inode = NULL; + mpnt->vm_offset = 0; + mpnt->vm_pte = 0; + insert_vm_struct(current, mpnt); + } + sp = (unsigned long *) (0xfffffffc & (unsigned long) p); + if(exec) sp -= DLINFO_ITEMS*2; + dlinfo = sp; + sp -= envc+1; + envp = sp; + sp -= argc+1; + argv = sp; + if (!ibcs) { + put_fs_long((unsigned long)envp,--sp); + put_fs_long((unsigned long)argv,--sp); + } + + /* The constant numbers (0-9) that we are writing here are + described in the header file sys/auxv.h on at least + some versions of SVr4 */ + if(exec) { /* Put this here for an ELF program interpreter */ + struct elf_phdr * eppnt; + eppnt = (struct elf_phdr *) exec->e_phoff; + put_fs_long(3,dlinfo++); put_fs_long(load_addr + exec->e_phoff,dlinfo++); + put_fs_long(4,dlinfo++); put_fs_long(sizeof(struct elf_phdr),dlinfo++); + put_fs_long(5,dlinfo++); put_fs_long(exec->e_phnum,dlinfo++); + put_fs_long(9,dlinfo++); put_fs_long((unsigned long) exec->e_entry,dlinfo++); + put_fs_long(7,dlinfo++); put_fs_long(SHM_RANGE_START,dlinfo++); + put_fs_long(8,dlinfo++); put_fs_long(0,dlinfo++); + put_fs_long(6,dlinfo++); put_fs_long(PAGE_SIZE,dlinfo++); + put_fs_long(0,dlinfo++); put_fs_long(0,dlinfo++); + }; + + put_fs_long((unsigned long)argc,--sp); + current->mm->arg_start = (unsigned long) p; + while (argc-->0) { + put_fs_long((unsigned long) p,argv++); + while (get_fs_byte(p++)) /* nothing */ ; + } + put_fs_long(0,argv); + current->mm->arg_end = current->mm->env_start = (unsigned long) p; + while (envc-->0) { + put_fs_long((unsigned long) p,envp++); + while (get_fs_byte(p++)) /* nothing */ ; + } + put_fs_long(0,envp); + current->mm->env_end = (unsigned long) p; + return sp; +} + + +/* This is much more generalized than the library routine read function, + so we keep this separate. Technically the library read function + is only provided so that we can read a.out libraries that have + an ELF header */ + +static unsigned int load_elf_interp(struct elfhdr * interp_elf_ex, + struct inode * interpreter_inode) +{ + struct file * file; + struct elf_phdr *elf_phdata = NULL; + struct elf_phdr *eppnt; + unsigned int len; + unsigned int load_addr; + int elf_exec_fileno; + int elf_bss; + int old_fs, retval; + unsigned int last_bss; + int error; + int i, k; + + elf_bss = 0; + last_bss = 0; + error = load_addr = 0; + + /* First of all, some simple consistency checks */ + if((interp_elf_ex->e_type != ET_EXEC && + interp_elf_ex->e_type != ET_DYN) || + (interp_elf_ex->e_machine != EM_386 && interp_elf_ex->e_machine != EM_486) || + (!interpreter_inode->i_op || + !interpreter_inode->i_op->default_file_ops->mmap)){ + return 0xffffffff; + }; + + /* Now read in all of the header information */ + + if(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE) + return 0xffffffff; + + elf_phdata = (struct elf_phdr *) + kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, GFP_KERNEL); + if(!elf_phdata) return 0xffffffff; + + old_fs = get_fs(); + set_fs(get_ds()); + retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, (char *) elf_phdata, + sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); + set_fs(old_fs); + + elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY); + if (elf_exec_fileno < 0) return 0xffffffff; + file = current->files->fd[elf_exec_fileno]; + + eppnt = elf_phdata; + for(i=0; ie_phnum; i++, eppnt++) + if(eppnt->p_type == PT_LOAD) { + error = do_mmap(file, + eppnt->p_vaddr & 0xfffff000, + eppnt->p_filesz + (eppnt->p_vaddr & 0xfff), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | (interp_elf_ex->e_type == ET_EXEC ? MAP_FIXED : 0), + eppnt->p_offset & 0xfffff000); + + if(!load_addr && interp_elf_ex->e_type == ET_DYN) + load_addr = error; + k = load_addr + eppnt->p_vaddr + eppnt->p_filesz; + if(k > elf_bss) elf_bss = k; + if(error < 0 && error > -1024) break; /* Real error */ + k = load_addr + eppnt->p_memsz + eppnt->p_vaddr; + if(k > last_bss) last_bss = k; + } + + /* Now use mmap to map the library into memory. */ + + + sys_close(elf_exec_fileno); + if(error < 0 && error > -1024) { + kfree(elf_phdata); + return 0xffffffff; + } + + padzero(elf_bss); + len = (elf_bss + 0xfff) & 0xfffff000; /* What we have mapped so far */ + + /* Map the last of the bss segment */ + if (last_bss > len) + do_mmap(NULL, len, last_bss-len, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + kfree(elf_phdata); + + return ((unsigned int) interp_elf_ex->e_entry) + load_addr; +} + +static unsigned int load_aout_interp(struct exec * interp_ex, + struct inode * interpreter_inode) +{ + int retval; + unsigned int elf_entry; + + current->mm->brk = interp_ex->a_bss + + (current->mm->end_data = interp_ex->a_data + + (current->mm->end_code = interp_ex->a_text)); + elf_entry = interp_ex->a_entry; + + + if (N_MAGIC(*interp_ex) == OMAGIC) { + do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + retval = read_exec(interpreter_inode, 32, (char *) 0, + interp_ex->a_text+interp_ex->a_data); + } else if (N_MAGIC(*interp_ex) == ZMAGIC || N_MAGIC(*interp_ex) == QMAGIC) { + do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + retval = read_exec(interpreter_inode, + N_TXTOFF(*interp_ex) , + (char *) N_TXTADDR(*interp_ex), + interp_ex->a_text+interp_ex->a_data); + } else + retval = -1; + + if(retval >= 0) + do_mmap(NULL, (interp_ex->a_text + interp_ex->a_data + 0xfff) & + 0xfffff000, interp_ex->a_bss, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + if(retval < 0) return 0xffffffff; + return elf_entry; +} + +/* + * These are the functions used to load ELF style executables and shared + * libraries. There is no binary dependent code anywhere else. + */ + +#define INTERPRETER_NONE 0 +#define INTERPRETER_AOUT 1 +#define INTERPRETER_ELF 2 + +static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) +{ + struct elfhdr elf_ex; + struct elfhdr interp_elf_ex; + struct file * file; + struct exec interp_ex; + struct inode *interpreter_inode; + unsigned int load_addr; + unsigned int interpreter_type = INTERPRETER_NONE; + int i; + int old_fs; + int error; + struct elf_phdr * elf_ppnt, *elf_phdata; + int elf_exec_fileno; + unsigned int elf_bss, k, elf_brk; + int retval; + char * elf_interpreter; + unsigned int elf_entry; + int status; + unsigned int start_code, end_code, end_data; + unsigned int elf_stack; + char passed_fileno[6]; + + status = 0; + load_addr = 0; + elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ + + if (elf_ex.e_ident[0] != 0x7f || + strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) + return -ENOEXEC; + + + /* First of all, some simple consistency checks */ + if(elf_ex.e_type != ET_EXEC || + (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) || + (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || + !bprm->inode->i_op->default_file_ops->mmap)){ + return -ENOEXEC; + }; + + /* Now read in all of the header information */ + + elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize * + elf_ex.e_phnum, GFP_KERNEL); + + old_fs = get_fs(); + set_fs(get_ds()); + retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata, + elf_ex.e_phentsize * elf_ex.e_phnum); + set_fs(old_fs); + if (retval < 0) { + kfree (elf_phdata); + return retval; + } + + elf_ppnt = elf_phdata; + + elf_bss = 0; + elf_brk = 0; + + elf_exec_fileno = open_inode(bprm->inode, O_RDONLY); + + if (elf_exec_fileno < 0) { + kfree (elf_phdata); + return elf_exec_fileno; + } + + file = current->files->fd[elf_exec_fileno]; + + elf_stack = 0xffffffff; + elf_interpreter = NULL; + start_code = 0; + end_code = 0; + end_data = 0; + + old_fs = get_fs(); + set_fs(get_ds()); + + for(i=0;i < elf_ex.e_phnum; i++){ + if(elf_ppnt->p_type == PT_INTERP) { + /* This is the program interpreter used for shared libraries - + for now assume that this is an a.out format binary */ + + elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz, + GFP_KERNEL); + + retval = read_exec(bprm->inode,elf_ppnt->p_offset,elf_interpreter, + elf_ppnt->p_filesz); +#if 0 + printk("Using ELF interpreter %s\n", elf_interpreter); +#endif + if(retval >= 0) + retval = namei(elf_interpreter, &interpreter_inode); + if(retval >= 0) + retval = read_exec(interpreter_inode,0,bprm->buf,128); + + if(retval >= 0){ + interp_ex = *((struct exec *) bprm->buf); /* exec-header */ + interp_elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ + + }; + if(retval < 0) { + kfree (elf_phdata); + kfree(elf_interpreter); + return retval; + }; + }; + elf_ppnt++; + }; + + set_fs(old_fs); + + /* Some simple consistency checks for the interpreter */ + if(elf_interpreter){ + interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT; + if(retval < 0) { + kfree(elf_interpreter); + kfree(elf_phdata); + return -ELIBACC; + }; + /* Now figure out which format our binary is */ + if((N_MAGIC(interp_ex) != OMAGIC) && + (N_MAGIC(interp_ex) != ZMAGIC) && + (N_MAGIC(interp_ex) != QMAGIC)) + interpreter_type = INTERPRETER_ELF; + + if (interp_elf_ex.e_ident[0] != 0x7f || + strncmp(&interp_elf_ex.e_ident[1], "ELF",3) != 0) + interpreter_type &= ~INTERPRETER_ELF; + + if(!interpreter_type) + { + kfree(elf_interpreter); + kfree(elf_phdata); + return -ELIBBAD; + }; + } + + /* OK, we are done with that, now set up the arg stuff, + and then start this sucker up */ + + if (!bprm->sh_bang) { + char * passed_p; + + if(interpreter_type == INTERPRETER_AOUT) { + sprintf(passed_fileno, "%d", elf_exec_fileno); + passed_p = passed_fileno; + + if(elf_interpreter) { + bprm->p = copy_strings(1,&passed_p,bprm->page,bprm->p,2); + bprm->argc++; + }; + }; + if (!bprm->p) { + if(elf_interpreter) { + kfree(elf_interpreter); + } + kfree (elf_phdata); + return -E2BIG; + } + } + + /* OK, This is the point of no return */ + flush_old_exec(bprm); + + current->mm->end_data = 0; + current->mm->end_code = 0; + current->mm->start_mmap = ELF_START_MMAP; + current->mm->mmap = NULL; + elf_entry = (unsigned int) elf_ex.e_entry; + + /* Do this so that we can load the interpreter, if need be. We will + change some of these later */ + current->mm->rss = 0; + bprm->p += change_ldt(0, bprm->page); + current->mm->start_stack = bprm->p; + + /* Now we do a little grungy work by mmaping the ELF image into + the correct location in memory. At this point, we assume that + the image should be loaded at fixed address, not at a variable + address. */ + + old_fs = get_fs(); + set_fs(get_ds()); + + elf_ppnt = elf_phdata; + for(i=0;i < elf_ex.e_phnum; i++){ + + if(elf_ppnt->p_type == PT_INTERP) { + /* Set these up so that we are able to load the interpreter */ + /* Now load the interpreter into user address space */ + set_fs(old_fs); + + if(interpreter_type & 1) elf_entry = + load_aout_interp(&interp_ex, interpreter_inode); + + if(interpreter_type & 2) elf_entry = + load_elf_interp(&interp_elf_ex, interpreter_inode); + + old_fs = get_fs(); + set_fs(get_ds()); + + iput(interpreter_inode); + kfree(elf_interpreter); + + if(elf_entry == 0xffffffff) { + printk("Unable to load interpreter\n"); + kfree(elf_phdata); + send_sig(SIGSEGV, current, 0); + return 0; + }; + }; + + + if(elf_ppnt->p_type == PT_LOAD) { + error = do_mmap(file, + elf_ppnt->p_vaddr & 0xfffff000, + elf_ppnt->p_filesz + (elf_ppnt->p_vaddr & 0xfff), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, + elf_ppnt->p_offset & 0xfffff000); + +#ifdef LOW_ELF_STACK + if(elf_ppnt->p_vaddr & 0xfffff000 < elf_stack) + elf_stack = elf_ppnt->p_vaddr & 0xfffff000; +#endif + + if(!load_addr) + load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset; + k = elf_ppnt->p_vaddr; + if(k > start_code) start_code = k; + k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; + if(k > elf_bss) elf_bss = k; + if((elf_ppnt->p_flags | PROT_WRITE) && end_code < k) + end_code = k; + if(end_data < k) end_data = k; + k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; + if(k > elf_brk) elf_brk = k; + }; + elf_ppnt++; + }; + set_fs(old_fs); + + kfree(elf_phdata); + + if(interpreter_type != INTERPRETER_AOUT) sys_close(elf_exec_fileno); + + /* The following 3 lines need a little bit of work if we are loading + an iBCS2 binary. We should initially load it this way, and if + we get a lcall7, then we should look to see if the iBCS2 execution + profile is present. If it is, then switch to that, otherwise + bomb. */ + current->personality = PER_LINUX; + current->lcall7 = no_lcall7; + current->signal_map = current->signal_invmap = ident_map; + + current->executable = bprm->inode; + bprm->inode->i_count++; +#ifdef LOW_ELF_STACK + current->start_stack = p = elf_stack - 4; +#endif + bprm->p -= MAX_ARG_PAGES*PAGE_SIZE; + bprm->p = (unsigned long) + create_elf_tables((char *)bprm->p, + bprm->argc, + bprm->envc, + (interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL), + load_addr, + (interpreter_type == INTERPRETER_AOUT ? 0 : 1)); + if(interpreter_type == INTERPRETER_AOUT) + current->mm->arg_start += strlen(passed_fileno) + 1; + current->mm->start_brk = current->mm->brk = elf_brk; + current->mm->end_code = end_code; + current->mm->start_code = start_code; + current->mm->end_data = end_data; + current->mm->start_stack = bprm->p; + current->suid = current->euid = bprm->e_uid; + current->sgid = current->egid = bprm->e_gid; + + /* Calling sys_brk effectively mmaps the pages that we need for the bss and break + sections */ + current->mm->brk = (elf_bss + 0xfff) & 0xfffff000; + sys_brk((elf_brk + 0xfff) & 0xfffff000); + + padzero(elf_bss); + + /* Why this, you ask??? Well SVr4 maps page 0 as read-only, + and some applications "depend" upon this behavior. + Since we do not have the power to recompile these, we + emulate the SVr4 behavior. Sigh. */ + error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, 0); + + regs->eip = elf_entry; /* eip, magic happens :-) */ + regs->esp = bprm->p; /* stack pointer */ + if (current->flags & PF_PTRACED) + send_sig(SIGTRAP, current, 0); + return 0; +} + +/* This is really simpleminded and specialized - we are loading an + a.out library that is given an ELF header. */ + +static int load_elf_library(int fd){ + struct file * file; + struct elfhdr elf_ex; + struct elf_phdr *elf_phdata = NULL; + struct inode * inode; + unsigned int len; + int elf_bss; + int old_fs, retval; + unsigned int bss; + int error; + int i,j, k; + + len = 0; + file = current->files->fd[fd]; + inode = file->f_inode; + elf_bss = 0; + + set_fs(KERNEL_DS); + if (file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)) != sizeof(elf_ex)) { + sys_close(fd); + return -EACCES; + } + set_fs(USER_DS); + + if (elf_ex.e_ident[0] != 0x7f || + strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) + return -ENOEXEC; + + /* First of all, some simple consistency checks */ + if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || + (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) || + (!inode->i_op || + !inode->i_op->default_file_ops->mmap)){ + return -ENOEXEC; + }; + + /* Now read in all of the header information */ + + if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) + return -ENOEXEC; + + elf_phdata = (struct elf_phdr *) + kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL); + + old_fs = get_fs(); + set_fs(get_ds()); + retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata, + sizeof(struct elf_phdr) * elf_ex.e_phnum); + set_fs(old_fs); + + j = 0; + for(i=0; ip_type == PT_LOAD) j++; + + if(j != 1) { + kfree(elf_phdata); + return -ENOEXEC; + }; + + while(elf_phdata->p_type != PT_LOAD) elf_phdata++; + + /* Now use mmap to map the library into memory. */ + error = do_mmap(file, + elf_phdata->p_vaddr & 0xfffff000, + elf_phdata->p_filesz + (elf_phdata->p_vaddr & 0xfff), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, + elf_phdata->p_offset & 0xfffff000); + + k = elf_phdata->p_vaddr + elf_phdata->p_filesz; + if(k > elf_bss) elf_bss = k; + + sys_close(fd); + if (error != elf_phdata->p_vaddr & 0xfffff000) { + kfree(elf_phdata); + return error; + } + + padzero(elf_bss); + + len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+ 0xfff) & 0xfffff000; + bss = elf_phdata->p_memsz + elf_phdata->p_vaddr; + if (bss > len) + do_mmap(NULL, len, bss-len, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + kfree(elf_phdata); + return 0; +} + +struct linux_binfmt elf_format = { NULL, load_elf_binary, load_elf_library }; diff -u --recursive --new-file v1.1.76/linux/arch/i386/ibcs/emulate.c linux/arch/i386/ibcs/emulate.c --- v1.1.76/linux/arch/i386/ibcs/emulate.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/ibcs/emulate.c Wed May 25 11:26:10 1994 @@ -0,0 +1,10 @@ +/* + * linux/abi/emulate.c + * + * Copyright (C) 1993 Linus Torvalds + */ + +/* + * Yes, sir, this file is completely empty, waiting for some real code.. + * I still copyright it, silly me. + */ diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile --- v1.1.76/linux/arch/i386/kernel/Makefile Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/Makefile Wed Jan 4 21:16:04 1995 @@ -0,0 +1,46 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o + +OBJS = process.o signal.o entry.o traps.o irq.o vm86.o bios32.o ptrace.o ioport.o ldt.o + +all: kernel.o head.o + +head.o: head.s + +head.s: head.S $(TOPDIR)/include/linux/tasks.h + $(CPP) -traditional -o $*.s $< + +kernel.o: $(OBJS) + $(LD) -r -o kernel.o $(OBJS) + sync + +dep: + $(CPP) -M *.c > .depend + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + + diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c --- v1.1.76/linux/arch/i386/kernel/bios32.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/bios32.c Sat Jan 7 12:57:43 1995 @@ -0,0 +1,638 @@ +/* + * bios32.c - BIOS32, PCI BIOS functions. + * + * Sponsored by + * iX Multiuser Multitasking Magazine + * Hannover, Germany + * hm@ix.de + * + * Copyright 1993, 1994 Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * Drew@Colorado.EDU + * +1 (303) 786-7975 + * + * Pciprobe added by Frederic Potter 1994 + * Potter@Cao-Vlsi.Ibp.FR + * + * + * For more information, please consult + * + * PCI BIOS Specification Revision + * PCI Local Bus Specification + * PCI System Design Guide + * + * PCI Special Interest Group + * M/S HF3-15A + * 5200 N.E. Elam Young Parkway + * Hillsboro, Oregon 97124-6497 + * +1 (503) 696-2000 + * +1 (800) 433-5177 + * + * Manuals are $25 each or $50 for all three, plus $7 shipping + * within the United States, $35 abroad. + * + * + * CHANGELOG : + * Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION + * Revision 2.0 present on 's ASUS mainboard. + */ + +#include +#include +#include +#include + +#include + +#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX +#define PCIBIOS_PCI_BIOS_PRESENT 0xb101 +#define PCIBIOS_FIND_PCI_DEVICE 0xb102 +#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103 +#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106 +#define PCIBIOS_READ_CONFIG_BYTE 0xb108 +#define PCIBIOS_READ_CONFIG_WORD 0xb109 +#define PCIBIOS_READ_CONFIG_DWORD 0xb10a +#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b +#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c +#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d + +/* BIOS32 signature: "_32_" */ +#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) + +/* PCI signature: "PCI " */ +#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24)) + +/* PCI service signature: "$PCI" */ +#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24)) + +/* + * This is the standard structure used to identify the entry point + * to the BIOS32 Service Directory, as documented in + * Standard BIOS 32-bit Service Directory Proposal + * Revision 0.4 May 24, 1993 + * Phoenix Technologies Ltd. + * Norwood, MA + * and the PCI BIOS specification. + */ + +union bios32 { + struct { + unsigned long signature; /* _32_ */ + unsigned long entry; /* 32 bit physical address */ + unsigned char revision; /* Revision level, 0 */ + unsigned char length; /* Length in paragraphs should be 01 */ + unsigned char checksum; /* All bytes must add up to zero */ + unsigned char reserved[5]; /* Must be zero */ + } fields; + char chars[16]; +}; + +/* + * Physical address of the service directory. I don't know if we're + * allowed to have more than one of these or not, so just in case + * we'll make bios32_init() take a memory start parameter and store + * the array there. + */ + +static unsigned long bios32_entry = 0; +static struct { + unsigned long address; + unsigned short segment; +} bios32_indirect = { 0, KERNEL_CS }; + +#ifdef CONFIG_PCI +/* + * Returns the entry point for the given service, NULL on error + */ + +static unsigned long bios32_service(unsigned long service) +{ + unsigned char return_code; /* %al */ + unsigned long address; /* %ebx */ + unsigned long length; /* %ecx */ + unsigned long entry; /* %edx */ + + __asm__("lcall (%%edi)" + : "=a" (return_code), + "=b" (address), + "=c" (length), + "=d" (entry) + : "0" (service), + "1" (0), + "D" (&bios32_indirect)); + + switch (return_code) { + case 0: + return address + entry; + case 0x80: /* Not present */ + printk("bios32_service(%ld) : not present\n", service); + return 0; + default: /* Shouldn't happen */ + printk("bios32_service(%ld) : returned 0x%x, mail drew@colorado.edu\n", + service, return_code); + return 0; + } +} + +static long pcibios_entry = 0; +static struct { + unsigned long address; + unsigned short segment; +} pci_indirect = { 0, KERNEL_CS }; + +void NCR53c810_test(void); + +static unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) +{ + unsigned long signature; + unsigned char present_status; + unsigned char major_revision; + unsigned char minor_revision; + int pack; + + if ((pcibios_entry = bios32_service(PCI_SERVICE))) { + pci_indirect.address = pcibios_entry; + + __asm__("lcall (%%edi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:\tshl $8, %%eax\n\t" + "movw %%bx, %%ax" + : "=d" (signature), + "=a" (pack) + : "1" (PCIBIOS_PCI_BIOS_PRESENT), + "D" (&pci_indirect) + : "bx", "cx"); + + present_status = (pack >> 16) & 0xff; + major_revision = (pack >> 8) & 0xff; + minor_revision = pack & 0xff; + if (present_status || (signature != PCI_SIGNATURE)) { + printk ("pcibios_init : %s : BIOS32 Service Directory says PCI BIOS is present,\n" + " but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n" + " and signature of 0x%08lx (%c%c%c%c). mail drew@Colorado.EDU\n", + (signature == PCI_SIGNATURE) ? "WARNING" : "ERROR", + present_status, signature, + (char) (signature >> 0), (char) (signature >> 8), + (char) (signature >> 16), (char) (signature >> 24)); + + if (signature != PCI_SIGNATURE) + pcibios_entry = 0; + } + if (pcibios_entry) { + printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n", + major_revision, minor_revision, pcibios_entry); + } + } + +#if 0 + NCR53c810_test(); +#endif + return memory_start; +} + +int pcibios_present(void) +{ + return pcibios_entry ? 1 : 0; +} + +int pcibios_find_class (unsigned long class_code, unsigned short index, + unsigned char *bus, unsigned char *device_fn) +{ + unsigned long bx; + unsigned long ret; + + __asm__ ("lcall (%%edi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=b" (bx), + "=a" (ret) + : "1" (PCIBIOS_FIND_PCI_CLASS_CODE), + "c" (class_code), + "S" ((int) index), + "D" (&pci_indirect)); + *bus = (bx >> 8) & 0xff; + *device_fn = bx & 0xff; + return (int) (ret & 0xff00) >> 8; +} + + +int pcibios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, unsigned char *device_fn) +{ + unsigned short bx; + unsigned short ret; + + __asm__("lcall (%%edi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=b" (bx), + "=a" (ret) + : "1" (PCIBIOS_FIND_PCI_DEVICE), + "c" (device_id), + "d" (vendor), + "S" ((int) index), + "D" (&pci_indirect)); + *bus = (bx >> 8) & 0xff; + *device_fn = bx & 0xff; + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_read_config_byte(unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned char *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_BYTE), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_read_config_word (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned short *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_WORD), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_read_config_dword (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned long *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_DWORD), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_write_config_byte (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned char value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_BYTE), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_write_config_word (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned short value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_WORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_write_config_dword (unsigned char bus, + unsigned char device_fn, unsigned char where, unsigned long value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__("lcall (%%esi)\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_DWORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (&pci_indirect)); + return (int) (ret & 0xff00) >> 8; +} + +void NCR53c810_test(void) +{ + unsigned char bus, device_fn; + unsigned short index; + int ret; + unsigned char row, col; + unsigned long val; + + for (index = 0; index < 4; ++index) { + ret = pcibios_find_device ( + (unsigned short) PCI_VENDOR_ID_NCR, + (unsigned short) PCI_DEVICE_ID_NCR_53C810, + index, &bus, &device_fn); + if (ret) + break; + printk ("ncr53c810 : at PCI bus %d, device %d, function %d.", + bus, ((device_fn & 0xf8) >> 3), (device_fn & 7)); + for (row = 0; row < 0x3c; row += 0x10) { + printk ("\n reg 0x%02x ", row); + for (col = 0; col < 0x10; col += 4) { + if (!(ret = pcibios_read_config_dword (bus, device_fn, row+col, &val))) + printk ("0x%08lx ", val); + else + printk ("error 0x%02x ", ret); + } + } + printk ("\n"); + } +} + +char *pcibios_strerror (int error) +{ + static char buf[80]; + + switch (error) { + case PCIBIOS_SUCCESSFUL: + return "SUCCESSFUL"; + + case PCIBIOS_FUNC_NOT_SUPPORTED: + return "FUNC_NOT_SUPPORTED"; + + case PCIBIOS_BAD_VENDOR_ID: + return "SUCCESSFUL"; + + case PCIBIOS_DEVICE_NOT_FOUND: + return "DEVICE_NOT_FOUND"; + + case PCIBIOS_BAD_REGISTER_NUMBER: + return "BAD_REGISTER_NUMBER"; + + default: + sprintf (buf, "UNKNOWN RETURN 0x%x", error); + return buf; + } +} + + +/* Recognize multi-function device */ + +int multi_function(unsigned char bus,unsigned char dev_fn) +{ + unsigned char header; + pcibios_read_config_byte( + bus, dev_fn, (unsigned char) PCI_HEADER_TYPE, &header); + return (header&7==7); +} + +/* Returns Interrupt register */ + +int interrupt_decod(unsigned char bus,unsigned char dev_fn) +{ + unsigned char interrupt; + pcibios_read_config_byte( + bus, dev_fn, (unsigned char) PCI_INTERRUPT_LINE, &interrupt); + if (interrupt>16) return 0; + return interrupt; +} + +/* probe for being bist capable */ + +int bist_probe(unsigned char bus,unsigned char dev_fn) +{ + unsigned char bist; + pcibios_read_config_byte( + bus, dev_fn, (unsigned char) PCI_BIST, &bist); + return (bist & PCI_BIST_CAPABLE !=0); +} + + +/* Get the chip revision */ + +int revision_decode(unsigned char bus,unsigned char dev_fn) +{ + unsigned char revision; + pcibios_read_config_byte( + bus, dev_fn, (unsigned char) PCI_CLASS_REVISION, &revision); + return revision; +} + + + +/* Gives the Class code using the 16 higher bits */ +/* of the PCI_CLASS_REVISION configuration register */ + +int class_decode(unsigned char bus,unsigned char dev_fn) +{ + struct pci_class_type pci_class[PCI_CLASS_NUM+1] = PCI_CLASS_TYPE; + int i; + unsigned long class; + pcibios_read_config_dword( + bus, dev_fn, (unsigned char) PCI_CLASS_REVISION, &class); + class=class >> 16; + for (i=0;i> 3), + (int) (dev_fn & 7)); + info(bus,dev_fn); + } + } +} + + + + + + + +void probe_pci(void) +{ + if (pcibios_present()==0) printk("ProbePci PCI bios not detected.\n"); + else { + printk( "Probing PCI hardware.\n"); + probe_devices(0); + } +} + +#endif + +unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) +{ + union bios32 *check; + unsigned char sum; + int i, length; + + /* + * Follow the standard procedure for locating the BIOS32 Service + * directory by scanning the permissible address range from + * 0xe0000 through 0xfffff for a valid BIOS32 structure. + * + * The PCI BIOS doesn't seem to work too well on many machines, + * so we disable this unless it's really needed (NCR SCSI driver) + */ + + for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) { + if (check->fields.signature != BIOS32_SIGNATURE) + continue; + length = check->fields.length * 16; + if (!length) + continue; + sum = 0; + for (i = 0; i < length ; ++i) + sum += check->chars[i]; + if (sum != 0) + continue; + if (check->fields.revision != 0) { + printk("bios32_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n", + check->fields.revision, check); + continue; + } + printk ("bios32_init : BIOS32 Service Directory structure at 0x%p\n", check); + if (!bios32_entry) { + bios32_indirect.address = bios32_entry = check->fields.entry; + printk ("bios32_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); + } else { + printk ("bios32_init : multiple entries, mail drew@colorado.edu\n"); + /* + * Jeremy Fitzhardinge reports at least one PCI BIOS + * with two different service directories, and as both + * worked for him, we'll just mention the fact, and + * not actually disallow it.. + */ +#if 0 + return memory_start; +#endif + } + } +#ifdef CONFIG_PCI + if (bios32_entry) { + memory_start = pcibios_init (memory_start, memory_end); + probe_pci(); + } +#endif + return memory_start; +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S --- v1.1.76/linux/arch/i386/kernel/entry.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/entry.S Tue Jan 3 15:15:08 1995 @@ -0,0 +1,544 @@ +/* + * linux/arch/i386/entry.S + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * entry.S contains the system-call and fault low-level handling routines. + * This also contains the timer-interrupt handler, as well as all interrupts + * and faults that can result in a task-switch. + * + * NOTE: This code handles signal-recognition, which happens every time + * after a timer-interrupt and after each system call. + * + * I changed all the .align's to 4 (16 byte alignment), as that's faster + * on a 486. + * + * Stack layout in 'ret_from_system_call': + * ptrace needs to have all regs on the stack. + * if the order here is changed, it needs to be + * updated in fork.c:copy_process, signal.c:do_signal, + * ptrace.c and ptrace.h + * + * 0(%esp) - %ebx + * 4(%esp) - %ecx + * 8(%esp) - %edx + * C(%esp) - %esi + * 10(%esp) - %edi + * 14(%esp) - %ebp + * 18(%esp) - %eax + * 1C(%esp) - %ds + * 20(%esp) - %es + * 24(%esp) - %fs + * 28(%esp) - %gs + * 2C(%esp) - orig_eax + * 30(%esp) - %eip + * 34(%esp) - %cs + * 38(%esp) - %eflags + * 3C(%esp) - %oldesp + * 40(%esp) - %oldss + */ + +#include +#include + +EBX = 0x00 +ECX = 0x04 +EDX = 0x08 +ESI = 0x0C +EDI = 0x10 +EBP = 0x14 +EAX = 0x18 +DS = 0x1C +ES = 0x20 +FS = 0x24 +GS = 0x28 +ORIG_EAX = 0x2C +EIP = 0x30 +CS = 0x34 +EFLAGS = 0x38 +OLDESP = 0x3C +OLDSS = 0x40 + +CF_MASK = 0x00000001 +IF_MASK = 0x00000200 +NT_MASK = 0x00004000 +VM_MASK = 0x00020000 + +/* + * these are offsets into the task-struct. + */ +state = 0 +counter = 4 +priority = 8 +signal = 12 +blocked = 16 +flags = 20 +errno = 24 +dbgreg6 = 52 +dbgreg7 = 56 +exec_domain = 60 + +ENOSYS = 38 + +.globl _system_call,_lcall7 +.globl _device_not_available, _coprocessor_error +.globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op +.globl _double_fault,_coprocessor_segment_overrun +.globl _invalid_TSS,_segment_not_present,_stack_segment +.globl _general_protection,_reserved +.globl _alignment_check,_page_fault +.globl ret_from_sys_call, _sys_call_table + +#define SAVE_ALL \ + cld; \ + push %gs; \ + push %fs; \ + push %es; \ + push %ds; \ + pushl %eax; \ + pushl %ebp; \ + pushl %edi; \ + pushl %esi; \ + pushl %edx; \ + pushl %ecx; \ + pushl %ebx; \ + movl $(KERNEL_DS),%edx; \ + mov %dx,%ds; \ + mov %dx,%es; \ + movl $(USER_DS),%edx; \ + mov %dx,%fs; + +#define RESTORE_ALL \ + cmpw $(KERNEL_CS),CS(%esp); \ + je 1f; \ + movl _current,%eax; \ + movl dbgreg7(%eax),%ebx; \ + movl %ebx,%db7; \ +1: popl %ebx; \ + popl %ecx; \ + popl %edx; \ + popl %esi; \ + popl %edi; \ + popl %ebp; \ + popl %eax; \ + pop %ds; \ + pop %es; \ + pop %fs; \ + pop %gs; \ + addl $4,%esp; \ + iret + +.align 4 +_lcall7: + pushfl # We get a different stack layout with call gates, + pushl %eax # which has to be cleaned up later.. + SAVE_ALL + movl EIP(%esp),%eax # due to call gates, this is eflags, not eip.. + movl CS(%esp),%edx # this is eip.. + movl EFLAGS(%esp),%ecx # and this is cs.. + movl %eax,EFLAGS(%esp) # + movl %edx,EIP(%esp) # Now we move them to their "normal" places + movl %ecx,CS(%esp) # + movl %esp,%eax + movl _current,%edx + pushl %eax + movl exec_domain(%edx),%edx # Get the execution domain + movl 4(%edx),%edx # Get the lcall7 handler for the domain + call *%edx + popl %eax + jmp ret_from_sys_call + +.align 4 +handle_bottom_half: + pushfl + incl _intr_count + sti + call _do_bottom_half + popfl + decl _intr_count + jmp 9f +.align 4 +reschedule: + pushl $ret_from_sys_call + jmp _schedule +.align 4 +_system_call: + pushl %eax # save orig_eax + SAVE_ALL + movl $-ENOSYS,EAX(%esp) + cmpl $(NR_syscalls),%eax + jae ret_from_sys_call + movl _sys_call_table(,%eax,4),%eax + testl %eax,%eax + je ret_from_sys_call + movl _current,%ebx + andl $~CF_MASK,EFLAGS(%esp) # clear carry - assume no errors + movl $0,errno(%ebx) + movl %db6,%edx + movl %edx,dbgreg6(%ebx) # save current hardware debugging status + testb $0x20,flags(%ebx) # PF_TRACESYS + jne 1f + call *%eax + movl %eax,EAX(%esp) # save the return value + movl errno(%ebx),%edx + negl %edx + je ret_from_sys_call + movl %edx,EAX(%esp) + orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error + jmp ret_from_sys_call +.align 4 +1: call _syscall_trace + movl ORIG_EAX(%esp),%eax + call _sys_call_table(,%eax,4) + movl %eax,EAX(%esp) # save the return value + movl _current,%eax + movl errno(%eax),%edx + negl %edx + je 1f + movl %edx,EAX(%esp) + orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error +1: call _syscall_trace + + .align 4,0x90 +ret_from_sys_call: + cmpl $0,_intr_count + jne 2f +9: movl _bh_mask,%eax + andl _bh_active,%eax + jne handle_bottom_half + movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are + testl $(VM_MASK),%eax # different then + jne 1f + cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ? + je 2f +1: sti + orl $(IF_MASK),%eax # these just try to make sure + andl $~NT_MASK,%eax # the program doesn't do anything + movl %eax,EFLAGS(%esp) # stupid + cmpl $0,_need_resched + jne reschedule + movl _current,%eax + cmpl _task,%eax # task[0] cannot have signals + je 2f + cmpl $0,state(%eax) # state + jne reschedule + cmpl $0,counter(%eax) # counter + je reschedule + movl blocked(%eax),%ecx + movl %ecx,%ebx # save blocked in %ebx for signal handling + notl %ecx + andl signal(%eax),%ecx + jne signal_return +2: RESTORE_ALL +.align 4 +signal_return: + movl %esp,%ecx + pushl %ecx + testl $(VM_MASK),EFLAGS(%ecx) + jne v86_signal_return + pushl %ebx + call _do_signal + popl %ebx + popl %ebx + RESTORE_ALL +.align 4 +v86_signal_return: + call _save_v86_state + movl %eax,%esp + pushl %eax + pushl %ebx + call _do_signal + popl %ebx + popl %ebx + RESTORE_ALL + +.align 4 +_divide_error: + pushl $0 # no error code + pushl $_do_divide_error +.align 4,0x90 +error_code: + push %fs + push %es + push %ds + pushl %eax + pushl %ebp + pushl %edi + pushl %esi + pushl %edx + pushl %ecx + pushl %ebx + movl $0,%eax + movl %eax,%db7 # disable hardware debugging... + cld + movl $-1, %eax + xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. ) + xorl %ebx,%ebx # zero ebx + mov %gs,%bx # get the lower order bits of gs + xchgl %ebx, GS(%esp) # get the address and save gs. + pushl %eax # push the error code + lea 4(%esp),%edx + pushl %edx + movl $(KERNEL_DS),%edx + mov %dx,%ds + mov %dx,%es + movl $(USER_DS),%edx + mov %dx,%fs + pushl %eax + movl _current,%eax + movl %db6,%edx + movl %edx,dbgreg6(%eax) # save current hardware debugging status + popl %eax + call *%ebx + addl $8,%esp + jmp ret_from_sys_call + +.align 4 +_coprocessor_error: + pushl $0 + pushl $_do_coprocessor_error + jmp error_code + +.align 4 +_device_not_available: + pushl $-1 # mark this as an int + SAVE_ALL + pushl $ret_from_sys_call + movl %cr0,%eax + testl $0x4,%eax # EM (math emulation bit) + je _math_state_restore + pushl $0 # temporary storage for ORIG_EIP + call _math_emulate + addl $4,%esp + ret + +.align 4 +_debug: + pushl $0 + pushl $_do_debug + jmp error_code + +.align 4 +_nmi: + pushl $0 + pushl $_do_nmi + jmp error_code + +.align 4 +_int3: + pushl $0 + pushl $_do_int3 + jmp error_code + +.align 4 +_overflow: + pushl $0 + pushl $_do_overflow + jmp error_code + +.align 4 +_bounds: + pushl $0 + pushl $_do_bounds + jmp error_code + +.align 4 +_invalid_op: + pushl $0 + pushl $_do_invalid_op + jmp error_code + +.align 4 +_coprocessor_segment_overrun: + pushl $0 + pushl $_do_coprocessor_segment_overrun + jmp error_code + +.align 4 +_reserved: + pushl $0 + pushl $_do_reserved + jmp error_code + +.align 4 +_double_fault: + pushl $_do_double_fault + jmp error_code + +.align 4 +_invalid_TSS: + pushl $_do_invalid_TSS + jmp error_code + +.align 4 +_segment_not_present: + pushl $_do_segment_not_present + jmp error_code + +.align 4 +_stack_segment: + pushl $_do_stack_segment + jmp error_code + +.align 4 +_general_protection: + pushl $_do_general_protection + jmp error_code + +.align 4 +_alignment_check: + pushl $_do_alignment_check + jmp error_code + +.align 4 +_page_fault: + pushl $_do_page_fault + jmp error_code + +.data +.align 4 +_sys_call_table: + .long _sys_setup /* 0 */ + .long _sys_exit + .long _sys_fork + .long _sys_read + .long _sys_write + .long _sys_open /* 5 */ + .long _sys_close + .long _sys_waitpid + .long _sys_creat + .long _sys_link + .long _sys_unlink /* 10 */ + .long _sys_execve + .long _sys_chdir + .long _sys_time + .long _sys_mknod + .long _sys_chmod /* 15 */ + .long _sys_chown + .long _sys_break + .long _sys_stat + .long _sys_lseek + .long _sys_getpid /* 20 */ + .long _sys_mount + .long _sys_umount + .long _sys_setuid + .long _sys_getuid + .long _sys_stime /* 25 */ + .long _sys_ptrace + .long _sys_alarm + .long _sys_fstat + .long _sys_pause + .long _sys_utime /* 30 */ + .long _sys_stty + .long _sys_gtty + .long _sys_access + .long _sys_nice + .long _sys_ftime /* 35 */ + .long _sys_sync + .long _sys_kill + .long _sys_rename + .long _sys_mkdir + .long _sys_rmdir /* 40 */ + .long _sys_dup + .long _sys_pipe + .long _sys_times + .long _sys_prof + .long _sys_brk /* 45 */ + .long _sys_setgid + .long _sys_getgid + .long _sys_signal + .long _sys_geteuid + .long _sys_getegid /* 50 */ + .long _sys_acct + .long _sys_phys + .long _sys_lock + .long _sys_ioctl + .long _sys_fcntl /* 55 */ + .long _sys_mpx + .long _sys_setpgid + .long _sys_ulimit + .long _sys_olduname + .long _sys_umask /* 60 */ + .long _sys_chroot + .long _sys_ustat + .long _sys_dup2 + .long _sys_getppid + .long _sys_getpgrp /* 65 */ + .long _sys_setsid + .long _sys_sigaction + .long _sys_sgetmask + .long _sys_ssetmask + .long _sys_setreuid /* 70 */ + .long _sys_setregid + .long _sys_sigsuspend + .long _sys_sigpending + .long _sys_sethostname + .long _sys_setrlimit /* 75 */ + .long _sys_getrlimit + .long _sys_getrusage + .long _sys_gettimeofday + .long _sys_settimeofday + .long _sys_getgroups /* 80 */ + .long _sys_setgroups + .long _sys_select + .long _sys_symlink + .long _sys_lstat + .long _sys_readlink /* 85 */ + .long _sys_uselib + .long _sys_swapon + .long _sys_reboot + .long _sys_readdir + .long _sys_mmap /* 90 */ + .long _sys_munmap + .long _sys_truncate + .long _sys_ftruncate + .long _sys_fchmod + .long _sys_fchown /* 95 */ + .long _sys_getpriority + .long _sys_setpriority + .long _sys_profil + .long _sys_statfs + .long _sys_fstatfs /* 100 */ + .long _sys_ioperm + .long _sys_socketcall + .long _sys_syslog + .long _sys_setitimer + .long _sys_getitimer /* 105 */ + .long _sys_newstat + .long _sys_newlstat + .long _sys_newfstat + .long _sys_uname + .long _sys_iopl /* 110 */ + .long _sys_vhangup + .long _sys_idle + .long _sys_vm86 + .long _sys_wait4 + .long _sys_swapoff /* 115 */ + .long _sys_sysinfo + .long _sys_ipc + .long _sys_fsync + .long _sys_sigreturn + .long _sys_clone /* 120 */ + .long _sys_setdomainname + .long _sys_newuname + .long _sys_modify_ldt + .long _sys_adjtimex + .long _sys_mprotect /* 125 */ + .long _sys_sigprocmask + .long _sys_create_module + .long _sys_init_module + .long _sys_delete_module + .long _sys_get_kernel_syms /* 130 */ + .long _sys_quotactl + .long _sys_getpgid + .long _sys_fchdir + .long _sys_bdflush + .long _sys_sysfs /* 135 */ + .long _sys_personality + .long 0 /* for afs_syscall */ + .long _sys_setfsuid + .long _sys_setfsgid + .long _sys_llseek /* 140 */ + .space (NR_syscalls-140)*4 diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S --- v1.1.76/linux/arch/i386/kernel/head.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/head.S Mon Jan 2 15:19:59 1995 @@ -0,0 +1,362 @@ +/* + * linux/arch/i386/head.S + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * head.S contains the 32-bit startup code. + */ + +.text +.globl _idt,_gdt, +.globl _swapper_pg_dir,_pg0 +.globl _empty_bad_page +.globl _empty_bad_page_table +.globl _empty_zero_page +.globl _floppy_track_buffer + +#define __ASSEMBLY__ +#include +#include +#include + +#define CL_MAGIC_ADDR 0x90020 +#define CL_MAGIC 0xA33F +#define CL_BASE_ADDR 0x90000 +#define CL_OFFSET 0x90022 + +/* + * swapper_pg_dir is the main page directory, address 0x00001000 (or at + * address 0x00101000 for a compressed boot). + */ +startup_32: + cld + movl $(KERNEL_DS),%eax + mov %ax,%ds + mov %ax,%es + mov %ax,%fs + mov %ax,%gs + lss stack_start,%esp +/* + * Clear BSS first so that there are no surprises... + */ + xorl %eax,%eax + movl $__edata,%edi + movl $__end,%ecx + subl %edi,%ecx + cld + rep + stosb +/* + * start system 32-bit setup. We need to re-do some of the things done + * in 16-bit mode for the "real" operations. + */ + call setup_idt + xorl %eax,%eax +1: incl %eax # check that A20 really IS enabled + movl %eax,0x000000 # loop forever if it isn't + cmpl %eax,0x100000 + je 1b +/* + * Initialize eflags. Some BIOS's leave bits like NT set. This would + * confuse the debugger if this code is traced. + * XXX - best to initialize before switching to protected mode. + */ + pushl $0 + popfl +/* + * Copy bootup parameters out of the way. First 2kB of + * _empty_zero_page is for boot parameters, second 2kB + * is for the command line. + */ + movl $0x90000,%esi + movl $_empty_zero_page,%edi + movl $512,%ecx + cld + rep + movsl + xorl %eax,%eax + movl $512,%ecx + rep + stosl + cmpw $(CL_MAGIC),CL_MAGIC_ADDR + jne 1f + movl $_empty_zero_page+2048,%edi + movzwl CL_OFFSET,%esi + addl $(CL_BASE_ADDR),%esi + movl $2048,%ecx + rep + movsb +1: +/* check if it is 486 or 386. */ +/* + * XXX - this does a lot of unnecessary setup. Alignment checks don't + * apply at our cpl of 0 and the stack ought to be aligned already, and + * we don't need to preserve eflags. + */ + movl $3,_x86 + pushfl # push EFLAGS + popl %eax # get EFLAGS + movl %eax,%ecx # save original EFLAGS + xorl $0x40000,%eax # flip AC bit in EFLAGS + pushl %eax # copy to EFLAGS + popfl # set EFLAGS + pushfl # get new EFLAGS + popl %eax # put it in eax + xorl %ecx,%eax # change in flags + andl $0x40000,%eax # check if AC bit changed + je is386 + movl $4,_x86 + movl %ecx,%eax + xorl $0x200000,%eax # check ID flag + pushl %eax + popfl # if we are on a straight 486DX, SX, or + pushfl # 487SX we can't change it + popl %eax + xorl %ecx,%eax + andl $0x200000,%eax + je is486 +isnew: pushl %ecx # restore original EFLAGS + popfl + /* get processor type */ + movl $1, %eax # Use the CPUID instruction to + .byte 0x0f, 0xa2 # check the processor type + movb %al, %cl # save reg for future use + andb $0x0f,%ah # mask processor family + movb %ah, _x86 + andb $0xf0, %eax # mask model + shrb $4, %al + movb %al, _x86_model + andb $0x0f, %cl # mask mask revision + movb %cl, _x86_mask + movl %edx, _x86_capability + /* get vendor info */ + xorl %eax, %eax # call CPUID with 0 -> return vendor ID + .byte 0x0f, 0xa2 # CPUID + movl %ebx, _x86_vendor_id # lo 4 chars + movl %edx, _x86_vendor_id+4 # next 4 chars + movl %ecx, _x86_vendor_id+8 # last 4 chars + + movl %cr0,%eax # 486+ + andl $0x80000011,%eax # Save PG,PE,ET + orl $0x50022,%eax # set AM, WP, NE and MP + jmp 2f +is486: pushl %ecx # restore original EFLAGS + popfl + movl %cr0,%eax # 486 + andl $0x80000011,%eax # Save PG,PE,ET + orl $0x50022,%eax # set AM, WP, NE and MP + jmp 2f +is386: pushl %ecx # restore original EFLAGS + popfl + movl %cr0,%eax # 386 + andl $0x80000011,%eax # Save PG,PE,ET + orl $2,%eax # set MP +2: movl %eax,%cr0 + call check_x87 + call setup_paging + lgdt gdt_descr + lidt idt_descr + ljmp $(KERNEL_CS),$1f +1: movl $(KERNEL_DS),%eax # reload all the segment registers + mov %ax,%ds # after changing gdt. + mov %ax,%es + mov %ax,%fs + mov %ax,%gs + lss stack_start,%esp + xorl %eax,%eax + lldt %ax + pushl %eax # These are the parameters to main :-) + pushl %eax + pushl %eax + cld # gcc2 wants the direction flag cleared at all times + call _start_kernel +L6: + jmp L6 # main should never return here, but + # just in case, we know what happens. + +/* + * We depend on ET to be correct. This checks for 287/387. + */ +check_x87: + movb $0,_hard_math + clts + fninit + fstsw %ax + cmpb $0,%al + je 1f + movl %cr0,%eax /* no coprocessor: have to set bits */ + xorl $4,%eax /* set EM */ + movl %eax,%cr0 + ret +.align 2 +1: movb $1,_hard_math + .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ + ret + +/* + * setup_idt + * + * sets up a idt with 256 entries pointing to + * ignore_int, interrupt gates. It doesn't actually load + * idt - that can be done only after paging has been enabled + * and the kernel moved to 0xC0000000. Interrupts + * are enabled elsewhere, when we can be relatively + * sure everything is ok. + */ +setup_idt: + lea ignore_int,%edx + movl $(KERNEL_CS << 16),%eax + movw %dx,%ax /* selector = 0x0010 = cs */ + movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ + + lea _idt,%edi + mov $256,%ecx +rp_sidt: + movl %eax,(%edi) + movl %edx,4(%edi) + addl $8,%edi + dec %ecx + jne rp_sidt + ret + + +/* + * Setup_paging + * + * This routine sets up paging by setting the page bit + * in cr0. The page tables are set up, identity-mapping + * the first 4MB. The rest are initialized later. + * + * (ref: added support for up to 32mb, 17Apr92) -- Rik Faith + * (ref: update, 25Sept92) -- croutons@crunchy.uucp + * (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit) + */ +.align 2 +setup_paging: + movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */ + xorl %eax,%eax + movl $_swapper_pg_dir,%edi /* swapper_pg_dir is at 0x1000 */ + cld;rep;stosl +/* Identity-map the kernel in low 4MB memory for ease of transition */ + movl $_pg0+7,_swapper_pg_dir /* set present bit/user r/w */ +/* But the real place is at 0xC0000000 */ + movl $_pg0+7,_swapper_pg_dir+3072 /* set present bit/user r/w */ + movl $_pg0+4092,%edi + movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */ + std +1: stosl /* fill the page backwards - more efficient :-) */ + subl $0x1000,%eax + jge 1b + cld + movl $_swapper_pg_dir,%eax + movl %eax,%cr3 /* cr3 - page directory start */ + movl %cr0,%eax + orl $0x80000000,%eax + movl %eax,%cr0 /* set paging (PG) bit */ + ret /* this also flushes the prefetch-queue */ + +/* + * page 0 is made non-existent, so that kernel NULL pointer references get + * caught. Thus the swapper page directory has been moved to 0x1000 + * + * XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte, + * with the introduction of the compressed boot code. Theoretically, + * the original design of overlaying the startup code with the swapper + * page directory is still possible --- it would reduce the size of the kernel + * by 2-3k. This would be a good thing to do at some point..... + */ +.org 0x1000 +_swapper_pg_dir: +/* + * The page tables are initialized to only 4MB here - the final page + * tables are set up later depending on memory size. + */ +.org 0x2000 +_pg0: + +.org 0x3000 +_empty_bad_page: + +.org 0x4000 +_empty_bad_page_table: + +.org 0x5000 +_empty_zero_page: + +.org 0x6000 +/* + * floppy_track_buffer is used to buffer one track of floppy data: it + * has to be separate from the tmp_floppy area, as otherwise a single- + * sector read/write can mess it up. It can contain one full cylinder (sic) of + * data (36*2*512 bytes). + */ +_floppy_track_buffer: + .fill 512*2*MAX_BUFFER_SECTORS,1,0 + +stack_start: + .long _init_user_stack+4096 + .long KERNEL_DS + +/* This is the default interrupt "handler" :-) */ +int_msg: + .asciz "Unknown interrupt\n" +.align 2 +ignore_int: + cld + pushl %eax + pushl %ecx + pushl %edx + push %ds + push %es + push %fs + movl $(KERNEL_DS),%eax + mov %ax,%ds + mov %ax,%es + mov %ax,%fs + pushl $int_msg + call _printk + popl %eax + pop %fs + pop %es + pop %ds + popl %edx + popl %ecx + popl %eax + iret + +/* + * The interrupt descriptor table has room for 256 idt's + */ +.align 4 +.word 0 +idt_descr: + .word 256*8-1 # idt contains 256 entries + .long 0xc0000000+_idt + +.align 4 +_idt: + .fill 256,8,0 # idt is uninitialized + +.align 4 +.word 0 +gdt_descr: + .word (8+2*NR_TASKS)*8-1 + .long 0xc0000000+_gdt + +/* + * This gdt setup gives the kernel a 1GB address space at virtual + * address 0xC0000000 - space enough for expansion, I hope. + */ +.align 4 +_gdt: + .quad 0x0000000000000000 /* NULL descriptor */ + .quad 0x0000000000000000 /* not used */ + .quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */ + .quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */ + .quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */ + .quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */ + .quad 0x0000000000000000 /* not used */ + .quad 0x0000000000000000 /* not used */ + .fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */ diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/ioport.c linux/arch/i386/kernel/ioport.c --- v1.1.76/linux/arch/i386/kernel/ioport.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/ioport.c Sat Jan 7 12:58:20 1995 @@ -0,0 +1,268 @@ +/* + * linux/kernel/ioport.c + * + * This contains the io-permission bitmap code - written by obz, with changes + * by Linus. + */ + +#include +#include +#include +#include +#include + +static unsigned long ioport_registrar[IO_BITMAP_SIZE] = {0, /* ... */}; +#define IOPORTNAMES_NUM 32 +#define IOPORTNAMES_LEN 26 +struct { int from; + int num; + int flags; + char name[IOPORTNAMES_LEN]; + } ioportnames[IOPORTNAMES_NUM]; + +#define _IODEBUG + +#ifdef IODEBUG +static char * ios(unsigned long l) +{ + static char str[33] = { '\0' }; + int i; + unsigned long mask; + + for (i = 0, mask = 0x80000000; i < 32; ++i, mask >>= 1) + str[i] = (l & mask) ? '1' : '0'; + return str; +} + +static void dump_io_bitmap(void) +{ + int i, j; + int numl = sizeof(current->tss.io_bitmap) >> 2; + + for (i = j = 0; j < numl; ++i) + { + printk("%4d [%3x]: ", 64*i, 64*i); + printk("%s ", ios(current->tss.io_bitmap[j++])); + if (j < numl) + printk("%s", ios(current->tss.io_bitmap[j++])); + printk("\n"); + } +} +#endif + +/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ +asmlinkage void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value) +{ + int mask; + unsigned long *bitmap_base = bitmap + (base >> 5); + unsigned short low_index = base & 0x1f; + int length = low_index + extent; + + if (low_index != 0) { + mask = (~0 << low_index); + if (length < 32) + mask &= ~(~0 << length); + if (new_value) + *bitmap_base++ |= mask; + else + *bitmap_base++ &= ~mask; + length -= 32; + } + + mask = (new_value ? ~0 : 0); + while (length >= 32) { + *bitmap_base++ = mask; + length -= 32; + } + + if (length > 0) { + mask = ~(~0 << length); + if (new_value) + *bitmap_base++ |= mask; + else + *bitmap_base++ &= ~mask; + } +} + +/* Check for set bits in BITMAP starting at BASE, going to EXTENT. */ +asmlinkage int check_bitmap(unsigned long *bitmap, short base, short extent) +{ + int mask; + unsigned long *bitmap_base = bitmap + (base >> 5); + unsigned short low_index = base & 0x1f; + int length = low_index + extent; + + if (low_index != 0) { + mask = (~0 << low_index); + if (length < 32) + mask &= ~(~0 << length); + if (*bitmap_base++ & mask) + return 1; + length -= 32; + } + while (length >= 32) { + if (*bitmap_base++ != 0) + return 1; + length -= 32; + } + + if (length > 0) { + mask = ~(~0 << length); + if (*bitmap_base++ & mask) + return 1; + } + return 0; +} + +/* + * This generates the report for /proc/ioports + */ +int get_ioport_list(char *buf) +{ int i=0,len=0; + while(i=4000) + len+=sprintf(buf+len,"4k-Limit reached!\n"); + return len; +} + +/* + * this changes the io permissions bitmap in the current task. + */ +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) +{ + if (from + num <= from) + return -EINVAL; + if (from + num > IO_BITMAP_SIZE*32) + return -EINVAL; + if (!suser()) + return -EPERM; + +#ifdef IODEBUG + printk("io: from=%d num=%d %s\n", from, num, (turn_on ? "on" : "off")); +#endif + set_bitmap((unsigned long *)current->tss.io_bitmap, from, num, !turn_on); + return 0; +} + +unsigned int *stack; + +/* + * sys_iopl has to be used when you want to access the IO ports + * beyond the 0x3ff range: to get the full 65536 ports bitmapped + * you'd need 8kB of bitmaps/process, which is a bit excessive. + * + * Here we just change the eflags value on the stack: we allow + * only the super-user to do it. This depends on the stack-layout + * on system-call entry - see also fork() and the signal handling + * code. + */ +asmlinkage int sys_iopl(long ebx,long ecx,long edx, + long esi, long edi, long ebp, long eax, long ds, + long es, long fs, long gs, long orig_eax, + long eip,long cs,long eflags,long esp,long ss) +{ + unsigned int level = ebx; + + if (level > 3) + return -EINVAL; + if (!suser()) + return -EPERM; + *(&eflags) = (eflags & 0xffffcfff) | (level << 12); + return 0; +} + +/* + * This is the 'old' snarfing worker function + */ +void do_snarf_region(unsigned int from, unsigned int num) +{ + if (from > IO_BITMAP_SIZE*32) + return; + if (from + num > IO_BITMAP_SIZE*32) + num = IO_BITMAP_SIZE*32 - from; + set_bitmap(ioport_registrar, from, num, 1); + return; +} + +/* + * Call this from the device driver to register the ioport region. + */ +void register_iomem(unsigned int from, unsigned int num, char *name) +{ + int i=0; + while(ioportnames[i].flags && i IO_BITMAP_SIZE*32) + return; + if (from + num > IO_BITMAP_SIZE*32) + num = IO_BITMAP_SIZE*32 - from; + set_bitmap(ioport_registrar, from, num, 0); + return; +} + +/* + * Call this when the device driver is unloaded + */ +void release_region(unsigned int from, unsigned int num) +{ int i=0; + while(i IO_BITMAP_SIZE*32) + return 0; + if (from + num > IO_BITMAP_SIZE*32) + num = IO_BITMAP_SIZE*32 - from; + return check_bitmap(ioport_registrar, from, num); +} + +/* Called from init/main.c to reserve IO ports. */ +void reserve_setup(char *str, int *ints) +{ + int i; + + for (i = 1; i < ints[0]; i += 2) + register_iomem(ints[i], ints[i+1],"reserved"); +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c --- v1.1.76/linux/arch/i386/kernel/irq.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/irq.c Wed Jan 4 21:16:04 1995 @@ -0,0 +1,370 @@ +/* + * linux/kernel/irq.c + * + * Copyright (C) 1992 Linus Torvalds + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +/* + * IRQ's are in fact implemented a bit like signal handlers for the kernel. + * The same sigaction struct is used, and with similar semantics (ie there + * is a SA_INTERRUPT flag etc). Naturally it's not a 1:1 relation, but there + * are similarities. + * + * sa_handler(int irq_NR) is the default function called (0 if no). + * sa_mask is horribly ugly (I won't even mention it) + * sa_flags contains various info: SA_INTERRUPT etc + * sa_restorer is the unused + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define CR0_NE 32 + +static unsigned char cache_21 = 0xff; +static unsigned char cache_A1 = 0xff; + +void disable_irq(unsigned int irq_nr) +{ + unsigned long flags; + unsigned char mask; + + mask = 1 << (irq_nr & 7); + save_flags(flags); + if (irq_nr < 8) { + cli(); + cache_21 |= mask; + outb(cache_21,0x21); + restore_flags(flags); + return; + } + cli(); + cache_A1 |= mask; + outb(cache_A1,0xA1); + restore_flags(flags); +} + +void enable_irq(unsigned int irq_nr) +{ + unsigned long flags; + unsigned char mask; + + mask = ~(1 << (irq_nr & 7)); + save_flags(flags); + if (irq_nr < 8) { + cli(); + cache_21 &= mask; + outb(cache_21,0x21); + restore_flags(flags); + return; + } + cli(); + cache_A1 &= mask; + outb(cache_A1,0xA1); + restore_flags(flags); +} + +/* + * This builds up the IRQ handler stubs using some ugly macros in irq.h + * + * These macros create the low-level assembly IRQ routines that do all + * the operations that are needed to keep the AT interrupt-controller + * happy. They are also written to be fast - and to disable interrupts + * as little as humanly possible. + * + * NOTE! These macros expand to three different handlers for each line: one + * complete handler that does all the fancy stuff (including signal handling), + * and one fast handler that is meant for simple IRQ's that want to be + * atomic. The specific handler is chosen depending on the SA_INTERRUPT + * flag when installing a handler. Finally, one "bad interrupt" handler, that + * is used when no handler is present. + */ +BUILD_IRQ(FIRST,0,0x01) +BUILD_IRQ(FIRST,1,0x02) +BUILD_IRQ(FIRST,2,0x04) +BUILD_IRQ(FIRST,3,0x08) +BUILD_IRQ(FIRST,4,0x10) +BUILD_IRQ(FIRST,5,0x20) +BUILD_IRQ(FIRST,6,0x40) +BUILD_IRQ(FIRST,7,0x80) +BUILD_IRQ(SECOND,8,0x01) +BUILD_IRQ(SECOND,9,0x02) +BUILD_IRQ(SECOND,10,0x04) +BUILD_IRQ(SECOND,11,0x08) +BUILD_IRQ(SECOND,12,0x10) +BUILD_IRQ(SECOND,13,0x20) +BUILD_IRQ(SECOND,14,0x40) +BUILD_IRQ(SECOND,15,0x80) + +/* + * Pointers to the low-level handlers: first the general ones, then the + * fast ones, then the bad ones. + */ +static void (*interrupt[16])(void) = { + IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt, + IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt, + IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt, + IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt +}; + +static void (*fast_interrupt[16])(void) = { + fast_IRQ0_interrupt, fast_IRQ1_interrupt, + fast_IRQ2_interrupt, fast_IRQ3_interrupt, + fast_IRQ4_interrupt, fast_IRQ5_interrupt, + fast_IRQ6_interrupt, fast_IRQ7_interrupt, + fast_IRQ8_interrupt, fast_IRQ9_interrupt, + fast_IRQ10_interrupt, fast_IRQ11_interrupt, + fast_IRQ12_interrupt, fast_IRQ13_interrupt, + fast_IRQ14_interrupt, fast_IRQ15_interrupt +}; + +static void (*bad_interrupt[16])(void) = { + bad_IRQ0_interrupt, bad_IRQ1_interrupt, + bad_IRQ2_interrupt, bad_IRQ3_interrupt, + bad_IRQ4_interrupt, bad_IRQ5_interrupt, + bad_IRQ6_interrupt, bad_IRQ7_interrupt, + bad_IRQ8_interrupt, bad_IRQ9_interrupt, + bad_IRQ10_interrupt, bad_IRQ11_interrupt, + bad_IRQ12_interrupt, bad_IRQ13_interrupt, + bad_IRQ14_interrupt, bad_IRQ15_interrupt +}; + +/* + * Initial irq handlers. + */ +static struct sigaction irq_sigaction[16] = { + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL } +}; + +int get_irq_list(char *buf) +{ + int i, len = 0; + struct sigaction * sa = irq_sigaction; + + for (i = 0 ; i < 16 ; i++, sa++) { + if (!sa->sa_handler) + continue; + len += sprintf(buf+len, "%2d: %8d %c %s\n", + i, kstat.interrupts[i], + (sa->sa_flags & SA_INTERRUPT) ? '+' : ' ', + (char *) sa->sa_mask); + } + return len; +} + +/* + * do_IRQ handles IRQ's that have been installed without the + * SA_INTERRUPT flag: it uses the full signal-handling return + * and runs with other interrupts enabled. All relatively slow + * IRQ's should use this format: notably the keyboard/timer + * routines. + */ +asmlinkage void do_IRQ(int irq, struct pt_regs * regs) +{ + struct sigaction * sa = irq + irq_sigaction; + + kstat.interrupts[irq]++; + sa->sa_handler((int) regs); +} + +/* + * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return + * stuff - the handler is also running with interrupts disabled unless + * it explicitly enables them later. + */ +asmlinkage void do_fast_IRQ(int irq) +{ + struct sigaction * sa = irq + irq_sigaction; + + kstat.interrupts[irq]++; + sa->sa_handler(irq); +} + +#define SA_PROBE SA_ONESHOT + +/* + * Using "struct sigaction" is slightly silly, but there + * are historical reasons and it works well, so.. + */ +static int irqaction(unsigned int irq, struct sigaction * new_sa) +{ + struct sigaction * sa; + unsigned long flags; + + if (irq > 15) + return -EINVAL; + sa = irq + irq_sigaction; + if (sa->sa_handler) + return -EBUSY; + if (!new_sa->sa_handler) + return -EINVAL; + save_flags(flags); + cli(); + *sa = *new_sa; + if (!(sa->sa_flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */ + if (sa->sa_flags & SA_INTERRUPT) + set_intr_gate(0x20+irq,fast_interrupt[irq]); + else + set_intr_gate(0x20+irq,interrupt[irq]); + } + if (irq < 8) { + cache_21 &= ~(1< 15) { + printk("Trying to free IRQ%d\n",irq); + return; + } + if (!sa->sa_handler) { + printk("Trying to free free IRQ%d\n",irq); + return; + } + save_flags(flags); + cli(); + if (irq < 8) { + cache_21 |= 1 << irq; + outb(cache_21,0x21); + } else { + cache_A1 |= 1 << (irq-8); + outb(cache_A1,0xA1); + } + set_intr_gate(0x20+irq,bad_interrupt[irq]); + sa->sa_handler = NULL; + sa->sa_flags = 0; + sa->sa_mask = 0; + sa->sa_restorer = NULL; + restore_flags(flags); +} + +/* + * Note that on a 486, we don't want to do a SIGFPE on a irq13 + * as the irq is unreliable, and exception 16 works correctly + * (ie as explained in the intel literature). On a 386, you + * can't use exception 16 due to bad IBM design, so we have to + * rely on the less exact irq13. + * + * Careful.. Not only is IRQ13 unreliable, but it is also + * leads to races. IBM designers who came up with it should + * be shot. + */ +static void math_error_irq(int cpl) +{ + outb(0,0xF0); + if (ignore_irq13 || !hard_math) + return; + math_error(); +} + +static void no_action(int cpl) { } + +unsigned int probe_irq_on (void) +{ + unsigned int i, irqs = 0, irqmask; + unsigned long delay; + + /* first, snaffle up any unassigned irqs */ + for (i = 15; i > 0; i--) { + if (!request_irq(i, no_action, SA_PROBE, "probe")) { + enable_irq(i); + irqs |= (1 << i); + } + } + + /* wait for spurious interrupts to mask themselves out again */ + for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms delay */ + + /* now filter out any obviously spurious interrupts */ + irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; + for (i = 15; i > 0; i--) { + if (irqs & (1 << i) & irqmask) { + irqs ^= (1 << i); + free_irq(i); + } + } +#ifdef DEBUG + printk("probe_irq_on: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); +#endif + return irqs; +} + +int probe_irq_off (unsigned int irqs) +{ + unsigned int i, irqmask; + + irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; + for (i = 15; i > 0; i--) { + if (irqs & (1 << i)) { + free_irq(i); + } + } +#ifdef DEBUG + printk("probe_irq_off: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); +#endif + irqs &= irqmask; + if (!irqs) + return 0; + i = ffz(~irqs); + if (irqs != (irqs & (1 << i))) + i = -i; + return i; +} + +void init_IRQ(void) +{ + int i; + + for (i = 0; i < 16 ; i++) + set_intr_gate(0x20+i,bad_interrupt[i]); + if (request_irq(2, no_action, SA_INTERRUPT, "cascade")) + printk("Unable to get IRQ2 for cascade\n"); + if (request_irq(13,math_error_irq, 0, "math error")) + printk("Unable to get IRQ13 for math-error handler\n"); +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/ldt.c linux/arch/i386/kernel/ldt.c --- v1.1.76/linux/arch/i386/kernel/ldt.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/ldt.c Thu Jan 5 13:55:40 1995 @@ -0,0 +1,102 @@ +/* + * linux/kernel/ldt.c + * + * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include + +static int read_ldt(void * ptr, unsigned long bytecount) +{ + int error; + void * address = current->ldt; + unsigned long size; + + if (!ptr) + return -EINVAL; + size = LDT_ENTRIES*LDT_ENTRY_SIZE; + if (!address) { + address = &default_ldt; + size = sizeof(default_ldt); + } + if (size > bytecount) + size = bytecount; + error = verify_area(VERIFY_WRITE, ptr, size); + if (error) + return error; + memcpy_tofs(ptr, address, size); + return size; +} + +static int write_ldt(void * ptr, unsigned long bytecount) +{ + struct modify_ldt_ldt_s ldt_info; + unsigned long *lp; + unsigned long base, limit; + int error, i; + + if (bytecount != sizeof(ldt_info)) + return -EINVAL; + error = verify_area(VERIFY_READ, ptr, sizeof(ldt_info)); + if (error) + return error; + + memcpy_fromfs(&ldt_info, ptr, sizeof(ldt_info)); + + if (ldt_info.contents == 3 || ldt_info.entry_number >= LDT_ENTRIES) + return -EINVAL; + + limit = ldt_info.limit; + base = ldt_info.base_addr; + if (ldt_info.limit_in_pages) + limit *= PAGE_SIZE; + + limit += base; + if (limit < base || limit >= 0xC0000000) + return -EINVAL; + + if (!current->ldt) { + for (i=1 ; ildt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE))) + return -ENOMEM; + set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, current->ldt, LDT_ENTRIES); + load_ldt(i); + } + } + } + + lp = (unsigned long *) ¤t->ldt[ldt_info.entry_number]; + /* Allow LDTs to be cleared by the user. */ + if (ldt_info.base_addr == 0 && ldt_info.limit == 0) { + *lp = 0; + *(lp+1) = 0; + return 0; + } + *lp = ((ldt_info.base_addr & 0x0000ffff) << 16) | + (ldt_info.limit & 0x0ffff); + *(lp+1) = (ldt_info.base_addr & 0xff000000) | + ((ldt_info.base_addr & 0x00ff0000)>>16) | + (ldt_info.limit & 0xf0000) | + (ldt_info.contents << 10) | + ((ldt_info.read_exec_only ^ 1) << 9) | + (ldt_info.seg_32bit << 22) | + (ldt_info.limit_in_pages << 23) | + ((ldt_info.seg_not_present ^1) << 15) | + 0x7000; + return 0; +} + +asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount) +{ + if (func == 0) + return read_ldt(ptr, bytecount); + if (func == 1) + return write_ldt(ptr, bytecount); + return -ENOSYS; +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c --- v1.1.76/linux/arch/i386/kernel/process.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/process.c Wed Jan 4 21:16:04 1995 @@ -0,0 +1,199 @@ +/* + * linux/arch/i386/kernel/process.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call"); + +/* + * The idle loop on a i386.. + */ +asmlinkage int sys_idle(void) +{ + int i; + + if (current->pid != 0) + return -EPERM; + + /* Map out the low memory: it's no longer needed */ + for (i = 0 ; i < 768 ; i++) + swapper_pg_dir[i] = 0; + + /* endless idle loop with no priority at all */ + current->counter = -100; + for (;;) { + if (hlt_works_ok && !need_resched) + __asm__("hlt"); + schedule(); + } +} + +/* + * Do necessary setup to start up a newly executed thread. + */ +void start_thread(struct pt_regs * regs, unsigned long eip, unsigned long esp) +{ + regs->eip = eip; + regs->esp = esp; +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ + /* forget local segments */ + __asm__ __volatile__("mov %w0,%%fs ; mov %w0,%%gs ; lldt %w0" + : /* no outputs */ + : "r" (0)); + current->tss.ldt = 0; + if (current->ldt) { + void * ldt = current->ldt; + current->ldt = NULL; + vfree(ldt); + } +} + +void flush_thread(void) +{ + int i; + + if (current->ldt) { + free_page((unsigned long) current->ldt); + current->ldt = NULL; + for (i=1 ; idebugreg[i] = 0; +} + +#define IS_CLONE (regs->orig_eax == __NR_clone) + +unsigned long copy_thread(int nr, unsigned long clone_flags, struct task_struct * p, struct pt_regs * regs) +{ + int i; + struct pt_regs * childregs; + + p->tss.es = KERNEL_DS; + p->tss.cs = KERNEL_CS; + p->tss.ss = KERNEL_DS; + p->tss.ds = KERNEL_DS; + p->tss.fs = USER_DS; + p->tss.gs = KERNEL_DS; + p->tss.ss0 = KERNEL_DS; + p->tss.esp0 = p->kernel_stack_page + PAGE_SIZE; + p->tss.tr = _TSS(nr); + childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1; + p->tss.esp = (unsigned long) childregs; + p->tss.eip = (unsigned long) ret_from_sys_call; + *childregs = *regs; + childregs->eax = 0; + p->tss.back_link = 0; + p->tss.eflags = regs->eflags & 0xffffcfff; /* iopl is always 0 for a new process */ + if (IS_CLONE) { + if (regs->ebx) + childregs->esp = regs->ebx; + clone_flags = regs->ecx; + if (childregs->esp == regs->esp) + clone_flags |= COPYVM; + } + p->tss.ldt = _LDT(nr); + if (p->ldt) { + p->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); + if (p->ldt != NULL) + memcpy(p->ldt, current->ldt, LDT_ENTRIES*LDT_ENTRY_SIZE); + } + set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); + if (p->ldt) + set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512); + else + set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1); + p->tss.bitmap = offsetof(struct thread_struct,io_bitmap); + for (i = 0; i < IO_BITMAP_SIZE+1 ; i++) /* IO bitmap is actually SIZE+1 */ + p->tss.io_bitmap[i] = ~0; + if (last_task_used_math == current) + __asm__("clts ; fnsave %0 ; frstor %0":"=m" (p->tss.i387)); + return clone_flags; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + int i; + +/* changed the size calculations - should hopefully work better. lbt */ + dump->magic = CMAGIC; + dump->start_code = 0; + dump->start_stack = regs->esp & ~(PAGE_SIZE - 1); + dump->u_tsize = ((unsigned long) current->mm->end_code) >> 12; + dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> 12; + dump->u_dsize -= dump->u_tsize; + dump->u_ssize = 0; + for (i = 0; i < 8; i++) + dump->u_debugreg[i] = current->debugreg[i]; + + if (dump->start_stack < TASK_SIZE) + dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> 12; + + dump->regs = *regs; + +/* Flag indicating the math stuff is valid. We don't support this for the + soft-float routines yet */ + if (hard_math) { + if ((dump->u_fpvalid = current->used_math) != 0) { + if (last_task_used_math == current) + __asm__("clts ; fnsave %0": :"m" (dump->i387)); + else + memcpy(&dump->i387,¤t->tss.i387.hard,sizeof(dump->i387)); + } + } else { + /* we should dump the emulator state here, but we need to + convert it into standard 387 format first.. */ + dump->u_fpvalid = 0; + } +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(struct pt_regs regs) +{ + int error; + char * filename; + + error = getname((char *) regs.ebx, &filename); + if (error) + return error; + error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, ®s); + putname(filename); + return error; +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c --- v1.1.76/linux/arch/i386/kernel/ptrace.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/ptrace.c Wed Jan 4 21:16:04 1995 @@ -0,0 +1,517 @@ +/* ptrace.c */ +/* By Ross Biro 1/23/92 */ +/* edited by Linus Torvalds */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* determines which flags the user has access to. */ +/* 1 = access 0 = no access */ +#define FLAG_MASK 0x00044dd5 + +/* set's the trap flag. */ +#define TRAP_FLAG 0x100 + +/* + * this is the number to subtract from the top of the stack. To find + * the local frame. + */ +#define MAGICNUMBER 68 + +/* change a pid into a task struct. */ +static inline struct task_struct * get_task(int pid) +{ + int i; + + for (i = 1; i < NR_TASKS; i++) { + if (task[i] != NULL && (task[i]->pid == pid)) + return task[i]; + } + return NULL; +} + +/* + * this routine will get a word off of the processes privileged stack. + * the offset is how far from the base addr as stored in the TSS. + * this routine assumes that all the privileged stacks are in our + * data space. + */ +static inline int get_stack_long(struct task_struct *task, int offset) +{ + unsigned char *stack; + + stack = (unsigned char *)task->tss.esp0; + stack += offset; + return (*((int *)stack)); +} + +/* + * this routine will put a word on the processes privileged stack. + * the offset is how far from the base addr as stored in the TSS. + * this routine assumes that all the privileged stacks are in our + * data space. + */ +static inline int put_stack_long(struct task_struct *task, int offset, + unsigned long data) +{ + unsigned char * stack; + + stack = (unsigned char *) task->tss.esp0; + stack += offset; + *(unsigned long *) stack = data; + return 0; +} + +/* + * This routine gets a long from any process space by following the page + * tables. NOTE! You should check that the long isn't on a page boundary, + * and that it is in the task area before calling this: this routine does + * no checking. + */ +static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr) +{ + unsigned long page; + +repeat: + page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr); + if (page & PAGE_PRESENT) { + page &= PAGE_MASK; + page += PAGE_PTR(addr); + page = *((unsigned long *) page); + } + if (!(page & PAGE_PRESENT)) { + do_no_page(vma, addr, 0); + goto repeat; + } +/* this is a hack for non-kernel-mapped video buffers and similar */ + if (page >= high_memory) + return 0; + page &= PAGE_MASK; + page += addr & ~PAGE_MASK; + return *(unsigned long *) page; +} + +/* + * This routine puts a long into any process space by following the page + * tables. NOTE! You should check that the long isn't on a page boundary, + * and that it is in the task area before calling this: this routine does + * no checking. + * + * Now keeps R/W state of page so that a text page stays readonly + * even if a debugger scribbles breakpoints into it. -M.U- + */ +static void put_long(struct vm_area_struct * vma, unsigned long addr, + unsigned long data) +{ + unsigned long page, pte = 0; + int readonly = 0; + +repeat: + page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr); + if (page & PAGE_PRESENT) { + page &= PAGE_MASK; + page += PAGE_PTR(addr); + pte = page; + page = *((unsigned long *) page); + } + if (!(page & PAGE_PRESENT)) { + do_no_page(vma, addr, 0 /* PAGE_RW */); + goto repeat; + } + if (!(page & PAGE_RW)) { + if (!(page & PAGE_COW)) + readonly = 1; + do_wp_page(vma, addr, PAGE_RW | PAGE_PRESENT); + goto repeat; + } +/* this is a hack for non-kernel-mapped video buffers and similar */ + if (page >= high_memory) + return; +/* we're bypassing pagetables, so we have to set the dirty bit ourselves */ + *(unsigned long *) pte |= (PAGE_DIRTY|PAGE_COW); + page &= PAGE_MASK; + page += addr & ~PAGE_MASK; + *(unsigned long *) page = data; + if (readonly) { + *(unsigned long *) pte &=~ (PAGE_RW|PAGE_COW); + invalidate(); + } +} + +static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long addr) +{ + struct vm_area_struct * vma; + + addr &= PAGE_MASK; + for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) { + if (!vma) + return NULL; + if (vma->vm_end > addr) + break; + } + if (vma->vm_start <= addr) + return vma; + if (!(vma->vm_flags & VM_GROWSDOWN)) + return NULL; + if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur) + return NULL; + vma->vm_offset -= vma->vm_start - addr; + vma->vm_start = addr; + return vma; +} + +/* + * This routine checks the page boundaries, and that the offset is + * within the task area. It then calls get_long() to read a long. + */ +static int read_long(struct task_struct * tsk, unsigned long addr, + unsigned long * result) +{ + struct vm_area_struct * vma = find_vma(tsk, addr); + + if (!vma) + return -EIO; + if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) { + unsigned long low,high; + struct vm_area_struct * vma_high = vma; + + if (addr + sizeof(long) >= vma->vm_end) { + vma_high = vma->vm_next; + if (!vma_high || vma_high->vm_start != vma->vm_end) + return -EIO; + } + low = get_long(vma, addr & ~(sizeof(long)-1)); + high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1)); + switch (addr & (sizeof(long)-1)) { + case 1: + low >>= 8; + low |= high << 24; + break; + case 2: + low >>= 16; + low |= high << 16; + break; + case 3: + low >>= 24; + low |= high << 8; + break; + } + *result = low; + } else + *result = get_long(vma, addr); + return 0; +} + +/* + * This routine checks the page boundaries, and that the offset is + * within the task area. It then calls put_long() to write a long. + */ +static int write_long(struct task_struct * tsk, unsigned long addr, + unsigned long data) +{ + struct vm_area_struct * vma = find_vma(tsk, addr); + + if (!vma) + return -EIO; + if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) { + unsigned long low,high; + struct vm_area_struct * vma_high = vma; + + if (addr + sizeof(long) >= vma->vm_end) { + vma_high = vma->vm_next; + if (!vma_high || vma_high->vm_start != vma->vm_end) + return -EIO; + } + low = get_long(vma, addr & ~(sizeof(long)-1)); + high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1)); + switch (addr & (sizeof(long)-1)) { + case 0: /* shouldn't happen, but safety first */ + low = data; + break; + case 1: + low &= 0x000000ff; + low |= data << 8; + high &= ~0xff; + high |= data >> 24; + break; + case 2: + low &= 0x0000ffff; + low |= data << 16; + high &= ~0xffff; + high |= data >> 16; + break; + case 3: + low &= 0x00ffffff; + low |= data << 24; + high &= ~0xffffff; + high |= data >> 8; + break; + } + put_long(vma, addr & ~(sizeof(long)-1),low); + put_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high); + } else + put_long(vma, addr, data); + return 0; +} + +asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + struct user * dummy; + int i; + + dummy = NULL; + + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->flags & PF_PTRACED) + return -EPERM; + /* set the ptrace bit in the process flags. */ + current->flags |= PF_PTRACED; + return 0; + } + if (pid == 1) /* you may not mess with init */ + return -EPERM; + if (!(child = get_task(pid))) + return -ESRCH; + if (request == PTRACE_ATTACH) { + if (child == current) + return -EPERM; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->gid)) && !suser()) + return -EPERM; + /* the same process cannot be attached many times */ + if (child->flags & PF_PTRACED) + return -EPERM; + child->flags |= PF_PTRACED; + if (child->p_pptr != current) { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + send_sig(SIGSTOP, child, 1); + return 0; + } + if (!(child->flags & PF_PTRACED)) + return -ESRCH; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + return -ESRCH; + } + if (child->p_pptr != current) + return -ESRCH; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int res; + + res = read_long(child, addr, &tmp); + if (res < 0) + return res; + res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); + if (!res) + put_fs_long(tmp,(unsigned long *) data); + return res; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + int res; + + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + return -EIO; + + res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); + if (res) + return res; + tmp = 0; /* Default return condition */ + if(addr < 17*sizeof(long)) { + addr = addr >> 2; /* temporary hack. */ + + tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER); + if (addr == DS || addr == ES || + addr == FS || addr == GS || + addr == CS || addr == SS) + tmp &= 0xffff; + }; + if(addr >= (long) &dummy->u_debugreg[0] && + addr <= (long) &dummy->u_debugreg[7]){ + addr -= (long) &dummy->u_debugreg[0]; + addr = addr >> 2; + tmp = child->debugreg[addr]; + }; + put_fs_long(tmp,(unsigned long *) data); + return 0; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + return write_long(child,addr,data); + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + return -EIO; + + addr = addr >> 2; /* temporary hack. */ + + if (addr == ORIG_EAX) + return -EIO; + if (addr == DS || addr == ES || + addr == FS || addr == GS || + addr == CS || addr == SS) { + data &= 0xffff; + if (data && (data & 3) != 3) + return -EIO; + } + if (addr == EFL) { /* flags. */ + data &= FLAG_MASK; + data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER) & ~FLAG_MASK; + } + /* Do not allow the user to set the debug register for kernel + address space */ + if(addr < 17){ + if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data)) + return -EIO; + return 0; + }; + + /* We need to be very careful here. We implicitly + want to modify a portion of the task_struct, and we + have to be selective about what portions we allow someone + to modify. */ + + addr = addr << 2; /* Convert back again */ + if(addr >= (long) &dummy->u_debugreg[0] && + addr <= (long) &dummy->u_debugreg[7]){ + + if(addr == (long) &dummy->u_debugreg[4]) return -EIO; + if(addr == (long) &dummy->u_debugreg[5]) return -EIO; + if(addr < (long) &dummy->u_debugreg[4] && + ((unsigned long) data) >= 0xbffffffd) return -EIO; + + if(addr == (long) &dummy->u_debugreg[7]) { + data &= ~DR_CONTROL_RESERVED; + for(i=0; i<4; i++) + if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1) + return -EIO; + }; + + addr -= (long) &dummy->u_debugreg; + addr = addr >> 2; + child->debugreg[addr] = data; + return 0; + }; + return -EIO; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + long tmp; + + if ((unsigned long) data > NSIG) + return -EIO; + if (request == PTRACE_SYSCALL) + child->flags |= PF_TRACESYS; + else + child->flags &= ~PF_TRACESYS; + child->exit_code = data; + child->state = TASK_RUNNING; + /* make sure the single step bit is not set. */ + tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG; + put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); + return 0; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + long tmp; + + child->state = TASK_RUNNING; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG; + put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); + return 0; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + long tmp; + + if ((unsigned long) data > NSIG) + return -EIO; + child->flags &= ~PF_TRACESYS; + tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG; + put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); + child->state = TASK_RUNNING; + child->exit_code = data; + /* give it a chance to run. */ + return 0; + } + + case PTRACE_DETACH: { /* detach a process that was attached. */ + long tmp; + + if ((unsigned long) data > NSIG) + return -EIO; + child->flags &= ~(PF_PTRACED|PF_TRACESYS); + child->state = TASK_RUNNING; + child->exit_code = data; + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + /* make sure the single step bit is not set. */ + tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG; + put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp); + return 0; + } + + default: + return -EIO; + } +} + +asmlinkage void syscall_trace(void) +{ + if ((current->flags & (PF_PTRACED|PF_TRACESYS)) + != (PF_PTRACED|PF_TRACESYS)) + return; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) + current->signal |= (1 << (current->exit_code - 1)); + current->exit_code = 0; +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c --- v1.1.76/linux/arch/i386/kernel/signal.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/signal.c Wed Jan 4 21:16:04 1995 @@ -0,0 +1,258 @@ +/* + * linux/arch/i386/kernel/signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define _S(nr) (1<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); + +/* + * atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set) +{ + unsigned long mask; + struct pt_regs * regs = (struct pt_regs *) &restart; + + mask = current->blocked; + current->blocked = set & _BLOCKABLE; + regs->eax = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(mask,regs)) + return -EINTR; + } +} + +/* + * This sets regs->esp even though we don't actually use sigstacks yet.. + */ +asmlinkage int sys_sigreturn(unsigned long __unused) +{ +#define COPY(x) regs->x = context.x +#define COPY_SEG(x) \ +if ((context.x & 0xfffc) && (context.x & 3) != 3) goto badframe; COPY(x); +#define COPY_SEG_STRICT(x) \ +if (!(context.x & 0xfffc) || (context.x & 3) != 3) goto badframe; COPY(x); + struct sigcontext_struct context; + struct pt_regs * regs; + + regs = (struct pt_regs *) &__unused; + if (verify_area(VERIFY_READ, (void *) regs->esp, sizeof(context))) + goto badframe; + memcpy_fromfs(&context,(void *) regs->esp, sizeof(context)); + current->blocked = context.oldmask & _BLOCKABLE; + COPY_SEG(ds); + COPY_SEG(es); + COPY_SEG(fs); + COPY_SEG(gs); + COPY_SEG_STRICT(ss); + COPY_SEG_STRICT(cs); + COPY(eip); + COPY(ecx); COPY(edx); + COPY(ebx); + COPY(esp); COPY(ebp); + COPY(edi); COPY(esi); + regs->eflags &= ~0x40DD5; + regs->eflags |= context.eflags & 0x40DD5; + regs->orig_eax = -1; /* disable syscall checks */ + return context.eax; +badframe: + do_exit(SIGSEGV); +} + +/* + * Set up a signal frame... Make the stack look the way iBCS2 expects + * it to look. + */ +void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long eip, + struct pt_regs * regs, int signr, unsigned long oldmask) +{ + unsigned long * frame; + +#define __CODE ((unsigned long)(frame+24)) +#define CODE(x) ((unsigned long *) ((x)+__CODE)) + frame = *fp; + if (regs->ss != USER_DS) + frame = (unsigned long *) sa->sa_restorer; + frame -= 32; + if (verify_area(VERIFY_WRITE,frame,32*4)) + do_exit(SIGSEGV); +/* set up the "normal" stack seen by the signal handler (iBCS2) */ + put_fs_long(__CODE,frame); + if (current->exec_domain && current->exec_domain->signal_invmap) + put_fs_long(current->exec_domain->signal_invmap[signr], frame+1); + else + put_fs_long(signr, frame+1); + put_fs_long(regs->gs, frame+2); + put_fs_long(regs->fs, frame+3); + put_fs_long(regs->es, frame+4); + put_fs_long(regs->ds, frame+5); + put_fs_long(regs->edi, frame+6); + put_fs_long(regs->esi, frame+7); + put_fs_long(regs->ebp, frame+8); + put_fs_long((long)*fp, frame+9); + put_fs_long(regs->ebx, frame+10); + put_fs_long(regs->edx, frame+11); + put_fs_long(regs->ecx, frame+12); + put_fs_long(regs->eax, frame+13); + put_fs_long(current->tss.trap_no, frame+14); + put_fs_long(current->tss.error_code, frame+15); + put_fs_long(eip, frame+16); + put_fs_long(regs->cs, frame+17); + put_fs_long(regs->eflags, frame+18); + put_fs_long(regs->esp, frame+19); + put_fs_long(regs->ss, frame+20); + put_fs_long(0,frame+21); /* 387 state pointer - not implemented*/ +/* non-iBCS2 extensions.. */ + put_fs_long(oldmask, frame+22); + put_fs_long(current->tss.cr2, frame+23); +/* set up the return code... */ + put_fs_long(0x0000b858, CODE(0)); /* popl %eax ; movl $,%eax */ + put_fs_long(0x80cd0000, CODE(4)); /* int $0x80 */ + put_fs_long(__NR_sigreturn, CODE(2)); + *fp = frame; +#undef __CODE +#undef CODE +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Note that we go through the signals twice: once to check the signals that + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + */ +asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) +{ + unsigned long mask = ~current->blocked; + unsigned long handler_signal = 0; + unsigned long *frame = NULL; + unsigned long eip = 0; + unsigned long signr; + struct sigaction * sa; + + while ((signr = current->signal & mask)) { + __asm__("bsf %2,%1\n\t" + "btrl %1,%0" + :"=m" (current->signal),"=r" (signr) + :"1" (signr)); + sa = current->sigaction + signr; + signr++; + if ((current->flags & PF_PTRACED) && signr != SIGKILL) { + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current); + schedule(); + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + if (signr == SIGSTOP) + continue; + if (_S(signr) & current->blocked) { + current->signal |= _S(signr); + continue; + } + sa = current->sigaction + signr - 1; + } + if (sa->sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* check for SIGCHLD: it's special */ + while (sys_waitpid(-1,NULL,WNOHANG) > 0) + /* nothing */; + continue; + } + if (sa->sa_handler == SIG_DFL) { + if (current->pid == 1) + continue; + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (current->flags & PF_PTRACED) + continue; + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & + SA_NOCLDSTOP)) + notify_parent(current); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGIOT: case SIGFPE: case SIGSEGV: + if (current->binfmt && current->binfmt->core_dump) { + if (current->binfmt->core_dump(signr, regs)) + signr |= 0x80; + } + /* fall through */ + default: + current->signal |= _S(signr & 0x7f); + do_exit(signr); + } + } + /* + * OK, we're invoking a handler + */ + if (regs->orig_eax >= 0) { + if (regs->eax == -ERESTARTNOHAND || + (regs->eax == -ERESTARTSYS && !(sa->sa_flags & SA_RESTART))) + regs->eax = -EINTR; + } + handler_signal |= 1 << (signr-1); + mask &= ~sa->sa_mask; + } + if (regs->orig_eax >= 0 && + (regs->eax == -ERESTARTNOHAND || + regs->eax == -ERESTARTSYS || + regs->eax == -ERESTARTNOINTR)) { + regs->eax = regs->orig_eax; + regs->eip -= 2; + } + if (!handler_signal) /* no handler will be called - return 0 */ + return 0; + eip = regs->eip; + frame = (unsigned long *) regs->esp; + signr = 1; + sa = current->sigaction; + for (mask = 1 ; mask ; sa++,signr++,mask += mask) { + if (mask > handler_signal) + break; + if (!(mask & handler_signal)) + continue; + setup_frame(sa,&frame,eip,regs,signr,oldmask); + eip = (unsigned long) sa->sa_handler; + if (sa->sa_flags & SA_ONESHOT) + sa->sa_handler = NULL; +/* force a supervisor-mode page-in of the signal handler to reduce races */ + __asm__("testb $0,%%fs:%0": :"m" (*(char *) eip)); + regs->cs = USER_CS; regs->ss = USER_DS; + regs->ds = USER_DS; regs->es = USER_DS; + regs->gs = USER_DS; regs->fs = USER_DS; + current->blocked |= sa->sa_mask; + oldmask |= sa->sa_mask; + } + regs->esp = (unsigned long) frame; + regs->eip = eip; /* "return" to the first handler */ + current->tss.trap_no = current->tss.error_code = 0; + return 1; +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c --- v1.1.76/linux/arch/i386/kernel/traps.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/traps.c Mon Jan 2 15:20:20 1995 @@ -0,0 +1,310 @@ +/* + * linux/arch/i386/traps.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * 'Traps.c' handles hardware traps and faults after we have saved some + * state in 'asm.s'. Currently mostly a debugging-aid, will be extended + * to mainly kill the offending process (probably by giving it a signal, + * but possibly by killing it outright if necessary). + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +asmlinkage int system_call(void); +asmlinkage void lcall7(void); +struct desc_struct default_ldt; + +static inline void console_verbose(void) +{ + extern int console_loglevel; + console_loglevel = 15; +} + +#define DO_ERROR(trapnr, signr, str, name, tsk) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + tsk->tss.error_code = error_code; \ + tsk->tss.trap_no = trapnr; \ + if (signr == SIGTRAP && current->flags & PF_PTRACED) \ + current->blocked &= ~(1 << (SIGTRAP-1)); \ + send_sig(signr, tsk, 1); \ + die_if_kernel(str,regs,error_code); \ +} + +#define get_seg_byte(seg,addr) ({ \ +register unsigned char __res; \ +__asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \ + :"=a" (__res):"0" (seg),"m" (*(addr))); \ +__res;}) + +#define get_seg_long(seg,addr) ({ \ +register unsigned long __res; \ +__asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \ + :"=a" (__res):"0" (seg),"m" (*(addr))); \ +__res;}) + +#define _fs() ({ \ +register unsigned short __res; \ +__asm__("mov %%fs,%%ax":"=a" (__res):); \ +__res;}) + +void page_exception(void); + +asmlinkage void divide_error(void); +asmlinkage void debug(void); +asmlinkage void nmi(void); +asmlinkage void int3(void); +asmlinkage void overflow(void); +asmlinkage void bounds(void); +asmlinkage void invalid_op(void); +asmlinkage void device_not_available(void); +asmlinkage void double_fault(void); +asmlinkage void coprocessor_segment_overrun(void); +asmlinkage void invalid_TSS(void); +asmlinkage void segment_not_present(void); +asmlinkage void stack_segment(void); +asmlinkage void general_protection(void); +asmlinkage void page_fault(void); +asmlinkage void coprocessor_error(void); +asmlinkage void reserved(void); +asmlinkage void alignment_check(void); + +/*static*/ void die_if_kernel(char * str, struct pt_regs * regs, long err) +{ + int i; + unsigned long esp; + unsigned short ss; + + esp = (unsigned long) ®s->esp; + ss = KERNEL_DS; + if ((regs->eflags & VM_MASK) || (3 & regs->cs) == 3) + return; + if (regs->cs & 3) { + esp = regs->esp; + ss = regs->ss; + } + console_verbose(); + printk("%s: %04lx\n", str, err & 0xffff); + printk("EIP: %04x:%08lx\nEFLAGS: %08lx\n", 0xffff & regs->cs,regs->eip,regs->eflags); + printk("eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n", + regs->eax, regs->ebx, regs->ecx, regs->edx); + printk("esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n", + regs->esi, regs->edi, regs->ebp, esp); + printk("ds: %04x es: %04x fs: %04x gs: %04x ss: %04x\n", + regs->ds, regs->es, regs->fs, regs->gs, ss); + store_TR(i); + if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page) + printk("Corrupted stack page\n"); + printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)\nStack: ", + current->comm, current->pid, 0xffff & i, current->kernel_stack_page); + for(i=0;i<5;i++) + printk("%08lx ", get_seg_long(ss,(i+(unsigned long *)esp))); + printk("\nCode: "); + for(i=0;i<20;i++) + printk("%02x ",0xff & get_seg_byte(regs->cs,(i+(char *)regs->eip))); + printk("\n"); + do_exit(SIGSEGV); +} + +DO_ERROR( 0, SIGFPE, "divide error", divide_error, current) +DO_ERROR( 3, SIGTRAP, "int3", int3, current) +DO_ERROR( 4, SIGSEGV, "overflow", overflow, current) +DO_ERROR( 5, SIGSEGV, "bounds", bounds, current) +DO_ERROR( 6, SIGILL, "invalid operand", invalid_op, current) +DO_ERROR( 7, SIGSEGV, "device not available", device_not_available, current) +DO_ERROR( 8, SIGSEGV, "double fault", double_fault, current) +DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun, last_task_used_math) +DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS, current) +DO_ERROR(11, SIGBUS, "segment not present", segment_not_present, current) +DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) +DO_ERROR(15, SIGSEGV, "reserved", reserved, current) +DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) + +asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) +{ + int signr = SIGSEGV; + + if (regs->eflags & VM_MASK) { + handle_vm86_fault((struct vm86_regs *) regs, error_code); + return; + } + die_if_kernel("general protection",regs,error_code); + switch (get_seg_byte(regs->cs, (char *)regs->eip)) { + case 0xCD: /* INT */ + case 0xF4: /* HLT */ + case 0xFA: /* CLI */ + case 0xFB: /* STI */ + signr = SIGILL; + } + current->tss.error_code = error_code; + current->tss.trap_no = 13; + send_sig(signr, current, 1); +} + +asmlinkage void do_nmi(struct pt_regs * regs, long error_code) +{ + printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n"); + printk("You probably have a hardware problem with your RAM chips\n"); +} + +asmlinkage void do_debug(struct pt_regs * regs, long error_code) +{ + if (regs->eflags & VM_MASK) { + handle_vm86_debug((struct vm86_regs *) regs, error_code); + return; + } + if (current->flags & PF_PTRACED) + current->blocked &= ~(1 << (SIGTRAP-1)); + send_sig(SIGTRAP, current, 1); + current->tss.trap_no = 1; + current->tss.error_code = error_code; + if ((regs->cs & 3) == 0) { + /* If this is a kernel mode trap, then reset db7 and allow us to continue */ + __asm__("movl %0,%%db7" + : /* no output */ + : "r" (0)); + return; + } + die_if_kernel("debug",regs,error_code); +} + +/* + * Allow the process which triggered the interrupt to recover the error + * condition. + * - the status word is saved in the cs selector. + * - the tag word is saved in the operand selector. + * - the status word is then cleared and the tags all set to Empty. + * + * This will give sufficient information for complete recovery provided that + * the affected process knows or can deduce the code and data segments + * which were in force when the exception condition arose. + * + * Note that we play around with the 'TS' bit to hopefully get + * the correct behaviour even in the presence of the asynchronous + * IRQ13 behaviour + */ +void math_error(void) +{ + struct i387_hard_struct * env; + + clts(); + if (!last_task_used_math) { + __asm__("fnclex"); + return; + } + env = &last_task_used_math->tss.i387.hard; + send_sig(SIGFPE, last_task_used_math, 1); + last_task_used_math->tss.trap_no = 16; + last_task_used_math->tss.error_code = 0; + __asm__ __volatile__("fnsave %0":"=m" (*env)); + last_task_used_math = NULL; + stts(); + env->fcs = (env->swd & 0x0000ffff) | (env->fcs & 0xffff0000); + env->fos = env->twd; + env->swd &= 0xffff3800; + env->twd = 0xffffffff; +} + +asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code) +{ + ignore_irq13 = 1; + math_error(); +} + +/* + * 'math_state_restore()' saves the current math information in the + * old math state array, and gets the new ones from the current task + * + * Careful.. There are problems with IBM-designed IRQ13 behaviour. + * Don't touch unless you *really* know how it works. + */ +asmlinkage void math_state_restore(void) +{ + __asm__ __volatile__("clts"); + if (last_task_used_math == current) + return; + timer_table[COPRO_TIMER].expires = jiffies+50; + timer_active |= 1<tss.i387)); + else + __asm__("fnclex"); + last_task_used_math = current; + if (current->used_math) { + __asm__("frstor %0": :"m" (current->tss.i387)); + } else { + __asm__("fninit"); + current->used_math=1; + } + timer_active &= ~(1<comm); + send_sig(SIGFPE,current,1); + schedule(); +} + +#endif /* CONFIG_MATH_EMULATION */ + +void trap_init(void) +{ + int i; + struct desc_struct * p; + + set_call_gate(&default_ldt,lcall7); + set_trap_gate(0,÷_error); + set_trap_gate(1,&debug); + set_trap_gate(2,&nmi); + set_system_gate(3,&int3); /* int3-5 can be called from all */ + set_system_gate(4,&overflow); + set_system_gate(5,&bounds); + set_trap_gate(6,&invalid_op); + set_trap_gate(7,&device_not_available); + set_trap_gate(8,&double_fault); + set_trap_gate(9,&coprocessor_segment_overrun); + set_trap_gate(10,&invalid_TSS); + set_trap_gate(11,&segment_not_present); + set_trap_gate(12,&stack_segment); + set_trap_gate(13,&general_protection); + set_trap_gate(14,&page_fault); + set_trap_gate(15,&reserved); + set_trap_gate(16,&coprocessor_error); + set_trap_gate(17,&alignment_check); + for (i=18;i<48;i++) + set_trap_gate(i,&reserved); + set_system_gate(0x80,&system_call); +/* set up GDT task & ldt entries */ + p = gdt+FIRST_TSS_ENTRY; + set_tss_desc(p, &init_task.tss); + p++; + set_ldt_desc(p, &default_ldt, 1); + p++; + for(i=1 ; ia=p->b=0; + p++; + p->a=p->b=0; + p++; + } +/* Clear NT, so that we won't have troubles with that later on */ + __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); + load_TR(0); + load_ldt(0); +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/kernel/vm86.c linux/arch/i386/kernel/vm86.c --- v1.1.76/linux/arch/i386/kernel/vm86.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/kernel/vm86.c Wed Jan 4 21:16:05 1995 @@ -0,0 +1,404 @@ +/* + * linux/kernel/vm86.c + * + * Copyright (C) 1994 Linus Torvalds + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Known problems: + * + * Interrupt handling is not guaranteed: + * - a real x86 will disable all interrupts for one instruction + * after a "mov ss,xx" to make stack handling atomic even without + * the 'lss' instruction. We can't guarantee this in v86 mode, + * as the next instruction might result in a page fault or similar. + * - a real x86 will have interrupts disabled for one instruction + * past the 'sti' that enables them. We don't bother with all the + * details yet.. + * + * Hopefully these problems do not actually matter for anything. + */ + +/* + * 8- and 16-bit register defines.. + */ +#define AL(regs) (((unsigned char *)&((regs)->eax))[0]) +#define AH(regs) (((unsigned char *)&((regs)->eax))[1]) +#define IP(regs) (*(unsigned short *)&((regs)->eip)) +#define SP(regs) (*(unsigned short *)&((regs)->esp)) + +/* + * virtual flags (16 and 32-bit versions) + */ +#define VFLAGS (*(unsigned short *)&(current->tss.v86flags)) +#define VEFLAGS (current->tss.v86flags) + +#define set_flags(X,new,mask) \ +((X) = ((X) & ~(mask)) | ((new) & (mask))) + +#define SAFE_MASK (0xDD5) +#define RETURN_MASK (0xDFF) + +asmlinkage struct pt_regs * save_v86_state(struct vm86_regs * regs) +{ + unsigned long tmp; + + if (!current->tss.vm86_info) { + printk("no vm86_info: BAD\n"); + do_exit(SIGSEGV); + } + set_flags(regs->eflags, VEFLAGS, VIF_MASK | current->tss.v86mask); + memcpy_tofs(¤t->tss.vm86_info->regs,regs,sizeof(*regs)); + put_fs_long(current->tss.screen_bitmap,¤t->tss.vm86_info->screen_bitmap); + tmp = current->tss.esp0; + current->tss.esp0 = current->saved_kernel_stack; + current->saved_kernel_stack = 0; + return (struct pt_regs *) tmp; +} + +static void mark_screen_rdonly(struct task_struct * tsk) +{ + unsigned long tmp; + unsigned long *pg_table; + + if ((tmp = tsk->tss.cr3) != 0) { + tmp = *(unsigned long *) tmp; + if (tmp & PAGE_PRESENT) { + tmp &= PAGE_MASK; + pg_table = (0xA0000 >> PAGE_SHIFT) + (unsigned long *) tmp; + tmp = 32; + while (tmp--) { + if (PAGE_PRESENT & *pg_table) + *pg_table &= ~PAGE_RW; + pg_table++; + } + } + } +} + +asmlinkage int sys_vm86(struct vm86_struct * v86) +{ + struct vm86_struct info; + struct pt_regs * pt_regs = (struct pt_regs *) &v86; + int error; + + if (current->saved_kernel_stack) + return -EPERM; + /* v86 must be readable (now) and writable (for save_v86_state) */ + error = verify_area(VERIFY_WRITE,v86,sizeof(*v86)); + if (error) + return error; + memcpy_fromfs(&info,v86,sizeof(info)); +/* + * make sure the vm86() system call doesn't try to do anything silly + */ + info.regs.__null_ds = 0; + info.regs.__null_es = 0; + info.regs.__null_fs = 0; + info.regs.__null_gs = 0; +/* + * The eflags register is also special: we cannot trust that the user + * has set it up safely, so this makes sure interrupt etc flags are + * inherited from protected mode. + */ + VEFLAGS = info.regs.eflags; + info.regs.eflags &= SAFE_MASK; + info.regs.eflags |= pt_regs->eflags & ~SAFE_MASK; + info.regs.eflags |= VM_MASK; + + switch (info.cpu_type) { + case CPU_286: + current->tss.v86mask = 0; + break; + case CPU_386: + current->tss.v86mask = NT_MASK | IOPL_MASK; + break; + case CPU_486: + current->tss.v86mask = AC_MASK | NT_MASK | IOPL_MASK; + break; + default: + current->tss.v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK; + break; + } + +/* + * Save old state, set default return value (%eax) to 0 + */ + pt_regs->eax = 0; + current->saved_kernel_stack = current->tss.esp0; + current->tss.esp0 = (unsigned long) pt_regs; + current->tss.vm86_info = v86; + + current->tss.screen_bitmap = info.screen_bitmap; + if (info.flags & VM86_SCREEN_BITMAP) + mark_screen_rdonly(current); + __asm__ __volatile__("movl %0,%%esp\n\t" + "jmp ret_from_sys_call" + : /* no outputs */ + :"r" (&info.regs)); + return 0; +} + +static inline void return_to_32bit(struct vm86_regs * regs16, int retval) +{ + struct pt_regs * regs32; + + regs32 = save_v86_state(regs16); + regs32->eax = retval; + __asm__ __volatile__("movl %0,%%esp\n\t" + "jmp ret_from_sys_call" + : : "r" (regs32)); +} + +static inline void set_IF(struct vm86_regs * regs) +{ + VEFLAGS |= VIF_MASK; + if (VEFLAGS & VIP_MASK) + return_to_32bit(regs, VM86_STI); +} + +static inline void clear_IF(struct vm86_regs * regs) +{ + VEFLAGS &= ~VIF_MASK; +} + +static inline void clear_TF(struct vm86_regs * regs) +{ + regs->eflags &= ~TF_MASK; +} + +static inline void set_vflags_long(unsigned long eflags, struct vm86_regs * regs) +{ + set_flags(VEFLAGS, eflags, current->tss.v86mask); + set_flags(regs->eflags, eflags, SAFE_MASK); + if (eflags & IF_MASK) + set_IF(regs); +} + +static inline void set_vflags_short(unsigned short flags, struct vm86_regs * regs) +{ + set_flags(VFLAGS, flags, current->tss.v86mask); + set_flags(regs->eflags, flags, SAFE_MASK); + if (flags & IF_MASK) + set_IF(regs); +} + +static inline unsigned long get_vflags(struct vm86_regs * regs) +{ + unsigned long flags = regs->eflags & RETURN_MASK; + + if (VEFLAGS & VIF_MASK) + flags |= IF_MASK; + return flags | (VEFLAGS & current->tss.v86mask); +} + +static inline int is_revectored(int nr, struct revectored_struct * bitmap) +{ + __asm__ __volatile__("btl %2,%%fs:%1\n\tsbbl %0,%0" + :"=r" (nr) + :"m" (*bitmap),"r" (nr)); + return nr; +} + +/* + * Boy are these ugly, but we need to do the correct 16-bit arithmetic. + * Gcc makes a mess of it, so we do it inline and use non-obvious calling + * conventions.. + */ +#define pushb(base, ptr, val) \ +__asm__ __volatile__( \ + "decw %w0\n\t" \ + "movb %2,%%fs:0(%1,%0)" \ + : "=r" (ptr) \ + : "r" (base), "q" (val), "0" (ptr)) + +#define pushw(base, ptr, val) \ +__asm__ __volatile__( \ + "decw %w0\n\t" \ + "movb %h2,%%fs:0(%1,%0)\n\t" \ + "decw %w0\n\t" \ + "movb %b2,%%fs:0(%1,%0)" \ + : "=r" (ptr) \ + : "r" (base), "q" (val), "0" (ptr)) + +#define pushl(base, ptr, val) \ +__asm__ __volatile__( \ + "decw %w0\n\t" \ + "rorl $16,%2\n\t" \ + "movb %h2,%%fs:0(%1,%0)\n\t" \ + "decw %w0\n\t" \ + "movb %b2,%%fs:0(%1,%0)\n\t" \ + "decw %w0\n\t" \ + "rorl $16,%2\n\t" \ + "movb %h2,%%fs:0(%1,%0)\n\t" \ + "decw %w0\n\t" \ + "movb %b2,%%fs:0(%1,%0)" \ + : "=r" (ptr) \ + : "r" (base), "q" (val), "0" (ptr)) + +#define popb(base, ptr) \ +({ unsigned long __res; \ +__asm__ __volatile__( \ + "movb %%fs:0(%1,%0),%b2\n\t" \ + "incw %w0" \ + : "=r" (ptr), "=r" (base), "=q" (__res) \ + : "0" (ptr), "1" (base), "2" (0)); \ +__res; }) + +#define popw(base, ptr) \ +({ unsigned long __res; \ +__asm__ __volatile__( \ + "movb %%fs:0(%1,%0),%b2\n\t" \ + "incw %w0\n\t" \ + "movb %%fs:0(%1,%0),%h2\n\t" \ + "incw %w0" \ + : "=r" (ptr), "=r" (base), "=q" (__res) \ + : "0" (ptr), "1" (base), "2" (0)); \ +__res; }) + +#define popl(base, ptr) \ +({ unsigned long __res; \ +__asm__ __volatile__( \ + "movb %%fs:0(%1,%0),%b2\n\t" \ + "incw %w0\n\t" \ + "movb %%fs:0(%1,%0),%h2\n\t" \ + "incw %w0\n\t" \ + "rorl $16,%2\n\t" \ + "movb %%fs:0(%1,%0),%b2\n\t" \ + "incw %w0\n\t" \ + "movb %%fs:0(%1,%0),%h2\n\t" \ + "incw %w0\n\t" \ + "rorl $16,%2" \ + : "=r" (ptr), "=r" (base), "=q" (__res) \ + : "0" (ptr), "1" (base)); \ +__res; }) + +static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned long sp) +{ + unsigned short seg = get_fs_word((void *) ((i<<2)+2)); + + if (seg == BIOSSEG || regs->cs == BIOSSEG || + is_revectored(i, ¤t->tss.vm86_info->int_revectored)) + return_to_32bit(regs, VM86_INTx + (i << 8)); + if (i==0x21 && is_revectored(AH(regs),¤t->tss.vm86_info->int21_revectored)) + return_to_32bit(regs, VM86_INTx + (i << 8)); + pushw(ssp, sp, get_vflags(regs)); + pushw(ssp, sp, regs->cs); + pushw(ssp, sp, IP(regs)); + regs->cs = seg; + SP(regs) -= 6; + IP(regs) = get_fs_word((void *) (i<<2)); + clear_TF(regs); + clear_IF(regs); + return; +} + +void handle_vm86_debug(struct vm86_regs * regs, long error_code) +{ +#if 0 + do_int(regs, 1, (unsigned char *) (regs->ss << 4), SP(regs)); +#else + if (current->flags & PF_PTRACED) + current->blocked &= ~(1 << (SIGTRAP-1)); + send_sig(SIGTRAP, current, 1); + current->tss.trap_no = 1; + current->tss.error_code = error_code; +#endif +} + +void handle_vm86_fault(struct vm86_regs * regs, long error_code) +{ + unsigned char *csp, *ssp; + unsigned long ip, sp; + + csp = (unsigned char *) (regs->cs << 4); + ssp = (unsigned char *) (regs->ss << 4); + sp = SP(regs); + ip = IP(regs); + + switch (popb(csp, ip)) { + + /* operand size override */ + case 0x66: + switch (popb(csp, ip)) { + + /* pushfd */ + case 0x9c: + SP(regs) -= 4; + IP(regs) += 2; + pushl(ssp, sp, get_vflags(regs)); + return; + + /* popfd */ + case 0x9d: + SP(regs) += 4; + IP(regs) += 2; + set_vflags_long(popl(ssp, sp), regs); + return; + } + + /* pushf */ + case 0x9c: + SP(regs) -= 2; + IP(regs)++; + pushw(ssp, sp, get_vflags(regs)); + return; + + /* popf */ + case 0x9d: + SP(regs) += 2; + IP(regs)++; + set_vflags_short(popw(ssp, sp), regs); + return; + + /* int 3 */ + case 0xcc: + IP(regs)++; + do_int(regs, 3, ssp, sp); + return; + + /* int xx */ + case 0xcd: + IP(regs) += 2; + do_int(regs, popb(csp, ip), ssp, sp); + return; + + /* iret */ + case 0xcf: + SP(regs) += 6; + IP(regs) = popw(ssp, sp); + regs->cs = popw(ssp, sp); + set_vflags_short(popw(ssp, sp), regs); + return; + + /* cli */ + case 0xfa: + IP(regs)++; + clear_IF(regs); + return; + + /* sti */ + /* + * Damn. This is incorrect: the 'sti' instruction should actually + * enable interrupts after the /next/ instruction. Not good. + * + * Probably needs some horsing around with the TF flag. Aiee.. + */ + case 0xfb: + IP(regs)++; + set_IF(regs); + return; + + default: + return_to_32bit(regs, VM86_UNKNOWN); + } +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/Makefile linux/arch/i386/math-emu/Makefile --- v1.1.76/linux/arch/i386/math-emu/Makefile Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/Makefile Mon Aug 1 08:19:12 1994 @@ -0,0 +1,50 @@ +# +# Makefile for wm-FPU-emu +# + +#DEBUG = -DDEBUGGING +DEBUG = +PARANOID = -DPARANOID +CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin + +.c.o: + $(CC) $(CFLAGS) $(MATH_EMULATION) -c $< + +.S.o: + $(CC) -D__ASSEMBLER__ $(PARANOID) -c $< + +.s.o: + $(CC) -c $< + +OBJS = fpu_entry.o div_small.o errors.o \ + fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \ + load_store.o get_address.o \ + poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \ + reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o \ + reg_div.o reg_mul.o reg_norm.o \ + reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \ + reg_round.o \ + wm_shrx.o wm_sqrt.o \ + div_Xsig.o polynom_Xsig.o round_Xsig.o \ + shr_Xsig.o mul_Xsig.o + +math.a: $(OBJS) + rm -f math.a + $(AR) rcs math.a $(OBJS) + sync + +dep: + $(CPP) -M *.c > .depend + $(CPP) -D__ASSEMBLER__ -M *.S >> .depend + +proto: + cproto -e -DMAKING_PROTO *.c >fpu_proto.h + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/README linux/arch/i386/math-emu/README --- v1.1.76/linux/arch/i386/math-emu/README Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/README Fri Aug 19 08:54:01 1994 @@ -0,0 +1,436 @@ + +---------------------------------------------------------------------------+ + | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | This program is free software; you can redistribute it and/or modify | + | it under the terms of the GNU General Public License version 2 as | + | published by the Free Software Foundation. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + | | + | You should have received a copy of the GNU General Public License | + | along with this program; if not, write to the Free Software | + | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | + | | + +---------------------------------------------------------------------------+ + + + +wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387 +which was my 80387 emulator for early versions of 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). Unfortunately, numerous +facets of the functioning of the FPU are not well covered in the +Reference Manual. The information in the manual has been supplemented +with measurements on real 80486's. Unfortunately, it is simply not +possible to be sure that all of the peculiarities of the 80486 have +been discovered, so there is always likely to be obscure differences +in the detailed behaviour of the emulator and a real 80486. + +wm-FPU-emu does not implement all of the behaviour of the 80486 FPU, +but is very close. See "Limitations" later in this file for a list of +some differences. + +Please report bugs, etc to me at: + billm@vaxc.cc.monash.edu.au + or at: + billm@jacobi.maths.monash.edu.au + + +--Bill Metzenthen + August 1994 + + +----------------------- 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. +(5) The argument reducing code for the trig function effectively uses + a value of pi which is accurate to more than 128 bits. As a consequence, + the reduced argument is accurate to more than 64 bits for arguments up + to a few pi, and accurate to more than 64 bits for most arguments, + even for arguments approaching 2^63. This is far superior to an + 80486, which uses a value of pi which is accurate to 66 bits. + +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 perhaps changing static +variables. 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 +As from version 1.12 of the emulator, no static variables are used +(apart from those in the kernel's per-process tables). The emulator is +therefore now fully re-entrant, rather than having just the restricted +form of re-entrancy which is required by the Linux kernel. + +----------------------- Limitations of wm-FPU-emu ----------------------- + +There are a number of differences between the current wm-FPU-emu +(version 1.20) and the 80486 FPU (apart from bugs). Some of the more +important differences are listed below: + +The Roundup flag does not have much meaning for the transcendental +functions and its 80486 value with these functions is likely to differ +from its emulator value. + +In a few rare cases the Underflow flag obtained with the emulator will +be different from that obtained with an 80486. This occurs when the +following conditions apply simultaneously: +(a) the operands have a higher precision than the current setting of the + precision control (PC) flags. +(b) the underflow exception is masked. +(c) the magnitude of the exact result (before rounding) is less than 2^-16382. +(d) the magnitude of the final result (after rounding) is exactly 2^-16382. +(e) the magnitude of the exact result would be exactly 2^-16382 if the + operands were rounded to the current precision before the arithmetic + operation was performed. +If all of these apply, the emulator will set the Underflow flag but a real +80486 will not. + +NOTE: Certain formats of Extended Real are UNSUPPORTED. They are +unsupported by the 80486. They are the Pseudo-NaNs, Pseudoinfinities, +and Unnormals. None of these will be generated by an 80486 or by the +emulator. Do not use them. The emulator treats them differently in +detail from the way an 80486 does. + +The emulator treats PseudoDenormals differently from an 80486. These +numbers are in fact properly normalised numbers with the exponent +offset by 1, and the emulator treats them as such. Unlike the 80486, +the emulator does not generate a Denormal Operand exception for these +numbers. The arithmetical results produced when using such a number as +an operand are the same for the emulator and a real 80486 (apart from +any slight precision difference for the transcendental functions). +Neither the emulator nor an 80486 produces one of these numbers as the +result of any arithmetic operation. An 80486 can keep one of these +numbers in an FPU register with its identity as a PseudoDenormal, but +the emulator will not; they are always converted to a valid number. + +Self modifying code can cause the emulator to fail. An example of such +code is: + movl %esp,[%ebx] + fld1 +The FPU instruction may be (usually will be) loaded into the pre-fetch +queue of the cpu before the mov instruction is executed. If the +destination of the 'movl' overlaps the FPU instruction then the bytes +in the prefetch queue and memory will be inconsistent when the FPU +instruction is executed. The emulator will be invoked but will not be +able to find the instruction which caused the device-not-present +exception. For this case, the emulator cannot emulate the behaviour of +an 80486DX. + +Handling of the address size override prefix byte (0x67) has not been +extensively tested yet. A major problem exists because using it in +vm86 mode can cause a general protection fault. Address offsets +greater than 0xffff appear to be illegal in vm86 mode but are quite +acceptable (and work) in real mode. A small test program developed to +check the addressing, and which runs successfully in real mode, +crashes dosemu under Linux and also brings Windows down with a general +protection fault message when run under the MS-DOS prompt of Windows +3.1. (The program simply reads data from a valid address). + +The emulator supports 16-bit protected mode, with one difference from +an 80486DX. A 80486DX will allow some floating point instructions to +write a few bytes below the lowest address of the stack. The emulator +will not allow this in 16-bit protected mode: no instructions are +allowed to write outside the bounds set by the protection. + +----------------------- 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 ----------------------- + + +The accuracy of the emulator is in almost all cases equal to or better +than that of an Intel 80486 FPU. + +The results of the basic arithmetic functions (+,-,*,/), and fsqrt +match those of an 80486 FPU. They are the best possible; the error for +these never exceeds 1/2 an lsb. The fprem and fprem1 instructions +return exact results; they have no error. + + +The following table compares the emulator accuracy for the sqrt(), +trig and log functions against the Turbo C "emulator". For this table, +each function was tested at about 400 points. Ideal worst-case results +would be 64 bits. The reduced Turbo C accuracy of cos() and tan() for +arguments greater than pi/4 can be thought of as being related 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. + + +Function Tested x range Worst result Turbo C + (relative bits) + +sqrt(x) 1 .. 2 64.1 63.2 +atan(x) 1e-10 .. 200 64.2 62.8 +cos(x) 0 .. pi/2-(1e-10) 64.4 (x <= pi/4) 62.4 + 64.1 (x = pi/2-(1e-10)) 31.9 +sin(x) 1e-10 .. pi/2 64.0 62.8 +tan(x) 1e-10 .. pi/2-(1e-10) 64.0 (x <= pi/4) 62.1 + 64.1 (x = pi/2-(1e-10)) 31.9 +exp(x) 0 .. 1 63.1 ** 62.9 +log(x) 1+1e-6 .. 2 63.8 ** 62.1 + +** The accuracy for exp() and log() is low because the FPU (emulator) +does not compute them directly; two operations are required. + + +The emulator passes the "paranoia" tests (compiled with gcc 2.3.3 or +later) 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). + +The code for reducing the argument for the trig functions (fsin, fcos, +fptan and fsincos) has been improved and now effectively uses a value +for pi which is accurate to more than 128 bits precision. As a +consequence, the accuracy of these functions for large arguments has +been dramatically improved (and is now very much better than an 80486 +FPU). There is also now no degradation of accuracy for fcos and fptan +for operands close to pi/2. Measured results are (note that the +definition of accuracy has changed slightly from that used for the +above table): + +Function Tested x range Worst result + (absolute bits) + +cos(x) 0 .. 9.22e+18 62.0 +sin(x) 1e-16 .. 9.22e+18 62.1 +tan(x) 1e-16 .. 9.22e+18 61.8 + +It is possible with some effort to find very large arguments which +give much degraded precision. For example, the integer number + 8227740058411162616.0 +is within about 10e-7 of a multiple of pi. To find the tan (for +example) of this number to 64 bits precision it would be necessary to +have a value of pi which had about 150 bits precision. The FPU +emulator computes the result to about 42.6 bits precision (the correct +result is about -9.739715e-8). On the other hand, an 80486 FPU returns +0.01059, which in relative terms is hopelessly inaccurate. + +For arguments close to critical angles (which occur at multiples of +pi/2) the emulator is more accurate than an 80486 FPU. For very large +arguments, the emulator is far more accurate. + + +Prior to version 1.20 of the emulator, the accuracy of the results for +the transcendental functions (in their principal range) was not as +good as the results from an 80486 FPU. From version 1.20, the accuracy +has been considerably improved and these functions now give measured +worst-case results which are better than the worst-case results given +by an 80486 FPU. + +The following table gives the measured results for the emulator. The +number of randomly selected arguments in each case is about half a +million. The group of three columns gives the frequency of the given +accuracy in number of times per million, thus the second of these +columns shows that an accuracy of between 63.80 and 63.89 bits was +found at a rate of 133 times per one million measurements for fsin. +The results show that the fsin, fcos and fptan instructions return +results which are in error (i.e. less accurate than the best possible +result (which is 64 bits)) for about one per cent of all arguments +between -pi/2 and +pi/2. The other instructions have a lower +frequency of results which are in error. The last two columns give +the worst accuracy which was found (in bits) and the approximate value +of the argument which produced it. + + frequency (per M) + ------------------- --------------- +instr arg range # tests 63.7 63.8 63.9 worst at arg + bits bits bits bits +----- ------------ ------- ---- ---- ----- ----- -------- +fsin (0,pi/2) 547756 0 133 10673 63.89 0.451317 +fcos (0,pi/2) 547563 0 126 10532 63.85 0.700801 +fptan (0,pi/2) 536274 11 267 10059 63.74 0.784876 +fpatan 4 quadrants 517087 0 8 1855 63.88 0.435121 (4q) +fyl2x (0,20) 541861 0 0 1323 63.94 1.40923 (x) +fyl2xp1 (-.293,.414) 520256 0 0 5678 63.93 0.408542 (x) +f2xm1 (-1,1) 538847 4 481 6488 63.79 0.167709 + + +Tests performed on an 80486 FPU showed results of lower accuracy. The +following table gives the results which were obtained with an AMD +486DX2/66 (other tests indicate that an Intel 486DX produces +identical results). The tests were basically the same as those used +to measure the emulator (the values, being random, were in general not +the same). The total number of tests for each instruction are given +at the end of the table, in case each about 100k tests were performed. +Another line of figures at the end of the table shows that most of the +instructions return results which are in error for more than 10 +percent of the arguments tested. + +The numbers in the body of the table give the approx number of times a +result of the given accuracy in bits (given in the left-most column) +was obtained per one million arguments. For three of the instructions, +two columns of results are given: * The second column for f2xm1 gives +the number cases where the results of the first column were for a +positive argument, this shows that this instruction gives better +results for positive arguments than it does for negative. * In the +cases of fcos and fptan, the first column gives the results when all +cases where arguments greater than 1.5 were removed from the results +given in the second column. Unlike the emulator, an 80486 FPU returns +results of relatively poor accuracy for these instructions when the +argument approaches pi/2. The table does not show those cases when the +accuracy of the results were less than 62 bits, which occurs quite +often for fsin and fptan when the argument approaches pi/2. This poor +accuracy is discussed above in relation to the Turbo C "emulator", and +the accuracy of the value of pi. + + +bits f2xm1 f2xm1 fpatan fcos fcos fyl2x fyl2xp1 fsin fptan fptan +62.0 0 0 0 0 437 0 0 0 0 925 +62.1 0 0 10 0 894 0 0 0 0 1023 +62.2 14 0 0 0 1033 0 0 0 0 945 +62.3 57 0 0 0 1202 0 0 0 0 1023 +62.4 385 0 0 10 1292 0 23 0 0 1178 +62.5 1140 0 0 119 1649 0 39 0 0 1149 +62.6 2037 0 0 189 1620 0 16 0 0 1169 +62.7 5086 14 0 646 2315 10 101 35 39 1402 +62.8 8818 86 0 984 3050 59 287 131 224 2036 +62.9 11340 1355 0 2126 4153 79 605 357 321 1948 +63.0 15557 4750 0 3319 5376 246 1281 862 808 2688 +63.1 20016 8288 0 4620 6628 511 2569 1723 1510 3302 +63.2 24945 11127 10 6588 8098 1120 4470 2968 2990 4724 +63.3 25686 12382 69 8774 10682 1906 6775 4482 5474 7236 +63.4 29219 14722 79 11109 12311 3094 9414 7259 8912 10587 +63.5 30458 14936 393 13802 15014 5874 12666 9609 13762 15262 +63.6 32439 16448 1277 17945 19028 10226 15537 14657 19158 20346 +63.7 35031 16805 4067 23003 23947 18910 20116 21333 25001 26209 +63.8 33251 15820 7673 24781 25675 24617 25354 24440 29433 30329 +63.9 33293 16833 18529 28318 29233 31267 31470 27748 29676 30601 + +Per cent with error: + 30.9 3.2 18.5 9.8 13.1 11.6 17.4 +Total arguments tested: + 70194 70099 101784 100641 100641 101799 128853 114893 102675 102675 + + +------------------------- Contributors ------------------------------- + +A number of people have contributed to the development of the +emulator, often by just reporting bugs, sometimes with suggested +fixes, 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 may have forgotten, please forgive 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 +Bruce Evans, bde@kralizec.zeta.org.au +Timo Korvola, Timo.Korvola@hut.fi +Rick Lyons, rick@razorback.brisnet.org.au +Rick, jrs@world.std.com + +...and numerous others who responded to my request for help with +a real 80486. + diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/control_w.h linux/arch/i386/math-emu/control_w.h --- v1.1.76/linux/arch/i386/math-emu/control_w.h Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/control_w.h Wed Feb 16 13:07:55 1994 @@ -0,0 +1,45 @@ +/*---------------------------------------------------------------------------+ + | control_w.h | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _CONTROLW_H_ +#define _CONTROLW_H_ + +#ifdef __ASSEMBLER__ +#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) +#define PR_RESERVED_BITS _Const_(0x100) +/* FULL_PRECISION simulates all exceptions masked */ +#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f) + +#endif _CONTROLW_H_ diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/div_Xsig.S linux/arch/i386/math-emu/div_Xsig.S --- v1.1.76/linux/arch/i386/math-emu/div_Xsig.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/div_Xsig.S Mon Aug 1 08:19:13 1994 @@ -0,0 +1,369 @@ + .file "div_Xsig.S" +/*---------------------------------------------------------------------------+ + | div_Xsig.S | + | | + | Division subroutine for 96 bit quantities | + | | + | Copyright (C) 1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Divide the 96 bit quantity pointed to by a, by that pointed to by b, and | + | put the 96 bit result at the location d. | + | | + | The result may not be accurate to 96 bits. It is intended for use where | + | a result better than 64 bits is required. The result should usually be | + | good to at least 94 bits. | + | The returned result is actually divided by one half. This is done to | + | prevent overflow. | + | | + | .aaaaaaaaaaaaaa / .bbbbbbbbbbbbb -> .dddddddddddd | + | | + | void div_Xsig(Xsig *a, Xsig *b, Xsig *dest) | + | | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "fpu_asm.h" + + +#define XsigLL(x) (x) +#define XsigL(x) 4(x) +#define XsigH(x) 8(x) + + +#ifndef NON_REENTRANT_FPU +/* + Local storage on the stack: + Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 + */ +#define FPU_accum_3 -4(%ebp) +#define FPU_accum_2 -8(%ebp) +#define FPU_accum_1 -12(%ebp) +#define FPU_accum_0 -16(%ebp) +#define FPU_result_3 -20(%ebp) +#define FPU_result_2 -24(%ebp) +#define FPU_result_1 -28(%ebp) + +#else +.data +/* + Local storage in a static area: + Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 + */ + .align 2,0 +FPU_accum_3: + .long 0 +FPU_accum_2: + .long 0 +FPU_accum_1: + .long 0 +FPU_accum_0: + .long 0 +FPU_result_3: + .long 0 +FPU_result_2: + .long 0 +FPU_result_1: + .long 0 +#endif NON_REENTRANT_FPU + + +.text + .align 2,144 + +.globl _div_Xsig + +_div_Xsig: + pushl %ebp + movl %esp,%ebp +#ifndef NON_REENTRANT_FPU + subl $28,%esp +#endif NON_REENTRANT_FPU + + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi /* pointer to num */ + movl PARAM2,%ebx /* pointer to denom */ + +#ifdef PARANOID + testl $0x80000000, XsigH(%ebx) /* Divisor */ + je L_bugged +#endif PARANOID + + +/*---------------------------------------------------------------------------+ + | Divide: Return arg1/arg2 to 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 | + | | + +---------------------------------------------------------------------------*/ + + /* Save extended dividend in local register */ + + /* Divide by 2 to prevent overflow */ + clc + movl XsigH(%esi),%eax + rcrl %eax + movl %eax,FPU_accum_3 + movl XsigL(%esi),%eax + rcrl %eax + movl %eax,FPU_accum_2 + movl XsigLL(%esi),%eax + rcrl %eax + movl %eax,FPU_accum_1 + movl $0,%eax + rcrl %eax + movl %eax,FPU_accum_0 + + movl FPU_accum_2,%eax /* Get the current num */ + movl FPU_accum_3,%edx + +/*----------------------------------------------------------------------*/ +/* Initialization done. + Do the first 32 bits. */ + + /* We will divide by a number which is too large */ + movl XsigH(%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,FPU_result_3 /* Put the result in the answer */ + + mull XsigH(%ebx) /* mul by the ms dw of the denom */ + + subl %eax,FPU_accum_2 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_3 + + movl FPU_result_3,%eax /* Get the result back */ + mull XsigL(%ebx) /* now mul the ls dw of the denom */ + + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + sbbl $0,FPU_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 FPU_result_3 /* Correct the answer */ + + movl XsigL(%ebx),%eax + movl XsigH(%ebx),%edx + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + +#ifdef PARANOID + sbbl $0,FPU_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, FPU_accum_0 not used from now on */ +LDo_2nd_32_bits: + movl FPU_accum_2,%edx /* get the reduced num */ + movl FPU_accum_1,%eax + + /* need to check for possible subsequent overflow */ + cmpl XsigH(%ebx),%edx + jb LDo_2nd_div + ja LPrevent_2nd_overflow + + cmpl XsigL(%ebx),%eax + jb LDo_2nd_div + +LPrevent_2nd_overflow: +/* The numerator is greater or equal, would cause overflow */ + /* prevent overflow */ + subl XsigL(%ebx),%eax + sbbl XsigH(%ebx),%edx + movl %edx,FPU_accum_2 + movl %eax,FPU_accum_1 + + incl FPU_result_3 /* 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,FPU_result_2 /* Put the result in the answer */ + + mull XsigH(%ebx) /* mul by the ms dw of the denom */ + + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 +#endif PARANOID + + movl FPU_result_2,%eax /* Get the result back */ + mull XsigL(%ebx) /* now mul the ls dw of the denom */ + + subl %eax,FPU_accum_0 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */ + sbbl $0,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 +#endif PARANOID + + jz LDo_3rd_32_bits + +#ifdef PARANOID + cmpl $1,FPU_accum_2 + jne L_bugged_2 +#endif PARANOID + + /* need to subtract another once of the denom */ + movl XsigL(%ebx),%eax + movl XsigH(%ebx),%edx + subl %eax,FPU_accum_0 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_1 + sbbl $0,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 + jne L_bugged_2 +#endif PARANOID + + addl $1,FPU_result_2 /* Correct the answer */ + adcl $0,FPU_result_3 + +#ifdef PARANOID + jc L_bugged_2 /* Must check for non-zero result here */ +#endif PARANOID + +/*----------------------------------------------------------------------*/ +/* The division is essentially finished here, we just need to perform + tidying operations. + Deal with the 3rd 32 bits */ +LDo_3rd_32_bits: + /* We use an approximation for the third 32 bits. + To take account of the 3rd 32 bits of the divisor + (call them del), we subtract del * (a/b) */ + + movl FPU_result_3,%eax /* a/b */ + mull XsigLL(%ebx) /* del */ + + subl %edx,FPU_accum_1 + + /* A borrow indicates that the result is negative */ + jnb LTest_over + + movl XsigH(%ebx),%edx + addl %edx,FPU_accum_1 + + subl $1,FPU_result_2 /* Adjust the answer */ + sbbl $0,FPU_result_3 + + /* The above addition might not have been enough, check again. */ + movl FPU_accum_1,%edx /* get the reduced num */ + cmpl XsigH(%ebx),%edx /* denom */ + jb LDo_3rd_div + + movl XsigH(%ebx),%edx + addl %edx,FPU_accum_1 + + subl $1,FPU_result_2 /* Adjust the answer */ + sbbl $0,FPU_result_3 + jmp LDo_3rd_div + +LTest_over: + movl FPU_accum_1,%edx /* get the reduced num */ + + /* need to check for possible subsequent overflow */ + cmpl XsigH(%ebx),%edx /* denom */ + jb LDo_3rd_div + + /* prevent overflow */ + subl XsigH(%ebx),%edx + movl %edx,FPU_accum_1 + + addl $1,FPU_result_2 /* Reflect the subtraction in the answer */ + adcl $0,FPU_result_3 + +LDo_3rd_div: + movl FPU_accum_0,%eax + movl FPU_accum_1,%edx + divl XsigH(%ebx) + + movl %eax,FPU_result_1 /* Rough estimate of third word */ + + movl PARAM3,%esi /* pointer to answer */ + + movl FPU_result_1,%eax + movl %eax,XsigLL(%esi) + movl FPU_result_2,%eax + movl %eax,XsigL(%esi) + movl FPU_result_3,%eax + movl %eax,XsigH(%esi) + +L_exit: + popl %ebx + popl %edi + popl %esi + + leave + ret + + +#ifdef PARANOID +/* The logic is wrong if we got here */ +L_bugged: + pushl EX_INTERNAL|0x240 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged_1: + pushl EX_INTERNAL|0x241 + call EXCEPTION + pop %ebx + jmp L_exit + +L_bugged_2: + pushl EX_INTERNAL|0x242 + call EXCEPTION + pop %ebx + jmp L_exit +#endif PARANOID diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/div_small.S linux/arch/i386/math-emu/div_small.S --- v1.1.76/linux/arch/i386/math-emu/div_small.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/div_small.S Wed Dec 1 14:44:16 1993 @@ -0,0 +1,50 @@ + .file "div_small.S" +/*---------------------------------------------------------------------------+ + | div_small.S | + | | + | Divide a 64 bit integer by a 32 bit integer & return remainder. | + | | + | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | 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 -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/errors.c linux/arch/i386/math-emu/errors.c --- v1.1.76/linux/arch/i386/math-emu/errors.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/errors.c Mon Aug 1 08:19:13 1994 @@ -0,0 +1,671 @@ +/*---------------------------------------------------------------------------+ + | errors.c | + | | + | The error handling functions for wm-FPU-emu | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | 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 + +#include + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.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; + unsigned long address = FPU_ORIG_EIP; + + RE_ENTRANT_CHECK_OFF; + /* No need to verify_area(), we have previously fetched these bytes. */ + printk("Unimplemented FPU Opcode at eip=%p : ", (void *) address); + if ( FPU_CS == USER_CS ) + { + while ( 1 ) + { + byte1 = get_fs_byte((unsigned char *) address); + if ( (byte1 & 0xf8) == 0xd8 ) break; + printk("[%02x]", byte1); + address++; + } + printk("%02x ", byte1); + FPU_modrm = get_fs_byte(1 + (unsigned char *) address); + + if (FPU_modrm >= 0300) + printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); + else + printk("/%d\n", (FPU_modrm >> 3) & 7); + } + else + { + printk("cs selector = %04x\n", FPU_CS); + } + + RE_ENTRANT_CHECK_ON; + + EXCEPTION(EX_Invalid); + +} + + +/* + Called for opcodes which are illegal and which are known to result in a + SIGILL with a real 80486. + */ +void FPU_illegal(void) +{ + math_abort(FPU_info,SIGILL); +} + + + +void emu_printall() +{ + int i; + static char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR", + "DeNorm", "Inf", "NaN", "Empty" }; + unsigned char byte1, FPU_modrm; + unsigned long address = FPU_ORIG_EIP; + + RE_ENTRANT_CHECK_OFF; + /* No need to verify_area(), we have previously fetched these bytes. */ + printk("At %p:", (void *) address); + if ( FPU_CS == USER_CS ) + { +#define MAX_PRINTED_BYTES 20 + for ( i = 0; i < MAX_PRINTED_BYTES; i++ ) + { + byte1 = get_fs_byte((unsigned char *) address); + if ( (byte1 & 0xf8) == 0xd8 ) + { + printk(" %02x", byte1); + break; + } + printk(" [%02x]", byte1); + address++; + } + if ( i == MAX_PRINTED_BYTES ) + printk(" [more..]\n"); + else + { + FPU_modrm = get_fs_byte(1 + (unsigned char *) address); + + if (FPU_modrm >= 0300) + printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); + else + printk(" /%d, mod=%d rm=%d\n", + (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7); + } + } + else + { + printk("%04x\n", FPU_CS); + } + + partial_status = status_word(); + +#ifdef DEBUGGING +if ( partial_status & SW_Backward ) printk("SW: backward compatibility\n"); +if ( partial_status & SW_C3 ) printk("SW: condition bit 3\n"); +if ( partial_status & SW_C2 ) printk("SW: condition bit 2\n"); +if ( partial_status & SW_C1 ) printk("SW: condition bit 1\n"); +if ( partial_status & SW_C0 ) printk("SW: condition bit 0\n"); +if ( partial_status & SW_Summary ) printk("SW: exception summary\n"); +if ( partial_status & SW_Stack_Fault ) printk("SW: stack fault\n"); +if ( partial_status & SW_Precision ) printk("SW: loss of precision\n"); +if ( partial_status & SW_Underflow ) printk("SW: underflow\n"); +if ( partial_status & SW_Overflow ) printk("SW: overflow\n"); +if ( partial_status & SW_Zero_Div ) printk("SW: divide by zero\n"); +if ( partial_status & SW_Denorm_Op ) printk("SW: denormalized operand\n"); +if ( partial_status & SW_Invalid ) printk("SW: invalid operation\n"); +#endif DEBUGGING + + printk(" SW: b=%d st=%ld es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n", + partial_status & 0x8000 ? 1 : 0, /* busy */ + (partial_status & 0x3800) >> 11, /* stack top pointer */ + partial_status & 0x80 ? 1 : 0, /* Error summary status */ + partial_status & 0x40 ? 1 : 0, /* Stack flag */ + partial_status & SW_C3?1:0, partial_status & SW_C2?1:0, /* cc */ + partial_status & SW_C1?1:0, partial_status & SW_C0?1:0, /* cc */ + partial_status & SW_Precision?1:0, partial_status & SW_Underflow?1:0, + partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0, + partial_status & SW_Denorm_Op?1:0, partial_status & SW_Invalid?1:0); + +printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld 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: +#if 0 + printk("st(%d) %c .0000 0000 0000 0000 ", + i, r->sign ? '-' : '+'); + break; +#endif + case TW_Valid: + case TW_NaN: +/* case TW_Denormal: */ + case TW_Infinity: + printk("st(%d) %c .%04lx %04lx %04lx %04lx e%+-6ld ", i, + r->sign ? '-' : '+', + (long)(r->sigh >> 16), + (long)(r->sigh & 0xFFFF), + (long)(r->sigl >> 16), + (long)(r->sigl & 0xFFFF), + r->exp - EXP_BIAS + 1); + break; + default: + printk("Whoops! Error in errors.c "); + break; + } + printk("%s\n", tag_desc[(int) (unsigned) r->tag]); + } + +#ifdef OBSOLETE + printk("[data] %c .%04lx %04lx %04lx %04lx e%+-6ld ", + 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); + printk("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]); +#endif OBSOLETE + RE_ENTRANT_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 fpu_etc.c + 0x1nn in a *.c file: + 0x101 in reg_add_sub.c + 0x102 in reg_mul.c + 0x104 in poly_atan.c + 0x105 in reg_mul.c + 0x107 in fpu_trig.c + 0x108 in reg_compare.c + 0x109 in reg_compare.c + 0x110 in reg_add_sub.c + 0x111 in fpe_entry.c + 0x112 in fpu_trig.c + 0x113 in errors.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 + 0x125 in fpu_trig.c + 0x126 in fpu_entry.c + 0x127 in poly_2xm1.c + 0x128 in fpu_entry.c + 0x129 in fpu_entry.c + 0x130 in get_address.c + 0x131 in get_address.c + 0x132 in get_address.c + 0x133 in get_address.c + 0x140 in load_store.c + 0x141 in load_store.c + 0x150 in poly_sin.c + 0x151 in poly_sin.c + 0x160 in reg_ld_str.c + 0x161 in reg_ld_str.c + 0x162 in reg_ld_str.c + 0x163 in reg_ld_str.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 + 0x220 in reg_norm.S + 0x221 in reg_norm.S + 0x230 in reg_round.S + 0x231 in reg_round.S + 0x232 in reg_round.S + 0x233 in reg_round.S + 0x234 in reg_round.S + 0x235 in reg_round.S + 0x236 in reg_round.S + 0x240 in div_Xsig.S + 0x241 in div_Xsig.S + 0x242 in div_Xsig.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! */ + partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward); + } + else + { + /* Extract only the bits which we use to set the status word */ + n &= (SW_Exc_Mask); + /* Set the corresponding exception bit */ + partial_status |= n; + /* Set summary bits iff exception isn't masked */ + if ( partial_status & ~control_word & CW_Exceptions ) + partial_status |= (SW_Summary | SW_Backward); + 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. */ + partial_status &= ~SW_C1; + } + } + + RE_ENTRANT_CHECK_OFF; + if ( (~control_word & n & CW_Exceptions) || (n == EX_INTERNAL) ) + { +#ifdef PRINT_MESSAGES + /* My message from the sponsor */ + printk(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 + printk("FP Exception: %s!\n", exception_names[i].name); +#endif PRINT_MESSAGES + } + else + printk("FPU emulator: Unknown Exception: 0x%04x!\n", n); + + if ( n == EX_INTERNAL ) + { + printk("FPU 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; */ + } + RE_ENTRANT_CHECK_ON; + +#ifdef __DEBUG__ + math_abort(FPU_info,SIGFPE); +#endif __DEBUG__ + +} + + +/* Real operation attempted on two operands, one a NaN. */ +/* Returns nz if the exception is unmasked */ +asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest) +{ + FPU_REG const *x; + int signalling; + + /* The default result for the case of two "equal" NaNs (signs may + differ) is chosen to reproduce 80486 behaviour */ + x = a; + if (a->tag == TW_NaN) + { + if (b->tag == TW_NaN) + { + signalling = !(a->sigh & b->sigh & 0x40000000); + /* find the "larger" */ + if ( significand(a) < significand(b) ) + 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 0; + } + + 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 !(control_word & CW_Invalid); +} + + +/* Invalid arith operation on Valid registers */ +/* Returns nz if the exception is unmasked */ +asmlinkage int arith_invalid(FPU_REG *dest) +{ + + EXCEPTION(EX_Invalid); + + if ( control_word & CW_Invalid ) + { + /* The masked response */ + reg_move(&CONST_QNaN, dest); + } + + return !(control_word & CW_Invalid); + +} + + +/* Divide a finite number by zero */ +asmlinkage int 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 !(control_word & CW_ZeroDiv); + +} + + +/* This may be called often, so keep it lean */ +int set_precision_flag(int flags) +{ + if ( control_word & CW_Precision ) + { + partial_status &= ~(SW_C1 & flags); + partial_status |= flags; /* The masked response */ + return 0; + } + else + { + exception(flags); + return 1; + } +} + + +/* This may be called often, so keep it lean */ +asmlinkage void set_precision_flag_up(void) +{ + if ( control_word & CW_Precision ) + partial_status |= (SW_Precision | SW_C1); /* The masked response */ + else + exception(EX_Precision | SW_C1); + +} + + +/* This may be called often, so keep it lean */ +asmlinkage void set_precision_flag_down(void) +{ + if ( control_word & CW_Precision ) + { /* The masked response */ + partial_status &= ~SW_C1; + partial_status |= SW_Precision; + } + else + exception(EX_Precision); +} + + +asmlinkage int denormal_operand(void) +{ + if ( control_word & CW_Denormal ) + { /* The masked response */ + partial_status |= SW_Denorm_Op; + return 0; + } + else + { + exception(EX_Denormal); + return 1; + } +} + + +asmlinkage int 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)); + } + + EXCEPTION(EX_Overflow); + if ( control_word & CW_Overflow ) + { + /* The overflow exception is masked. */ + /* By definition, precision is lost. + The roundup bit (C1) is also set because we have + "rounded" upwards to Infinity. */ + EXCEPTION(EX_Precision | SW_C1); + return !(control_word & CW_Precision); + } + + return !(control_word & CW_Overflow); + +} + + +asmlinkage int arith_underflow(FPU_REG *dest) +{ + + if ( control_word & CW_Underflow ) + { + /* The masked response */ + if ( dest->exp <= EXP_UNDER - 63 ) + { + reg_move(&CONST_Z, dest); + partial_status &= ~SW_C1; /* Round down. */ + } + } + else + { + /* Add the magic number to the exponent. */ + dest->exp += (3 * (1 << 13)); + } + + EXCEPTION(EX_Underflow); + if ( control_word & CW_Underflow ) + { + /* The underflow exception is masked. */ + EXCEPTION(EX_Precision); + return !(control_word & CW_Precision); + } + + return !(control_word & CW_Underflow); + +} + + +void stack_overflow(void) +{ + + if ( control_word & CW_Invalid ) + { + /* The masked response */ + top--; + reg_move(&CONST_QNaN, &st(0)); + } + + EXCEPTION(EX_StackOver); + + return; + +} + + +void stack_underflow(void) +{ + + if ( control_word & CW_Invalid ) + { + /* The masked response */ + reg_move(&CONST_QNaN, &st(0)); + } + + 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 -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/exception.h linux/arch/i386/math-emu/exception.h --- v1.1.76/linux/arch/i386/math-emu/exception.h Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/exception.h Wed Dec 1 14:44:16 1993 @@ -0,0 +1,53 @@ +/*---------------------------------------------------------------------------+ + | exception.h | + | | + | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + + +#ifdef __ASSEMBLER__ +#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 */ + + +#define PRECISION_LOST_UP Const_((EX_Precision | SW_C1)) +#define PRECISION_LOST_DOWN Const_(EX_Precision) + + +#ifndef __ASSEMBLER__ + +#ifdef DEBUG +#define EXCEPTION(x) { printk("exception in %s at line %d\n", \ + __FILE__, __LINE__); exception(x); } +#else +#define EXCEPTION(x) exception(x) +#endif + +#endif __ASSEMBLER__ + +#endif _EXCEPTION_H_ diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/fpu_arith.c linux/arch/i386/math-emu/fpu_arith.c --- v1.1.76/linux/arch/i386/math-emu/fpu_arith.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/fpu_arith.c Thu Jun 2 10:28:23 1994 @@ -0,0 +1,179 @@ +/*---------------------------------------------------------------------------+ + | 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 billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "status_w.h" + + +void fadd__() +{ + /* fadd st,st(i) */ + clear_C1(); + reg_add(&st(0), &st(FPU_rm), &st(0), control_word); +} + + +void fmul__() +{ + /* fmul st,st(i) */ + clear_C1(); + reg_mul(&st(0), &st(FPU_rm), &st(0), control_word); +} + + + +void fsub__() +{ + /* fsub st,st(i) */ + clear_C1(); + reg_sub(&st(0), &st(FPU_rm), &st(0), control_word); +} + + +void fsubr_() +{ + /* fsubr st,st(i) */ + clear_C1(); + reg_sub(&st(FPU_rm), &st(0), &st(0), control_word); +} + + +void fdiv__() +{ + /* fdiv st,st(i) */ + clear_C1(); + reg_div(&st(0), &st(FPU_rm), &st(0), control_word); +} + + +void fdivr_() +{ + /* fdivr st,st(i) */ + clear_C1(); + reg_div(&st(FPU_rm), &st(0), &st(0), control_word); +} + + + +void fadd_i() +{ + /* fadd st(i),st */ + clear_C1(); + reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); +} + + +void fmul_i() +{ + /* fmul st(i),st */ + clear_C1(); + reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); +} + + +void fsubri() +{ + /* fsubr st(i),st */ + /* This is the sense of the 80486 manual + reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */ + clear_C1(); + reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); +} + + +void fsub_i() +{ + /* fsub st(i),st */ + /* This is the sense of the 80486 manual + reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */ + clear_C1(); + reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); +} + + +void fdivri() +{ + /* fdivr st(i),st */ + clear_C1(); + reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); +} + + +void fdiv_i() +{ + /* fdiv st(i),st */ + clear_C1(); + reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); +} + + + +void faddp_() +{ + /* faddp st(i),st */ + clear_C1(); + if ( !reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) + pop(); +} + + +void fmulp_() +{ + /* fmulp st(i),st */ + clear_C1(); + if ( !reg_mul(&st(0), &st(FPU_rm), &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), &st(0), &st(FPU_rm), control_word); */ + clear_C1(); + if ( !reg_sub(&st(0), &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(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */ + clear_C1(); + if ( !reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) + pop(); +} + + +void fdivrp() +{ + /* fdivrp st(i),st */ + clear_C1(); + if ( !reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) + pop(); +} + + +void fdivp_() +{ + /* fdivp st(i),st */ + clear_C1(); + if ( !reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) + pop(); +} + diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/fpu_asm.h linux/arch/i386/math-emu/fpu_asm.h --- v1.1.76/linux/arch/i386/math-emu/fpu_asm.h Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/fpu_asm.h Wed Dec 1 14:44:16 1993 @@ -0,0 +1,30 @@ +/*---------------------------------------------------------------------------+ + | fpu_asm.h | + | | + | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#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 -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/fpu_aux.c linux/arch/i386/math-emu/fpu_aux.c --- v1.1.76/linux/arch/i386/math-emu/fpu_aux.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/fpu_aux.c Thu Jun 2 10:28:23 1994 @@ -0,0 +1,184 @@ +/*---------------------------------------------------------------------------+ + | fpu_aux.c | + | | + | Code to implement some of the FPU auxiliary instructions. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "control_w.h" + + +static void fnop(void) +{ +} + +void fclex(void) +{ + partial_status &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision| + SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op| + SW_Invalid); + no_ip_update = 1; +} + +/* Needs to be externally visible */ +void finit() +{ + int r; + control_word = 0x037f; + partial_status = 0; + top = 0; /* We don't keep top in the status word internally. */ + for (r = 0; r < 8; r++) + { + regs[r].tag = TW_Empty; + } + /* The behaviour is different to that detailed in + Section 15.1.6 of the Intel manual */ + operand_address.offset = 0; + operand_address.selector = 0; + instruction_address.offset = 0; + instruction_address.selector = 0; + instruction_address.opcode = 0; + no_ip_update = 1; +} + +/* + * These are nops on the i387.. + */ +#define feni fnop +#define fdisi fnop +#define fsetpm fnop + +static FUNC const finit_table[] = { + feni, fdisi, fclex, finit, + fsetpm, FPU_illegal, FPU_illegal, FPU_illegal +}; + +void finit_() +{ + (finit_table[FPU_rm])(); +} + + +static void fstsw_ax(void) +{ + *(short *) &FPU_EAX = status_word(); + no_ip_update = 1; +} + +static FUNC const fstsw_table[] = { + fstsw_ax, FPU_illegal, FPU_illegal, FPU_illegal, + FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal +}; + +void fstsw_() +{ + (fstsw_table[FPU_rm])(); +} + + +static FUNC const fp_nop_table[] = { + fnop, FPU_illegal, FPU_illegal, FPU_illegal, + FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal +}; + +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 & CW_Invalid ) + { + /* The masked response */ + stack_underflow(); + } + else + EXCEPTION(EX_StackUnder); + } + +} + + +void fxch_i() +{ + /* fxch st(i) */ + FPU_REG t; + register FPU_REG *sti_ptr = &st(FPU_rm), *st0_ptr = &st(0); + + if ( st0_ptr->tag == TW_Empty ) + { + if ( sti_ptr->tag == TW_Empty ) + { + stack_underflow(); + stack_underflow_i(FPU_rm); + return; + } + if ( control_word & CW_Invalid ) + reg_move(sti_ptr, st0_ptr); /* Masked response */ + stack_underflow_i(FPU_rm); + return; + } + if ( sti_ptr->tag == TW_Empty ) + { + if ( control_word & CW_Invalid ) + reg_move(st0_ptr, sti_ptr); /* Masked response */ + stack_underflow(); + return; + } + clear_C1(); + reg_move(st0_ptr, &t); + reg_move(sti_ptr, 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(&st(0), &st(FPU_rm)); +} + + +void fstp_i() +{ + /* fstp st(i) */ + reg_move(&st(0), &st(FPU_rm)); + pop(); +} + diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/fpu_emu.h linux/arch/i386/math-emu/fpu_emu.h --- v1.1.76/linux/arch/i386/math-emu/fpu_emu.h Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/fpu_emu.h Mon Aug 1 08:19:13 1994 @@ -0,0 +1,171 @@ +/*---------------------------------------------------------------------------+ + | fpu_emu.h | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + + +#ifndef _FPU_EMU_H_ +#define _FPU_EMU_H_ + +/* + * Define DENORM_OPERAND to make the emulator detect denormals + * and use the denormal flag of the status word. Note: this only + * affects the flag and corresponding interrupt, the emulator + * will always generate denormals and operate upon them as required. + */ +#define DENORM_OPERAND + +/* + * Define PECULIAR_486 to get a closer approximation to 80486 behaviour, + * rather than behaviour which appears to be cleaner. + * 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 __ASSEMBLER__ +#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 EXP_Infinity EXP_OVER +#define EXP_NaN EXP_OVER + +#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 */ + + +#ifndef __ASSEMBLER__ + +#include +#include + +/* +#define RE_ENTRANT_CHECKING + */ + +#ifdef RE_ENTRANT_CHECKING +extern char emulating; +# define RE_ENTRANT_CHECK_OFF emulating = 0 +# define RE_ENTRANT_CHECK_ON emulating = 1 +#else +# define RE_ENTRANT_CHECK_OFF +# define RE_ENTRANT_CHECK_ON +#endif RE_ENTRANT_CHECKING + +#define FWAIT_OPCODE 0x9b +#define OP_SIZE_PREFIX 0x66 +#define ADDR_SIZE_PREFIX 0x67 +#define PREFIX_CS 0x2e +#define PREFIX_DS 0x3e +#define PREFIX_ES 0x26 +#define PREFIX_SS 0x36 +#define PREFIX_FS 0x64 +#define PREFIX_GS 0x65 +#define PREFIX_REPE 0xf3 +#define PREFIX_REPNE 0xf2 +#define PREFIX_LOCK 0xf0 +#define PREFIX_CS_ 1 +#define PREFIX_DS_ 2 +#define PREFIX_ES_ 3 +#define PREFIX_FS_ 4 +#define PREFIX_GS_ 5 +#define PREFIX_SS_ 6 +#define PREFIX_DEFAULT 7 + +struct address { + unsigned int offset; + unsigned int selector:16; + unsigned int opcode:11; + unsigned int empty:5; +}; +typedef void (*FUNC)(void); +typedef struct fpu_reg FPU_REG; +typedef void (*FUNC_ST0)(FPU_REG *st0_ptr); +typedef struct { unsigned char address_size, operand_size, segment; } + overrides; +/* This structure is 32 bits: */ +typedef struct { overrides override; + unsigned char default_mode; } fpu_addr_modes; +/* PROTECTED has a restricted meaning in the emulator; it is used + to signal that the emulator needs to do special things to ensure + that protection is respected in a segmented model. */ +#define PROTECTED 4 +#define SIXTEEN 1 /* We rely upon this being 1 (true) */ +#define VM86 SIXTEEN +#define PM16 (SIXTEEN | PROTECTED) +#define SEG32 PROTECTED +extern unsigned char const data_sizes_16[32]; + +#define st(x) ( regs[((top+x) &7 )] ) + +#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty) +#define NOT_EMPTY(i) (st(i).tag != TW_Empty) +#define NOT_EMPTY_ST0 (st0_tag ^ TW_Empty) + +#define pop() { regs[(top++ & 7 )].tag = TW_Empty; } +#define poppop() { regs[((top + 1) & 7 )].tag \ + = regs[(top & 7 )].tag = TW_Empty; \ + top += 2; } + +/* push() does not affect the tags */ +#define push() { top--; } + + +#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); } + +#define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] ) + + +/*----- Prototypes for functions written in assembler -----*/ +/* extern void reg_move(FPU_REG *a, FPU_REG *b); */ + +asmlinkage void normalize(FPU_REG *x); +asmlinkage void normalize_nuo(FPU_REG *x); +asmlinkage int reg_div(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w); +asmlinkage int reg_u_sub(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w); +asmlinkage int reg_u_mul(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w); +asmlinkage int reg_u_div(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w); +asmlinkage int reg_u_add(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w); +asmlinkage int wm_sqrt(FPU_REG *n, unsigned int control_w); +asmlinkage unsigned shrx(void *l, unsigned x); +asmlinkage unsigned shrxs(void *v, unsigned x); +asmlinkage unsigned long div_small(unsigned long long *x, unsigned long y); +asmlinkage void round_reg(FPU_REG *arg, unsigned int extent, + unsigned int control_w); + +#ifndef MAKING_PROTO +#include "fpu_proto.h" +#endif + +#endif __ASSEMBLER__ + +#endif _FPU_EMU_H_ diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/fpu_entry.c linux/arch/i386/math-emu/fpu_entry.c --- v1.1.76/linux/arch/i386/math-emu/fpu_entry.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/fpu_entry.c Sun Nov 27 20:19:52 1994 @@ -0,0 +1,690 @@ +/*---------------------------------------------------------------------------+ + | fpu_entry.c | + | | + | The entry function for wm-FPU-emu | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | See the files "README" and "COPYING" for further copyright and warranty | + | information. | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | 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 + +#include + +#include "fpu_system.h" +#include "fpu_emu.h" +#include "exception.h" +#include "control_w.h" +#include "status_w.h" + +#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */ + +#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 const st_instr_table[64] = { + fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_, + fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_, + fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_, + fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_, + fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, + fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, + fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, + fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, +}; + +#else /* Support only documented FPU op-codes */ + +static FUNC const st_instr_table[64] = { + fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__, + fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__, + fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__, + fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__, + fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, + fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, + fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, + fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, +}; + +#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 const type_table[64] = { + _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_, + _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_, + _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, + _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, + _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 const type_table[64] = { + _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_, + _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_, + _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_, + _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_, + _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 + + +#ifdef RE_ENTRANT_CHECKING +char emulating=0; +#endif RE_ENTRANT_CHECKING + +static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip, + overrides *override); + +asmlinkage void math_emulate(long arg) +{ + unsigned char FPU_modrm, byte1; + unsigned short code; + fpu_addr_modes addr_modes; + int unmasked; + FPU_REG loaded_data; + void *data_address; + struct address data_sel_off; + struct address entry_sel_off; + unsigned long code_base = 0; + unsigned long code_limit = 0; /* Initialized to stop compiler warnings */ + char st0_tag; + FPU_REG *st0_ptr; + struct desc_struct code_descriptor; + +#ifdef RE_ENTRANT_CHECKING + if ( emulating ) + { + printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n"); + } + RE_ENTRANT_CHECK_ON; +#endif RE_ENTRANT_CHECKING + + if (!current->used_math) + { + int i; + for ( i = 0; i < 8; i++ ) + { + /* Make sure that the registers are compatible + with the assumptions of the emulator. */ + regs[i].exp = 0; + regs[i].sigh = 0x80000000; + } + finit(); + current->used_math = 1; + } + + SETUP_DATA_AREA(arg); + + FPU_ORIG_EIP = FPU_EIP; + + if ( (FPU_EFLAGS & 0x00020000) != 0 ) + { + /* Virtual 8086 mode */ + addr_modes.default_mode = VM86; + FPU_EIP += code_base = FPU_CS << 4; + code_limit = code_base + 0xffff; /* Assumes code_base <= 0xffff0000 */ + } + else if ( FPU_CS == USER_CS && FPU_DS == USER_DS ) + { + addr_modes.default_mode = 0; + } + else if ( FPU_CS == KERNEL_CS ) + { + printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP); + panic("Math emulation needed in kernel"); + } + else + { + + if ( (FPU_CS & 4) != 4 ) /* Must be in the LDT */ + { + /* Can only handle segmented addressing via the LDT + for now, and it must be 16 bit */ + printk("FPU emulator: Unsupported addressing mode\n"); + math_abort(FPU_info, SIGILL); + } + + if ( SEG_D_SIZE(code_descriptor = LDT_DESCRIPTOR(FPU_CS)) ) + { + /* The above test may be wrong, the book is not clear */ + /* Segmented 32 bit protected mode */ + addr_modes.default_mode = SEG32; + } + else + { + /* 16 bit protected mode */ + addr_modes.default_mode = PM16; + } + FPU_EIP += code_base = SEG_BASE_ADDR(code_descriptor); + code_limit = code_base + + (SEG_LIMIT(code_descriptor)+1) * SEG_GRANULARITY(code_descriptor) + - 1; + if ( code_limit < code_base ) code_limit = 0xffffffff; + } + + FPU_lookahead = 1; + if (current->flags & PF_PTRACED) + FPU_lookahead = 0; + + if ( !valid_prefix(&byte1, (unsigned char **)&FPU_EIP, + &addr_modes.override) ) + { + RE_ENTRANT_CHECK_OFF; + printk("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n" + "FPU emulator: self-modifying code! (emulation impossible)\n", + byte1); + RE_ENTRANT_CHECK_ON; + EXCEPTION(EX_INTERNAL|0x126); + math_abort(FPU_info,SIGILL); + } + +do_another_FPU_instruction: + + no_ip_update = 0; + + FPU_EIP++; /* We have fetched the prefix and first code bytes. */ + + if ( addr_modes.default_mode ) + { + /* This checks for the minimum instruction bytes. + We also need to check any extra (address mode) code access. */ + if ( FPU_EIP > code_limit ) + math_abort(FPU_info,SIGSEGV); + } + + if ( (byte1 & 0xf8) != 0xd8 ) + { + if ( byte1 == FWAIT_OPCODE ) + { + if (partial_status & SW_Summary) + goto do_the_FPU_interrupt; + else + goto FPU_fwait_done; + } +#ifdef PARANOID + EXCEPTION(EX_INTERNAL|0x128); + math_abort(FPU_info,SIGILL); +#endif PARANOID + } + + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + FPU_modrm = get_fs_byte((unsigned char *) FPU_EIP); + RE_ENTRANT_CHECK_ON; + FPU_EIP++; + + if (partial_status & 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, + but a real 80486 uses the following instructions: + fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex. + */ + code = (FPU_modrm << 8) | byte1; + if ( ! ( (((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */ + (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv, + fnstsw */ + ((code & 0xc000) != 0xc000))) ) ) + { + /* + * 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: + instruction_address.selector = status_word(); + operand_address.selector = tag_word(); + partial_status = 0; + top = 0; + { + int r; + for (r = 0; r < 8; r++) + { + regs[r].tag = TW_Empty; + } + } + + FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */ + + RE_ENTRANT_CHECK_OFF; + current->tss.trap_no = 16; + current->tss.error_code = 0; + send_sig(SIGFPE, current, 1); + return; + } + } + + entry_sel_off.offset = FPU_ORIG_EIP; + entry_sel_off.selector = FPU_CS; + entry_sel_off.opcode = (byte1 << 8) | FPU_modrm; + + FPU_rm = FPU_modrm & 7; + + if ( FPU_modrm < 0300 ) + { + /* All of these instructions use the mod/rm byte to get a data address */ + + if ( (addr_modes.default_mode & SIXTEEN) + ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX) ) + data_address = get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off, + addr_modes); + else + data_address = get_address(FPU_modrm, &FPU_EIP, &data_sel_off, + addr_modes); + + if ( addr_modes.default_mode ) + { + if ( FPU_EIP-1 > code_limit ) + math_abort(FPU_info,SIGSEGV); + } + + if ( !(byte1 & 1) ) + { + unsigned short status1 = partial_status; + + st0_ptr = &st(0); + st0_tag = st0_ptr->tag; + + /* Stack underflow has priority */ + if ( NOT_EMPTY_ST0 ) + { + if ( addr_modes.default_mode & PROTECTED ) + { + /* This table works for 16 and 32 bit protected mode */ + if ( access_limit < data_sizes_16[(byte1 >> 1) & 3] ) + math_abort(FPU_info,SIGSEGV); + } + + unmasked = 0; /* Do this here to stop compiler warnings. */ + switch ( (byte1 >> 1) & 3 ) + { + case 0: + unmasked = reg_load_single((float *)data_address, + &loaded_data); + break; + case 1: + reg_load_int32((long *)data_address, &loaded_data); + break; + case 2: + unmasked = reg_load_double((double *)data_address, + &loaded_data); + break; + case 3: + reg_load_int16((short *)data_address, &loaded_data); + break; + } + + /* No more access to user memory, it is safe + to use static data now */ + + /* NaN operands have the next priority. */ + /* We have to delay looking at st(0) until after + loading the data, because that data might contain an SNaN */ + if ( (st0_tag == TW_NaN) || + (loaded_data.tag == TW_NaN) ) + { + /* Restore the status word; we might have loaded a + denormal. */ + partial_status = status1; + if ( (FPU_modrm & 0x30) == 0x10 ) + { + /* fcom or fcomp */ + EXCEPTION(EX_Invalid); + setcc(SW_C3 | SW_C2 | SW_C0); + if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) ) + pop(); /* fcomp, masked, so we pop. */ + } + else + { +#ifdef PECULIAR_486 + /* This is not really needed, but gives behaviour + identical to an 80486 */ + if ( (FPU_modrm & 0x28) == 0x20 ) + /* fdiv or fsub */ + real_2op_NaN(&loaded_data, st0_ptr, + st0_ptr); + else +#endif PECULIAR_486 + /* fadd, fdivr, fmul, or fsubr */ + real_2op_NaN(st0_ptr, &loaded_data, + st0_ptr); + } + goto reg_mem_instr_done; + } + + if ( unmasked && !((FPU_modrm & 0x30) == 0x10) ) + { + /* Is not a comparison instruction. */ + if ( (FPU_modrm & 0x38) == 0x38 ) + { + /* fdivr */ + if ( (st0_tag == TW_Zero) && + (loaded_data.tag == TW_Valid) ) + { + if ( divide_by_zero(loaded_data.sign, + st0_ptr) ) + { + /* We use the fact here that the unmasked + exception in the loaded data was for a + denormal operand */ + /* Restore the state of the denormal op bit */ + partial_status &= ~SW_Denorm_Op; + partial_status |= status1 & SW_Denorm_Op; + } + } + } + goto reg_mem_instr_done; + } + + switch ( (FPU_modrm >> 3) & 7 ) + { + case 0: /* fadd */ + clear_C1(); + reg_add(st0_ptr, &loaded_data, st0_ptr, + control_word); + break; + case 1: /* fmul */ + clear_C1(); + reg_mul(st0_ptr, &loaded_data, st0_ptr, + control_word); + break; + case 2: /* fcom */ + compare_st_data(&loaded_data); + break; + case 3: /* fcomp */ + if ( !compare_st_data(&loaded_data) && !unmasked ) + pop(); + break; + case 4: /* fsub */ + clear_C1(); + reg_sub(st0_ptr, &loaded_data, st0_ptr, + control_word); + break; + case 5: /* fsubr */ + clear_C1(); + reg_sub(&loaded_data, st0_ptr, st0_ptr, + control_word); + break; + case 6: /* fdiv */ + clear_C1(); + reg_div(st0_ptr, &loaded_data, st0_ptr, + control_word); + break; + case 7: /* fdivr */ + clear_C1(); + if ( st0_tag == TW_Zero ) + partial_status = status1; /* Undo any denorm tag, + zero-divide has priority. */ + reg_div(&loaded_data, st0_ptr, 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) && (control_word & CW_Invalid) ) + pop(); /* fcomp */ + } + else + stack_underflow(); + } + reg_mem_instr_done: + operand_address = data_sel_off; + } + else + { + if ( !(no_ip_update = + load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1, + addr_modes, data_address)) ) + { + operand_address = data_sel_off; + } + } + + } + else + { + /* None of these instructions access user memory */ + unsigned char instr_index = (FPU_modrm & 0x38) | (byte1 & 7); + +#ifdef PECULIAR_486 + /* This is supposed to be undefined, but a real 80486 seems + to do this: */ + operand_address.offset = 0; + operand_address.selector = FPU_DS; +#endif PECULIAR_486 + + st0_ptr = &st(0); + st0_tag = st0_ptr->tag; + switch ( type_table[(int) instr_index] ) + { + case _NONE_: /* also _REGIc: _REGIn */ + break; + case _REG0_: + if ( !NOT_EMPTY_ST0 ) + { + stack_underflow(); + goto FPU_instruction_done; + } + break; + case _REGIi: + if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) + { + stack_underflow_i(FPU_rm); + goto FPU_instruction_done; + } + break; + case _REGIp: + if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) + { + stack_underflow_pop(FPU_rm); + goto FPU_instruction_done; + } + break; + case _REGI_: + if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) + { + stack_underflow(); + goto FPU_instruction_done; + } + break; + case _PUSH_: /* Only used by the fld st(i) instruction */ + break; + case _null_: + FPU_illegal(); + goto FPU_instruction_done; + default: + EXCEPTION(EX_INTERNAL|0x111); + goto FPU_instruction_done; + } + (*st_instr_table[(int) instr_index])(); + +FPU_instruction_done: + ; + } + + if ( ! no_ip_update ) + instruction_address = entry_sel_off; + +FPU_fwait_done: + +#ifdef DEBUG + RE_ENTRANT_CHECK_OFF; + emu_printall(); + RE_ENTRANT_CHECK_ON; +#endif DEBUG + + if (FPU_lookahead && !need_resched) + { + FPU_ORIG_EIP = FPU_EIP - code_base; + if ( valid_prefix(&byte1, (unsigned char **)&FPU_EIP, + &addr_modes.override) ) + goto do_another_FPU_instruction; + } + + if ( addr_modes.default_mode ) + FPU_EIP -= code_base; + + RE_ENTRANT_CHECK_OFF; +} + + +/* Support for prefix bytes is not yet complete. To properly handle + all prefix bytes, further changes are needed in the emulator code + which accesses user address space. Access to separate segments is + important for msdos emulation. */ +static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip, + overrides *override) +{ + unsigned char byte; + unsigned char *ip = *fpu_eip; + + *override = (overrides) { 0, 0, PREFIX_DEFAULT }; /* defaults */ + + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + byte = get_fs_byte(ip); + RE_ENTRANT_CHECK_ON; + + while ( 1 ) + { + switch ( byte ) + { + case ADDR_SIZE_PREFIX: + override->address_size = ADDR_SIZE_PREFIX; + goto do_next_byte; + + case OP_SIZE_PREFIX: + override->operand_size = OP_SIZE_PREFIX; + goto do_next_byte; + + case PREFIX_CS: + override->segment = PREFIX_CS_; + goto do_next_byte; + case PREFIX_ES: + override->segment = PREFIX_ES_; + goto do_next_byte; + case PREFIX_SS: + override->segment = PREFIX_SS_; + goto do_next_byte; + case PREFIX_FS: + override->segment = PREFIX_FS_; + goto do_next_byte; + case PREFIX_GS: + override->segment = PREFIX_GS_; + goto do_next_byte; + case PREFIX_DS: + override->segment = PREFIX_DS_; + goto do_next_byte; + +/* lock is not a valid prefix for FPU instructions, + let the cpu handle it to generate a SIGILL. */ +/* case PREFIX_LOCK: */ + + /* rep.. prefixes have no meaning for FPU instructions */ + case PREFIX_REPE: + case PREFIX_REPNE: + + do_next_byte: + ip++; + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + byte = get_fs_byte(ip); + RE_ENTRANT_CHECK_ON; + break; + case FWAIT_OPCODE: + *Byte = byte; + return 1; + default: + if ( (byte & 0xf8) == 0xd8 ) + { + *Byte = byte; + *fpu_eip = ip; + return 1; + } + else + { + /* Not a valid sequence of prefix bytes followed by + an FPU instruction. */ + *Byte = byte; /* Needed for error message. */ + return 0; + } + } + } +} + + +void math_abort(struct info * info, unsigned int signal) +{ + FPU_EIP = FPU_ORIG_EIP; + current->tss.trap_no = 16; + current->tss.error_code = 0; + send_sig(signal,current,1); + RE_ENTRANT_CHECK_OFF; + __asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4)); +#ifdef PARANOID + printk("ERROR: wm-FPU-emu math_abort failed!\n"); +#endif PARANOID +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/fpu_etc.c linux/arch/i386/math-emu/fpu_etc.c --- v1.1.76/linux/arch/i386/math-emu/fpu_etc.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/fpu_etc.c Fri Aug 19 08:54:01 1994 @@ -0,0 +1,129 @@ +/*---------------------------------------------------------------------------+ + | fpu_etc.c | + | | + | Implement a few FPU instructions. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "reg_constant.h" + + +static void fchs(FPU_REG *st0_ptr) +{ + if ( st0_ptr->tag ^ TW_Empty ) + { + st0_ptr->sign ^= SIGN_POS^SIGN_NEG; + clear_C1(); + } + else + stack_underflow(); +} + +static void fabs(FPU_REG *st0_ptr) +{ + if ( st0_ptr->tag ^ TW_Empty ) + { + st0_ptr->sign = SIGN_POS; + clear_C1(); + } + else + stack_underflow(); +} + + +static void ftst_(FPU_REG *st0_ptr) +{ + switch (st0_ptr->tag) + { + case TW_Zero: + setcc(SW_C3); + break; + case TW_Valid: + if (st0_ptr->sign == SIGN_POS) + setcc(0); + else + setcc(SW_C0); + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + { +#ifdef PECULIAR_486 + /* This is weird! */ + if (st0_ptr->sign == SIGN_POS) + setcc(SW_C3); +#endif PECULIAR_486 + return; + } +#endif DENORM_OPERAND + + break; + case TW_NaN: + setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ + EXCEPTION(EX_Invalid); + break; + case TW_Infinity: + if (st0_ptr->sign == SIGN_POS) + setcc(0); + else + setcc(SW_C0); + break; + case TW_Empty: + setcc(SW_C0|SW_C2|SW_C3); + EXCEPTION(EX_StackUnder); + break; + default: + setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ + EXCEPTION(EX_INTERNAL|0x14); + break; + } +} + +static void fxam(FPU_REG *st0_ptr) +{ + int c=0; + switch (st0_ptr->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 ( st0_ptr->exp <= EXP_UNDER ) + c = SW_C2|SW_C3; /* Denormal */ + else + c = SW_C2; + break; + case TW_NaN: + c = SW_C0; + break; + case TW_Infinity: + c = SW_C2|SW_C0; + break; + } + if (st0_ptr->sign == SIGN_NEG) + c |= SW_C1; + setcc(c); +} + + +static FUNC_ST0 const fp_etc_table[] = { + fchs, fabs, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal, + ftst_, fxam, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal +}; + +void fp_etc() +{ + (fp_etc_table[FPU_rm])(&st(0)); +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/fpu_proto.h linux/arch/i386/math-emu/fpu_proto.h --- v1.1.76/linux/arch/i386/math-emu/fpu_proto.h Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/fpu_proto.h Mon Aug 1 08:19:13 1994 @@ -0,0 +1,137 @@ +/* errors.c */ +extern void Un_impl(void); +extern void FPU_illegal(void); +extern void emu_printall(void); +extern void stack_overflow(void); +extern void stack_underflow(void); +extern void stack_underflow_i(int i); +extern void stack_underflow_pop(int i); +extern int set_precision_flag(int flags); +asmlinkage void exception(int n); +asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest); +asmlinkage int arith_invalid(FPU_REG *dest); +asmlinkage int divide_by_zero(int sign, FPU_REG *dest); +asmlinkage void set_precision_flag_up(void); +asmlinkage void set_precision_flag_down(void); +asmlinkage int denormal_operand(void); +asmlinkage int arith_overflow(FPU_REG *dest); +asmlinkage int arith_underflow(FPU_REG *dest); + +/* 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 */ +asmlinkage void math_emulate(long arg); +extern void math_abort(struct info *info, unsigned int signal); + +/* fpu_etc.c */ +extern void fp_etc(void); + +/* fpu_trig.c */ +extern void convert_l2reg(long const *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, unsigned long *fpu_eip, + struct address *addr, + fpu_addr_modes); +extern void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, + fpu_addr_modes); + +/* load_store.c */ +extern int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, + void *address); + +/* poly_2xm1.c */ +extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result); + +/* poly_atan.c */ +extern void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result); + +/* poly_l2.c */ +extern void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result); +extern int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result); + +/* poly_sin.c */ +extern void poly_sine(FPU_REG const *arg, FPU_REG *result); +extern void poly_cos(FPU_REG const *arg, FPU_REG *result); + +/* poly_tan.c */ +extern void poly_tan(FPU_REG const *arg, FPU_REG *result); + +/* reg_add_sub.c */ +extern int reg_add(FPU_REG const *a, FPU_REG const *b, + FPU_REG *dest, int control_w); +extern int reg_sub(FPU_REG const *a, FPU_REG const *b, + FPU_REG *dest, int control_w); + +/* reg_compare.c */ +extern int compare(FPU_REG const *b); +extern int compare_st_data(FPU_REG const *b); +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 int reg_load_extended(long double *addr, FPU_REG *loaded_data); +extern int reg_load_double(double *dfloat, FPU_REG *loaded_data); +extern int reg_load_single(float *single, FPU_REG *loaded_data); +extern void reg_load_int64(long long *_s, FPU_REG *loaded_data); +extern void reg_load_int32(long *_s, FPU_REG *loaded_data); +extern void reg_load_int16(short *_s, FPU_REG *loaded_data); +extern void reg_load_bcd(char *s, FPU_REG *loaded_data); +extern int reg_store_extended(long double *d, FPU_REG *st0_ptr); +extern int reg_store_double(double *dfloat, FPU_REG *st0_ptr); +extern int reg_store_single(float *single, FPU_REG *st0_ptr); +extern int reg_store_int64(long long *d, FPU_REG *st0_ptr); +extern int reg_store_int32(long *d, FPU_REG *st0_ptr); +extern int reg_store_int16(short *d, FPU_REG *st0_ptr); +extern int reg_store_bcd(char *d, FPU_REG *st0_ptr); +extern int round_to_int(FPU_REG *r); +extern char *fldenv(fpu_addr_modes addr_modes, char *address); +extern void frstor(fpu_addr_modes addr_modes, char *address); +extern unsigned short tag_word(void); +extern char *fstenv(fpu_addr_modes addr_modes, char *address); +extern void fsave(fpu_addr_modes addr_modes, char *address); + +/* reg_mul.c */ +extern int reg_mul(FPU_REG const *a, FPU_REG const *b, + FPU_REG *dest, unsigned int control_w); diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/fpu_system.h linux/arch/i386/math-emu/fpu_system.h --- v1.1.76/linux/arch/i386/math-emu/fpu_system.h Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/fpu_system.h Thu Jun 2 10:28:24 1994 @@ -0,0 +1,82 @@ +/*---------------------------------------------------------------------------+ + | fpu_system.h | + | | + | Copyright (C) 1992,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _FPU_SYSTEM_H +#define _FPU_SYSTEM_H + +/* system dependent definitions */ + +#include +#include + +/* This sets the pointer FPU_info to point to the argument part + of the stack frame of math_emulate() */ +#define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg + +#define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3]) +#define SEG_D_SIZE(x) ((x).b & (3 << 21)) +#define SEG_G_BIT(x) ((x).b & (1 << 23)) +#define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1) +#define SEG_286_MODE(x) ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23))) +#define SEG_BASE_ADDR(s) (((s).b & 0xff000000) \ + | (((s).b & 0xff) << 16) | ((s).a >> 16)) +#define SEG_LIMIT(s) (((s).b & 0xff0000) | ((s).a & 0xffff)) +#define SEG_EXECUTE_ONLY(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 11)) +#define SEG_WRITE_PERM(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 9)) +#define SEG_EXPAND_DOWN(s) (((s).b & ((1 << 11) | (1 << 10))) \ + == (1 << 10)) + +#define I387 (current->tss.i387) +#define FPU_info (I387.soft.info) + +#define FPU_CS (*(unsigned short *) &(FPU_info->___cs)) +#define FPU_SS (*(unsigned short *) &(FPU_info->___ss)) +#define FPU_DS (*(unsigned short *) &(FPU_info->___ds)) +#define FPU_EAX (FPU_info->___eax) +#define FPU_EFLAGS (FPU_info->___eflags) +#define FPU_EIP (FPU_info->___eip) +#define FPU_ORIG_EIP (FPU_info->___orig_eip) + +#define FPU_lookahead (I387.soft.lookahead) + +/* nz if ip_offset and cs_selector are not to be set for the current + instruction. */ +#define no_ip_update (((char *)&(I387.soft.twd))[0]) +#define FPU_rm (((unsigned char *)&(I387.soft.twd))[1]) + +/* Number of bytes of data which can be legally accessed by the current + instruction. This only needs to hold a number <= 108, so a byte will do. */ +#define access_limit (((unsigned char *)&(I387.soft.twd))[2]) + +#define partial_status (I387.soft.swd) +#define control_word (I387.soft.cwd) +#define regs (I387.soft.regs) +#define top (I387.soft.top) + +#define instruction_address (*(struct address *)&I387.soft.fip) +#define operand_address (*(struct address *)&I387.soft.foo) + +#define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \ + math_abort(FPU_info,SIGSEGV) + +#undef FPU_IGNORE_CODE_SEGV +#ifdef FPU_IGNORE_CODE_SEGV +/* verify_area() is very expensive, and causes the emulator to run + about 20% slower if applied to the code. Anyway, errors due to bad + code addresses should be much rarer than errors due to bad data + addresses. */ +#define FPU_code_verify_area(z) +#else +/* A simpler test than verify_area() can probably be done for + FPU_code_verify_area() because the only possible error is to step + past the upper boundary of a legal code area. */ +#define FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z) +#endif + +#endif diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/fpu_trig.c linux/arch/i386/math-emu/fpu_trig.c --- v1.1.76/linux/arch/i386/math-emu/fpu_trig.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/fpu_trig.c Mon Aug 1 08:19:13 1994 @@ -0,0 +1,1718 @@ +/*---------------------------------------------------------------------------+ + | fpu_trig.c | + | | + | Implementation of the FPU "transcendental" functions. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "control_w.h" +#include "reg_constant.h" + + +static void rem_kernel(unsigned long long st0, unsigned long long *y, + unsigned long long st1, + unsigned long long q, int n); + +#define BETTER_THAN_486 + +#define FCOS 4 +/* Not needed now with new code +#define FPTAN 1 + */ + +/* Used only by fptan, fsin, fcos, and fsincos. */ +/* This routine produces very accurate results, similar to + using a value of pi with more than 128 bits precision. */ +/* Limited measurements show no results worse than 64 bit precision + except for the results for arguments close to 2^63, where the + precision of the result sometimes degrades to about 63.9 bits */ +static int trig_arg(FPU_REG *X, int even) +{ + FPU_REG tmp; + unsigned long long q; + int old_cw = control_word, saved_status = partial_status; + + if ( X->exp >= EXP_BIAS + 63 ) + { + partial_status |= SW_C2; /* Reduction incomplete. */ + return -1; + } + + control_word &= ~CW_RC; + control_word |= RC_CHOP; + + reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f); + round_to_int(&tmp); /* Fortunately, this can't overflow + to 2^64 */ + q = significand(&tmp); + if ( q ) + { + rem_kernel(significand(X), + &significand(&tmp), + significand(&CONST_PI2), + q, X->exp - CONST_PI2.exp); + tmp.exp = CONST_PI2.exp; + normalize(&tmp); + reg_move(&tmp, X); + } + +#ifdef FPTAN + if ( even == FPTAN ) + { + if ( ((X->exp >= EXP_BIAS) || + ((X->exp == EXP_BIAS-1) + && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) ) + even = FCOS; + else + even = 0; + } +#endif FPTAN + + if ( (even && !(q & 1)) || (!even && (q & 1)) ) + { + reg_sub(&CONST_PI2, X, X, FULL_PRECISION); +#ifdef BETTER_THAN_486 + /* So far, the results are exact but based upon a 64 bit + precision approximation to pi/2. The technique used + now is equivalent to using an approximation to pi/2 which + is accurate to about 128 bits. */ + if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) ) + { + /* This code gives the effect of having p/2 to better than + 128 bits precision. */ + significand(&tmp) = q + 1; + tmp.exp = EXP_BIAS + 63; + tmp.tag = TW_Valid; + normalize(&tmp); + reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); + reg_add(X, &tmp, X, FULL_PRECISION); + if ( X->sign == SIGN_NEG ) + { + /* CONST_PI2extra is negative, so the result of the addition + can be negative. This means that the argument is actually + in a different quadrant. The correction is always < pi/2, + so it can't overflow into yet another quadrant. */ + X->sign = SIGN_POS; + q++; + } + } +#endif BETTER_THAN_486 + } +#ifdef BETTER_THAN_486 + else + { + /* So far, the results are exact but based upon a 64 bit + precision approximation to pi/2. The technique used + now is equivalent to using an approximation to pi/2 which + is accurate to about 128 bits. */ + if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) ) + { + /* This code gives the effect of having p/2 to better than + 128 bits precision. */ + significand(&tmp) = q; + tmp.exp = EXP_BIAS + 63; + tmp.tag = TW_Valid; + normalize(&tmp); + reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); + reg_sub(X, &tmp, X, FULL_PRECISION); + if ( (X->exp == CONST_PI2.exp) && + ((X->sigh > CONST_PI2.sigh) + || ((X->sigh == CONST_PI2.sigh) + && (X->sigl > CONST_PI2.sigl))) ) + { + /* CONST_PI2extra is negative, so the result of the + subtraction can be larger than pi/2. This means + that the argument is actually in a different quadrant. + The correction is always < pi/2, so it can't overflow + into yet another quadrant. */ + reg_sub(&CONST_PI, X, X, FULL_PRECISION); + q++; + } + } + } +#endif BETTER_THAN_486 + + control_word = old_cw; + partial_status = saved_status & ~SW_C2; /* Reduction complete. */ + + return (q & 3) | even; +} + + +/* Convert a long to register */ +void convert_l2reg(long const *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(FPU_REG *st0_ptr) +{ + switch ( st0_ptr->tag ) + { + case TW_NaN: + if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ + { + EXCEPTION(EX_Invalid); + if ( control_word & CW_Invalid ) + st0_ptr->sigh |= 0x40000000; /* Convert to a QNaN */ + } + break; /* return with a NaN in st(0) */ + case TW_Empty: + stack_underflow(); /* Puts a QNaN in st(0) */ + break; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL|0x0112); +#endif PARANOID + } +} + + +static void single_arg_2_error(FPU_REG *st0_ptr) +{ + FPU_REG *st_new_ptr; + + switch ( st0_ptr->tag ) + { + case TW_NaN: + if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ + { + EXCEPTION(EX_Invalid); + if ( control_word & CW_Invalid ) + { + /* The masked response */ + /* Convert to a QNaN */ + st0_ptr->sigh |= 0x40000000; + st_new_ptr = &st(-1); + push(); + reg_move(&st(1), st_new_ptr); + } + } + else + { + /* A QNaN */ + st_new_ptr = &st(-1); + push(); + reg_move(&st(1), st_new_ptr); + } + break; /* return with a NaN in st(0) */ +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL|0x0112); +#endif PARANOID + } +} + + +/*---------------------------------------------------------------------------*/ + +static void f2xm1(FPU_REG *st0_ptr) +{ + clear_C1(); + switch ( st0_ptr->tag ) + { + case TW_Valid: + { + if ( st0_ptr->exp >= 0 ) + { + /* For an 80486 FPU, the result is undefined. */ + } +#ifdef DENORM_OPERAND + else if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + else + { + /* poly_2xm1(x) requires 0 < x < 1. */ + poly_2xm1(st0_ptr, st0_ptr); + } + if ( st0_ptr->exp <= EXP_UNDER ) + { + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + arith_underflow(st0_ptr); + } + set_precision_flag_up(); /* 80486 appears to always do this */ + return; + } + case TW_Zero: + return; + case TW_Infinity: + if ( st0_ptr->sign == SIGN_NEG ) + { + /* -infinity gives -1 (p16-10) */ + reg_move(&CONST_1, st0_ptr); + st0_ptr->sign = SIGN_NEG; + } + return; + default: + single_arg_error(st0_ptr); + } +} + + +static void fptan(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st_new_ptr; + int q; + char arg_sign = st0_ptr->sign; + + /* Stack underflow has higher priority */ + if ( st0_tag == TW_Empty ) + { + stack_underflow(); /* Puts a QNaN in st(0) */ + if ( control_word & CW_Invalid ) + { + st_new_ptr = &st(-1); + push(); + stack_underflow(); /* Puts a QNaN in the new st(0) */ + } + return; + } + + if ( STACK_OVERFLOW ) + { stack_overflow(); return; } + + switch ( st0_tag ) + { + case TW_Valid: + if ( st0_ptr->exp > EXP_BIAS - 40 ) + { + st0_ptr->sign = SIGN_POS; + if ( (q = trig_arg(st0_ptr, 0)) != -1 ) + { + poly_tan(st0_ptr, st0_ptr); + st0_ptr->sign = (q & 1) ^ arg_sign; + } + else + { + /* Operand is out of range */ + st0_ptr->sign = arg_sign; /* restore st(0) */ + return; + } + set_precision_flag_up(); /* We do not really know if up or down */ + } + else + { + /* For a small arg, the result == the argument */ + /* Underflow may happen */ + + if ( st0_ptr->exp <= EXP_UNDER ) + { +#ifdef DENORM_OPERAND + if ( denormal_operand() ) + return; +#endif DENORM_OPERAND + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + if ( arith_underflow(st0_ptr) ) + return; + } + set_precision_flag_down(); /* Must be down. */ + } + push(); + reg_move(&CONST_1, st_new_ptr); + return; + break; + case TW_Infinity: + /* The 80486 treats infinity as an invalid operand */ + arith_invalid(st0_ptr); + if ( control_word & CW_Invalid ) + { + st_new_ptr = &st(-1); + push(); + arith_invalid(st_new_ptr); + } + return; + case TW_Zero: + push(); + reg_move(&CONST_1, st_new_ptr); + setcc(0); + break; + default: + single_arg_2_error(st0_ptr); + break; + } +} + + +static void fxtract(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st_new_ptr; + register FPU_REG *st1_ptr = st0_ptr; /* anticipate */ + + if ( STACK_OVERFLOW ) + { stack_overflow(); return; } + clear_C1(); + if ( !(st0_tag ^ TW_Valid) ) + { + long e; + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + push(); + reg_move(st1_ptr, st_new_ptr); + st_new_ptr->exp = EXP_BIAS; + e = st1_ptr->exp - EXP_BIAS; + convert_l2reg(&e, st1_ptr); + return; + } + else if ( st0_tag == TW_Zero ) + { + char sign = st0_ptr->sign; + if ( divide_by_zero(SIGN_NEG, st0_ptr) ) + return; + push(); + reg_move(&CONST_Z, st_new_ptr); + st_new_ptr->sign = sign; + return; + } + else if ( st0_tag == TW_Infinity ) + { + char sign = st0_ptr->sign; + st0_ptr->sign = SIGN_POS; + push(); + reg_move(&CONST_INF, st_new_ptr); + st_new_ptr->sign = sign; + return; + } + else if ( st0_tag == TW_NaN ) + { + if ( real_2op_NaN(st0_ptr, st0_ptr, st0_ptr) ) + return; + push(); + reg_move(st1_ptr, st_new_ptr); + return; + } + else if ( 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(FPU_REG *st0_ptr) +{ + clear_C1(); + top--; /* st0_ptr will be fixed in math_emulate() before the next instr */ +} + +static void fincstp(FPU_REG *st0_ptr) +{ + clear_C1(); + top++; /* st0_ptr will be fixed in math_emulate() before the next instr */ +} + + +static void fsqrt_(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + + clear_C1(); + if ( !(st0_tag ^ TW_Valid) ) + { + int expon; + + if (st0_ptr->sign == SIGN_NEG) + { + arith_invalid(st0_ptr); /* sqrt(negative) is invalid */ + return; + } + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + expon = st0_ptr->exp - EXP_BIAS; + st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 .. 4.0) */ + + wm_sqrt(st0_ptr, control_word); /* Do the computation */ + + st0_ptr->exp += expon >> 1; + st0_ptr->sign = SIGN_POS; + } + else if ( st0_tag == TW_Zero ) + return; + else if ( st0_tag == TW_Infinity ) + { + if ( st0_ptr->sign == SIGN_NEG ) + arith_invalid(st0_ptr); /* sqrt(-Infinity) is invalid */ + return; + } + else + { single_arg_error(st0_ptr); return; } + +} + + +static void frndint_(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + int flags; + + if ( !(st0_tag ^ TW_Valid) ) + { + if (st0_ptr->exp > EXP_BIAS+63) + return; + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + /* Fortunately, this can't overflow to 2^64 */ + if ( (flags = round_to_int(st0_ptr)) ) + set_precision_flag(flags); + + st0_ptr->exp = EXP_BIAS + 63; + normalize(st0_ptr); + return; + } + else if ( (st0_tag == TW_Zero) || (st0_tag == TW_Infinity) ) + return; + else + single_arg_error(st0_ptr); +} + + +static void fsin(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + char arg_sign = st0_ptr->sign; + + if ( st0_tag == TW_Valid ) + { + FPU_REG rv; + int q; + + if ( st0_ptr->exp > EXP_BIAS - 40 ) + { + st0_ptr->sign = SIGN_POS; + if ( (q = trig_arg(st0_ptr, 0)) != -1 ) + { + + poly_sine(st0_ptr, &rv); + + if (q & 2) + rv.sign ^= SIGN_POS ^ SIGN_NEG; + rv.sign ^= arg_sign; + reg_move(&rv, st0_ptr); + + /* We do not really know if up or down */ + set_precision_flag_up(); + return; + } + else + { + /* Operand is out of range */ + st0_ptr->sign = arg_sign; /* restore st(0) */ + return; + } + } + else + { + /* For a small arg, the result == the argument */ + /* Underflow may happen */ + + if ( st0_ptr->exp <= EXP_UNDER ) + { +#ifdef DENORM_OPERAND + if ( denormal_operand() ) + return; +#endif DENORM_OPERAND + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + arith_underflow(st0_ptr); + return; + } + + set_precision_flag_up(); /* Must be up. */ + } + } + else if ( st0_tag == TW_Zero ) + { + setcc(0); + return; + } + else if ( st0_tag == TW_Infinity ) + { + /* The 80486 treats infinity as an invalid operand */ + arith_invalid(st0_ptr); + return; + } + else + single_arg_error(st0_ptr); +} + + +static int f_cos(FPU_REG *arg) +{ + char arg_sign = arg->sign; + + if ( arg->tag == TW_Valid ) + { + FPU_REG rv; + int q; + + if ( arg->exp > EXP_BIAS - 40 ) + { + arg->sign = SIGN_POS; + if ( (arg->exp < EXP_BIAS) + || ((arg->exp == EXP_BIAS) + && (significand(arg) <= 0xc90fdaa22168c234LL)) ) + { + poly_cos(arg, &rv); + reg_move(&rv, arg); + + /* We do not really know if up or down */ + set_precision_flag_down(); + + return 0; + } + else if ( (q = trig_arg(arg, FCOS)) != -1 ) + { + poly_sine(arg, &rv); + + if ((q+1) & 2) + rv.sign ^= SIGN_POS ^ SIGN_NEG; + reg_move(&rv, arg); + + /* We do not really know if up or down */ + set_precision_flag_down(); + + return 0; + } + else + { + /* Operand is out of range */ + arg->sign = arg_sign; /* restore st(0) */ + return 1; + } + } + else + { +#ifdef DENORM_OPERAND + if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) ) + return 1; +#endif DENORM_OPERAND + + setcc(0); + reg_move(&CONST_1, arg); +#ifdef PECULIAR_486 + set_precision_flag_down(); /* 80486 appears to do this. */ +#else + set_precision_flag_up(); /* Must be up. */ +#endif PECULIAR_486 + return 0; + } + } + else if ( arg->tag == TW_Zero ) + { + reg_move(&CONST_1, arg); + setcc(0); + return 0; + } + else if ( arg->tag == TW_Infinity ) + { + /* The 80486 treats infinity as an invalid operand */ + arith_invalid(arg); + return 1; + } + else + { + single_arg_error(arg); /* requires arg == &st(0) */ + return 1; + } +} + + +static void fcos(FPU_REG *st0_ptr) +{ + f_cos(st0_ptr); +} + + +static void fsincos(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st_new_ptr; + FPU_REG arg; + + /* Stack underflow has higher priority */ + if ( st0_tag == TW_Empty ) + { + stack_underflow(); /* Puts a QNaN in st(0) */ + if ( control_word & CW_Invalid ) + { + st_new_ptr = &st(-1); + push(); + stack_underflow(); /* Puts a QNaN in the new st(0) */ + } + return; + } + + if ( STACK_OVERFLOW ) + { stack_overflow(); return; } + + if ( st0_tag == TW_NaN ) + { + single_arg_2_error(st0_ptr); + return; + } + else if ( st0_tag == TW_Infinity ) + { + /* The 80486 treats infinity as an invalid operand */ + if ( !arith_invalid(st0_ptr) ) + { + /* unmasked response */ + push(); + arith_invalid(st_new_ptr); + } + return; + } + + reg_move(st0_ptr,&arg); + if ( !f_cos(&arg) ) + { + fsin(st0_ptr); + push(); + reg_move(&arg,st_new_ptr); + } + +} + + +/*---------------------------------------------------------------------------*/ +/* The following all require two arguments: st(0) and st(1) */ + +/* A lean, mean kernel for the fprem instructions. This relies upon + the division and rounding to an integer in do_fprem giving an + exact result. Because of this, rem_kernel() needs to deal only with + the least significant 64 bits, the more significant bits of the + result must be zero. + */ +static void rem_kernel(unsigned long long st0, unsigned long long *y, + unsigned long long st1, + unsigned long long q, int n) +{ + unsigned long long x; + + x = st0 << n; + + /* Do the required multiplication and subtraction in the one operation */ + asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1; + movl %3,%%eax; mull %4; subl %%eax,%1; + movl %2,%%eax; mull %5; subl %%eax,%1;" + :"=m" (x), "=m" (((unsigned *)&x)[1]) + :"m" (st1),"m" (((unsigned *)&st1)[1]), + "m" (q),"m" (((unsigned *)&q)[1]) + :"%ax","%dx"); + + *y = x; +} + + +/* Remainder of st(0) / st(1) */ +/* This routine produces exact results, i.e. there is never any + rounding or truncation, etc of the result. */ +static void do_fprem(FPU_REG *st0_ptr, int round) +{ + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + char st0_tag = st0_ptr->tag; + char sign = st0_ptr->sign; + + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + { + FPU_REG tmp; + int old_cw = control_word; + int expdif = st0_ptr->exp - st1_ptr->exp; + long long q; + unsigned short saved_status; + int cc = 0; + +#ifdef DENORM_OPERAND + if ( ((st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + /* We want the status following the denorm tests, but don't want + the status changed by the arithmetic operations. */ + saved_status = partial_status; + control_word &= ~CW_RC; + control_word |= RC_CHOP; + + if (expdif < 64) + { + /* This should be the most common case */ + + if ( expdif > -2 ) + { + reg_div(st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); + + if ( tmp.exp >= EXP_BIAS ) + { + round_to_int(&tmp); /* Fortunately, this can't overflow + to 2^64 */ + q = significand(&tmp); + + rem_kernel(significand(st0_ptr), + &significand(&tmp), + significand(st1_ptr), + q, expdif); + + tmp.exp = st1_ptr->exp; + } + else + { + reg_move(st0_ptr, &tmp); + q = 0; + } + tmp.sign = sign; + + if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) ) + { + /* We may need to subtract st(1) once more, + to get a result <= 1/2 of st(1). */ + unsigned long long x; + expdif = st1_ptr->exp - tmp.exp; + if ( expdif <= 1 ) + { + if ( expdif == 0 ) + x = significand(st1_ptr) - significand(&tmp); + else /* expdif is 1 */ + x = (significand(st1_ptr) << 1) - significand(&tmp); + if ( (x < significand(&tmp)) || + /* or equi-distant (from 0 & st(1)) and q is odd */ + ((x == significand(&tmp)) && (q & 1) ) ) + { + tmp.sign ^= (SIGN_POS^SIGN_NEG); + significand(&tmp) = x; + q++; + } + } + } + + if (q & 4) cc |= SW_C0; + if (q & 2) cc |= SW_C3; + if (q & 1) cc |= SW_C1; + } + else + { + control_word = old_cw; + setcc(0); + return; + } + } + else + { + /* There is a large exponent difference ( >= 64 ) */ + /* To make much sense, the code in this section should + be done at high precision. */ + int exp_1; + + /* prevent overflow here */ + /* N is 'a number between 32 and 63' (p26-113) */ + reg_move(st0_ptr, &tmp); + tmp.exp = EXP_BIAS + 56; + exp_1 = st1_ptr->exp; st1_ptr->exp = EXP_BIAS; + expdif -= 56; + + reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); + st1_ptr->exp = exp_1; + + round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */ + + rem_kernel(significand(st0_ptr), + &significand(&tmp), + significand(st1_ptr), + significand(&tmp), + tmp.exp - EXP_BIAS + ); + tmp.exp = exp_1 + expdif; + tmp.sign = sign; + + /* It is possible for the operation to be complete here. + What does the IEEE standard say? The Intel 80486 manual + implies that the operation will never be completed at this + point, and the behaviour of a real 80486 confirms this. + */ + if ( !(tmp.sigh | tmp.sigl) ) + { + /* The result is zero */ + control_word = old_cw; + partial_status = saved_status; + reg_move(&CONST_Z, st0_ptr); + st0_ptr->sign = sign; +#ifdef PECULIAR_486 + setcc(SW_C2); +#else + setcc(0); +#endif PECULIAR_486 + return; + } + cc = SW_C2; + } + + control_word = old_cw; + partial_status = saved_status; + normalize_nuo(&tmp); + reg_move(&tmp, st0_ptr); + setcc(cc); + + /* The only condition to be looked for is underflow, + and it can occur here only if underflow is unmasked. */ + if ( (st0_ptr->exp <= EXP_UNDER) && (st0_ptr->tag != TW_Zero) + && !(control_word & CW_Underflow) ) + arith_underflow(st0_ptr); + + return; + } + else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) + { + stack_underflow(); + return; + } + else if ( 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(st0_ptr); return; } /* fprem(?,0) always invalid */ + else if ( st1_tag == TW_Infinity ) + { setcc(0); return; } + } + else if ( st0_tag == TW_Valid ) + { + if ( st1_tag == TW_Zero ) + { + arith_invalid(st0_ptr); /* fprem(Valid,Zero) is invalid */ + return; + } + else if ( st1_tag != TW_NaN ) + { +#ifdef DENORM_OPERAND + if ( (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 ( st0_tag == TW_Infinity ) + { + if ( st1_tag != TW_NaN ) + { + arith_invalid(st0_ptr); /* fprem(Infinity,?) is invalid */ + return; + } + } + + /* One of the registers must contain a NaN is we got here. */ + +#ifdef PARANOID + if ( (st0_tag != TW_NaN) && (st1_tag != TW_NaN) ) + EXCEPTION(EX_INTERNAL | 0x118); +#endif PARANOID + + real_2op_NaN(st1_ptr, st0_ptr, st0_ptr); + +} + + +/* ST(1) <- ST(1) * log ST; pop ST */ +static void fyl2x(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st1_ptr = &st(1), exponent; + char st1_tag = st1_ptr->tag; + int e; + + clear_C1(); + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + { + if ( st0_ptr->sign == SIGN_POS ) + { +#ifdef DENORM_OPERAND + if ( ((st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + if ( (st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0) ) + { + /* Special case. The result can be precise. */ + e = st0_ptr->exp - EXP_BIAS; + if ( e > 0 ) + { + exponent.sigh = e; + exponent.sign = SIGN_POS; + } + else + { + exponent.sigh = -e; + exponent.sign = SIGN_NEG; + } + exponent.sigl = 0; + exponent.exp = EXP_BIAS + 31; + exponent.tag = TW_Valid; + normalize_nuo(&exponent); + reg_mul(&exponent, st1_ptr, st1_ptr, FULL_PRECISION); + } + else + { + /* The usual case */ + poly_l2(st0_ptr, st1_ptr, st1_ptr); + if ( st1_ptr->exp <= EXP_UNDER ) + { + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + arith_underflow(st1_ptr); + } + else + set_precision_flag_up(); /* 80486 appears to always do this */ + } + pop(); + return; + } + else + { + /* negative */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } + } + else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) + { + stack_underflow_pop(1); + return; + } + else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } + else if ( (st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) ) + { + /* one of the args is zero, the other valid, or both zero */ + if ( st0_tag == TW_Zero ) + { + if ( st1_tag == TW_Zero ) + { + /* Both args zero is invalid */ + if ( !arith_invalid(st1_ptr) ) + pop(); + } +#ifdef PECULIAR_486 + /* This case is not specifically covered in the manual, + but divide-by-zero would seem to be the best response. + However, a real 80486 does it this way... */ + else if ( st0_ptr->tag == TW_Infinity ) + { + reg_move(&CONST_INF, st1_ptr); + pop(); + } +#endif PECULIAR_486 + else + { + if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) ) + pop(); + } + return; + } + else + { + /* st(1) contains zero, st(0) valid <> 0 */ + /* Zero is the valid answer */ + char sign = st1_ptr->sign; + + if ( st0_ptr->sign == SIGN_NEG ) + { + /* log(negative) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS; + pop(); st0_ptr = &st(0); + reg_move(&CONST_Z, st0_ptr); + st0_ptr->sign = sign; + return; + } + } + /* One or both arg must be an infinity */ + else if ( st0_tag == TW_Infinity ) + { + if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) ) + { + /* log(-infinity) or 0*log(infinity) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } + else + { + char sign = st1_ptr->sign; + +#ifdef DENORM_OPERAND + if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + pop(); st0_ptr = &st(0); + reg_move(&CONST_INF, st0_ptr); + st0_ptr->sign = sign; + return; + } + } + /* st(1) must be infinity here */ + else if ( (st0_tag == TW_Valid) && (st0_ptr->sign == SIGN_POS) ) + { + if ( st0_ptr->exp >= EXP_BIAS ) + { + if ( (st0_ptr->exp == EXP_BIAS) && + (st0_ptr->sigh == 0x80000000) && + (st0_ptr->sigl == 0) ) + { + /* st(0) holds 1.0 */ + /* infinity*log(1) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } + /* st(0) is positive and > 1.0 */ + pop(); + } + else + { + /* st(0) is positive and < 1.0 */ + +#ifdef DENORM_OPERAND + if ( (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 ( st0_ptr->tag == TW_Zero ) + { + /* This should be invalid, but a real 80486 is happy with it. */ +#ifndef PECULIAR_486 + if ( !divide_by_zero(st1_ptr->sign, st1_ptr) ) +#endif PECULIAR_486 + { + st1_ptr->sign ^= SIGN_NEG^SIGN_POS; + pop(); + } + } + else + { + /* log(negative) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + } + return; + } +} + + +static void fpatan(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + + clear_C1(); + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + { +#ifdef DENORM_OPERAND + if ( ((st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + poly_atan(st0_ptr, st1_ptr, st1_ptr); + + if ( st1_ptr->exp <= EXP_UNDER ) + { + /* A denormal result has been produced. + Precision must have been lost. + This is by definition an underflow. */ + arith_underflow(st1_ptr); + pop(); + return; + } + } + else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) + { + stack_underflow_pop(1); + return; + } + else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } + else if ( (st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) ) + { + char sign = st1_ptr->sign; + if ( st0_tag == TW_Infinity ) + { + if ( st1_tag == TW_Infinity ) + { + if ( 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_tag != TW_Zero ) + { + if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; + } +#endif DENORM_OPERAND + + if ( st0_ptr->sign == SIGN_POS ) + { + reg_move(&CONST_Z, st1_ptr); + st1_ptr->sign = sign; /* An 80486 preserves the sign */ + pop(); + return; + } + else + reg_move(&CONST_PI, st1_ptr); + } + } + else + { + /* st(1) is infinity, st(0) not infinity */ +#ifdef DENORM_OPERAND + if ( st0_tag != TW_Zero ) + { + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; + } +#endif DENORM_OPERAND + + 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 ( st0_tag != TW_Zero ) + { + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; + } +#endif DENORM_OPERAND + + if ( st0_ptr->sign == SIGN_POS ) + { /* An 80486 preserves the sign */ pop(); return; } + else + reg_move(&CONST_PI, st1_ptr); + st1_ptr->sign = sign; + } + else if ( 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 | 0x125); +#endif PARANOID + + pop(); + set_precision_flag_up(); /* We do not really know if up or down */ +} + + +static void fprem(FPU_REG *st0_ptr) +{ + do_fprem(st0_ptr, RC_CHOP); +} + + +static void fprem1(FPU_REG *st0_ptr) +{ + do_fprem(st0_ptr, RC_RND); +} + + +static void fyl2xp1(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag, sign; + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + + clear_C1(); + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + { +#ifdef DENORM_OPERAND + if ( ((st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() ) + return; +#endif DENORM_OPERAND + + if ( poly_l2p1(st0_ptr, st1_ptr, st1_ptr) ) + { +#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; +#else + if ( arith_invalid(st1_ptr) ) /* poly_l2p1() returned invalid */ + return; +#endif PECULIAR_486 + } + if ( st1_ptr->exp <= EXP_UNDER ) + { + /* A denormal result has been produced. + Precision must have been lost, this is always + an underflow. */ + sign = st1_ptr->sign; + arith_underflow(st1_ptr); + st1_ptr->sign = sign; + } + else + set_precision_flag_up(); /* 80486 appears to always do this */ + pop(); + return; + } + else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) + { + stack_underflow_pop(1); + return; + } + else if ( 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 + + st0_ptr->sign ^= st1_ptr->sign; + reg_move(st0_ptr, st1_ptr); + } + else if ( st1_tag == TW_Infinity ) + { + /* Infinity*log(1) */ + if ( !arith_invalid(st1_ptr) ) + pop(); + return; + } + else if ( st1_tag == TW_NaN ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } +#ifdef PARANOID + else + { + EXCEPTION(EX_INTERNAL | 0x116); + return; + } +#endif PARANOID + pop(); return; + } + else if ( st0_tag == TW_Valid ) + { + if ( st1_tag == TW_Zero ) + { + if ( st0_ptr->sign == SIGN_NEG ) + { + if ( st0_ptr->exp >= EXP_BIAS ) + { + /* st(0) holds <= -1.0 */ +#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; +#else + if ( arith_invalid(st1_ptr) ) return; +#endif PECULIAR_486 + pop(); return; + } +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; + pop(); return; + } +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + pop(); return; + } + if ( st1_tag == TW_Infinity ) + { + if ( st0_ptr->sign == SIGN_NEG ) + { + if ( (st0_ptr->exp >= EXP_BIAS) && + !((st0_ptr->sigh == 0x80000000) && + (st0_ptr->sigl == 0)) ) + { + /* st(0) holds < -1.0 */ +#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; +#else + if ( arith_invalid(st1_ptr) ) return; +#endif PECULIAR_486 + pop(); return; + } +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + st1_ptr->sign ^= SIGN_POS^SIGN_NEG; + pop(); return; + } +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + pop(); return; + } + if ( st1_tag == TW_NaN ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } + } + else if ( st0_tag == TW_NaN ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } + else if ( st0_tag == TW_Infinity ) + { + if ( st1_tag == TW_NaN ) + { + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) + pop(); + return; + } + else if ( st0_ptr->sign == SIGN_NEG ) + { + int exponent = st1_ptr->exp; +#ifndef PECULIAR_486 + /* This should have higher priority than denormals, but... */ + if ( arith_invalid(st1_ptr) ) /* log(-infinity) */ + return; +#endif PECULIAR_486 +#ifdef DENORM_OPERAND + if ( st1_tag != TW_Zero ) + { + if ( (exponent <= EXP_UNDER) && (denormal_operand()) ) + return; + } +#endif DENORM_OPERAND +#ifdef PECULIAR_486 + /* Denormal operands actually get higher priority */ + if ( arith_invalid(st1_ptr) ) /* log(-infinity) */ + return; +#endif PECULIAR_486 + pop(); + return; + } + else if ( st1_tag == TW_Zero ) + { + /* log(infinity) */ + if ( !arith_invalid(st1_ptr) ) + 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(FPU_REG *st0_ptr) +{ + char st0_tag = st0_ptr->tag; + FPU_REG *st1_ptr = &st(1); + char st1_tag = st1_ptr->tag; + int old_cw = control_word; + char sign = st0_ptr->sign; + + clear_C1(); + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + { + long scale; + FPU_REG tmp; + +#ifdef DENORM_OPERAND + if ( ((st0_ptr->exp <= EXP_UNDER) || + (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + 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 = st0_ptr->sign; + reg_move(&CONST_INF, st0_ptr); + st0_ptr->sign = sign; + } + else + { + EXCEPTION(EX_Underflow); + sign = st0_ptr->sign; + reg_move(&CONST_Z, st0_ptr); + 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 += st0_ptr->exp; + st0_ptr->exp = scale; + + /* Use round_reg() to properly detect under/overflow etc */ + round_reg(st0_ptr, 0, control_word); + + return; + } + else if ( st0_tag == TW_Valid ) + { + if ( st1_tag == TW_Zero ) + { + +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + return; + } + if ( st1_tag == TW_Infinity ) + { +#ifdef DENORM_OPERAND + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + return; +#endif DENORM_OPERAND + + if ( st1_ptr->sign == SIGN_POS ) + { reg_move(&CONST_INF, st0_ptr); } + else + reg_move(&CONST_Z, st0_ptr); + st0_ptr->sign = sign; + return; + } + if ( st1_tag == TW_NaN ) + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } + } + else if ( 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(st0_ptr); /* Zero scaled by +Infinity */ + return; + } + } + else if ( st1_tag == TW_NaN ) + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } + } + else if ( st0_tag == TW_Infinity ) + { + if ( st1_tag == TW_Valid ) + { + +#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(st0_ptr); /* Infinity scaled by -Infinity */ + return; + } + else if ( st1_tag == TW_NaN ) + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } + } + else if ( st0_tag == TW_NaN ) + { + if ( st1_tag != TW_Empty ) + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } + } + +#ifdef PARANOID + if ( !((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_ST0 const trig_table_a[] = { + f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp +}; + +void trig_a(void) +{ + (trig_table_a[FPU_rm])(&st(0)); +} + + +static FUNC_ST0 const trig_table_b[] = + { + fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos + }; + +void trig_b(void) +{ + (trig_table_b[FPU_rm])(&st(0)); +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/get_address.c linux/arch/i386/math-emu/get_address.c --- v1.1.76/linux/arch/i386/math-emu/get_address.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/get_address.c Fri Aug 19 08:54:01 1994 @@ -0,0 +1,423 @@ +/*---------------------------------------------------------------------------+ + | get_address.c | + | | + | Get the effective address from an FPU instruction. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | 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 +#include + +#include + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" + + +#define FPU_WRITE_BIT 0x10 + +static int reg_offset[] = { + offsetof(struct info,___eax), + offsetof(struct info,___ecx), + offsetof(struct info,___edx), + offsetof(struct info,___ebx), + offsetof(struct info,___esp), + offsetof(struct info,___ebp), + offsetof(struct info,___esi), + offsetof(struct info,___edi) +}; + +#define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info)) + +static int reg_offset_vm86[] = { + offsetof(struct info,___cs), + offsetof(struct info,___vm86_ds), + offsetof(struct info,___vm86_es), + offsetof(struct info,___vm86_fs), + offsetof(struct info,___vm86_gs), + offsetof(struct info,___ss), + offsetof(struct info,___vm86_ds) + }; + +#define VM86_REG_(x) (*(unsigned short *) \ + (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info)) + +static int reg_offset_pm[] = { + offsetof(struct info,___cs), + offsetof(struct info,___ds), + offsetof(struct info,___es), + offsetof(struct info,___fs), + offsetof(struct info,___gs), + offsetof(struct info,___ss), + offsetof(struct info,___ds) + }; + +#define PM_REG_(x) (*(unsigned short *) \ + (reg_offset_pm[((unsigned)x)]+(char *) FPU_info)) + + +/* Decode the SIB byte. This function assumes mod != 0 */ +static int sib(int mod, unsigned long *fpu_eip) +{ + unsigned char ss,index,base; + long offset; + + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + base = get_fs_byte((char *) (*fpu_eip)); /* The SIB byte */ + RE_ENTRANT_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 */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + offset += (signed char) get_fs_byte((char *) (*fpu_eip)); + RE_ENTRANT_CHECK_ON; + (*fpu_eip)++; + } + else if (mod == 2 || base == 5) /* The second condition also has mod==0 */ + { + /* 32 bit displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(4); + offset += (signed) get_fs_long((unsigned long *) (*fpu_eip)); + RE_ENTRANT_CHECK_ON; + (*fpu_eip) += 4; + } + + return offset; +} + + +static unsigned long vm86_segment(unsigned char segment, + unsigned short *selector) +{ + segment--; +#ifdef PARANOID + if ( segment > PREFIX_SS_ ) + { + EXCEPTION(EX_INTERNAL|0x130); + math_abort(FPU_info,SIGSEGV); + } +#endif PARANOID + *selector = VM86_REG_(segment); + return (unsigned long)VM86_REG_(segment) << 4; +} + + +/* This should work for 16 and 32 bit protected mode. */ +static long pm_address(unsigned char FPU_modrm, unsigned char segment, + unsigned short *selector, long offset) +{ + struct desc_struct descriptor; + unsigned long base_address, limit, address, seg_top; + + segment--; +#ifdef PARANOID + if ( segment > PREFIX_SS_ ) + { + EXCEPTION(EX_INTERNAL|0x132); + math_abort(FPU_info,SIGSEGV); + } +#endif PARANOID + + *selector = PM_REG_(segment); + + descriptor = LDT_DESCRIPTOR(PM_REG_(segment)); + base_address = SEG_BASE_ADDR(descriptor); + address = base_address + offset; + limit = base_address + + (SEG_LIMIT(descriptor)+1) * SEG_GRANULARITY(descriptor) - 1; + if ( limit < base_address ) limit = 0xffffffff; + + if ( SEG_EXPAND_DOWN(descriptor) ) + { + if ( SEG_G_BIT(descriptor) ) + seg_top = 0xffffffff; + else + { + seg_top = base_address + (1 << 20); + if ( seg_top < base_address ) seg_top = 0xffffffff; + } + access_limit = + (address <= limit) || (address >= seg_top) ? 0 : + ((seg_top-address) >= 255 ? 255 : seg_top-address); + } + else + { + access_limit = + (address > limit) || (address < base_address) ? 0 : + ((limit-address) >= 254 ? 255 : limit-address+1); + } + if ( SEG_EXECUTE_ONLY(descriptor) || + (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT)) ) + { + access_limit = 0; + } + return address; +} + + +/* + 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 long *fpu_eip, + struct address *addr, +/* unsigned short *selector, unsigned long *offset, */ + fpu_addr_modes addr_modes) +{ + unsigned char mod; + unsigned rm = FPU_modrm & 7; + long *cpu_reg_ptr; + int address = 0; /* Initialized just to stop compiler warnings. */ + + /* Memory accessed via the cs selector is write protected + in `non-segmented' 32 bit protected mode. */ + if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) + && (addr_modes.override.segment == PREFIX_CS_) ) + { + math_abort(FPU_info,SIGSEGV); + } + + addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ + + mod = (FPU_modrm >> 6) & 3; + + if (rm == 4 && mod != 3) + { + address = sib(mod, fpu_eip); + } + else + { + cpu_reg_ptr = & REG_(rm); + switch (mod) + { + case 0: + if (rm == 5) + { + /* Special case: disp32 */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(4); + address = get_fs_long((unsigned long *) (*fpu_eip)); + (*fpu_eip) += 4; + RE_ENTRANT_CHECK_ON; + addr->offset = address; + return (void *) address; + } + else + { + address = *cpu_reg_ptr; /* Just return the contents + of the cpu register */ + addr->offset = address; + return (void *) address; + } + case 1: + /* 8 bit signed displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + address = (signed char) get_fs_byte((char *) (*fpu_eip)); + RE_ENTRANT_CHECK_ON; + (*fpu_eip)++; + break; + case 2: + /* 32 bit displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(4); + address = (signed) get_fs_long((unsigned long *) (*fpu_eip)); + (*fpu_eip) += 4; + RE_ENTRANT_CHECK_ON; + break; + case 3: + /* Not legal for the FPU */ + EXCEPTION(EX_Invalid); + } + address += *cpu_reg_ptr; + } + + addr->offset = address; + + switch ( addr_modes.default_mode ) + { + case 0: + break; + case VM86: + address += vm86_segment(addr_modes.override.segment, + (unsigned short *)&(addr->selector)); + break; + case PM16: + case SEG32: + address = pm_address(FPU_modrm, addr_modes.override.segment, + (unsigned short *)&(addr->selector), address); + break; + default: + EXCEPTION(EX_INTERNAL|0x133); + } + + return (void *)address; +} + + +void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, +/* unsigned short *selector, unsigned long *offset, */ + fpu_addr_modes addr_modes) +{ + unsigned char mod; + unsigned rm = FPU_modrm & 7; + int address = 0; /* Default used for mod == 0 */ + + /* Memory accessed via the cs selector is write protected + in `non-segmented' 32 bit protected mode. */ + if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) + && (addr_modes.override.segment == PREFIX_CS_) ) + { + math_abort(FPU_info,SIGSEGV); + } + + addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ + + mod = (FPU_modrm >> 6) & 3; + + switch (mod) + { + case 0: + if (rm == 6) + { + /* Special case: disp16 */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(2); + address = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip)); + (*fpu_eip) += 2; + RE_ENTRANT_CHECK_ON; + goto add_segment; + } + break; + case 1: + /* 8 bit signed displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + address = (signed char) get_fs_byte((signed char *) (*fpu_eip)); + RE_ENTRANT_CHECK_ON; + (*fpu_eip)++; + break; + case 2: + /* 16 bit displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(2); + address = (unsigned) get_fs_word((unsigned short *) (*fpu_eip)); + (*fpu_eip) += 2; + RE_ENTRANT_CHECK_ON; + break; + case 3: + /* Not legal for the FPU */ + EXCEPTION(EX_Invalid); + break; + } + switch ( rm ) + { + case 0: + address += FPU_info->___ebx + FPU_info->___esi; + break; + case 1: + address += FPU_info->___ebx + FPU_info->___edi; + break; + case 2: + address += FPU_info->___ebp + FPU_info->___esi; + if ( addr_modes.override.segment == PREFIX_DEFAULT ) + addr_modes.override.segment = PREFIX_SS_; + break; + case 3: + address += FPU_info->___ebp + FPU_info->___edi; + if ( addr_modes.override.segment == PREFIX_DEFAULT ) + addr_modes.override.segment = PREFIX_SS_; + break; + case 4: + address += FPU_info->___esi; + break; + case 5: + address += FPU_info->___edi; + break; + case 6: + address += FPU_info->___ebp; + if ( addr_modes.override.segment == PREFIX_DEFAULT ) + addr_modes.override.segment = PREFIX_SS_; + break; + case 7: + address += FPU_info->___ebx; + break; + } + + add_segment: + address &= 0xffff; + + addr->offset = address; + + switch ( addr_modes.default_mode ) + { + case 0: + break; + case VM86: + address += vm86_segment(addr_modes.override.segment, + (unsigned short *)&(addr->selector)); + break; + case PM16: + case SEG32: + address = pm_address(FPU_modrm, addr_modes.override.segment, + (unsigned short *)&(addr->selector), address); + break; + default: + EXCEPTION(EX_INTERNAL|0x131); + } + + return (void *)address ; +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/load_store.c linux/arch/i386/math-emu/load_store.c --- v1.1.76/linux/arch/i386/math-emu/load_store.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/load_store.c Thu Jun 2 10:28:26 1994 @@ -0,0 +1,260 @@ +/*---------------------------------------------------------------------------+ + | 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,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | 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 + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "control_w.h" + + +#define _NONE_ 0 /* 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() { st0_ptr->tag = TW_Empty; top++; } + + +static unsigned char const 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_ + }; + +unsigned char const data_sizes_16[32] = { + 4, 4, 8, 2, 0, 0, 0, 0, + 4, 4, 8, 2, 4, 4, 8, 2, + 14, 0, 94, 10, 2, 10, 0, 8, + 14, 0, 94, 10, 2, 10, 2, 8 +}; + +unsigned char const data_sizes_32[32] = { + 4, 4, 8, 2, 0, 0, 0, 0, + 4, 4, 8, 2, 4, 4, 8, 2, + 28, 0,108, 10, 2, 10, 0, 8, + 28, 0,108, 10, 2, 10, 2, 8 +}; + +int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, + void *data_address) +{ + FPU_REG loaded_data; + FPU_REG *st0_ptr; + + st0_ptr = NULL; /* Initialized just to stop compiler warnings. */ + + if ( addr_modes.default_mode & PROTECTED ) + { + if ( addr_modes.default_mode == SEG32 ) + { + if ( access_limit < data_sizes_32[type] ) + math_abort(FPU_info,SIGSEGV); + } + else if ( addr_modes.default_mode == PM16 ) + { + if ( access_limit < data_sizes_16[type] ) + math_abort(FPU_info,SIGSEGV); + } +#ifdef PARANOID + else + EXCEPTION(EX_INTERNAL|0x140); +#endif PARANOID + } + + switch ( type_table[type] ) + { + case _NONE_: + break; + case _REG0_: + st0_ptr = &st(0); /* Some of these instructions pop after + storing */ + break; + case _PUSH_: + { + st0_ptr = &st(-1); + if ( st0_ptr->tag != TW_Empty ) + { stack_overflow(); return 0; } + top--; + } + break; + case _null_: + FPU_illegal(); + return 0; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL|0x141); + return 0; +#endif PARANOID + } + + switch ( type ) + { + case 000: /* fld m32real */ + clear_C1(); + reg_load_single((float *)data_address, &loaded_data); + if ( (loaded_data.tag == TW_NaN) && + real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) + { + top++; + break; + } + reg_move(&loaded_data, st0_ptr); + break; + case 001: /* fild m32int */ + clear_C1(); + reg_load_int32((long *)data_address, st0_ptr); + break; + case 002: /* fld m64real */ + clear_C1(); + reg_load_double((double *)data_address, &loaded_data); + if ( (loaded_data.tag == TW_NaN) && + real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) + { + top++; + break; + } + reg_move(&loaded_data, st0_ptr); + break; + case 003: /* fild m16int */ + clear_C1(); + reg_load_int16((short *)data_address, st0_ptr); + break; + case 010: /* fst m32real */ + clear_C1(); + reg_store_single((float *)data_address, st0_ptr); + break; + case 011: /* fist m32int */ + clear_C1(); + reg_store_int32((long *)data_address, st0_ptr); + break; + case 012: /* fst m64real */ + clear_C1(); + reg_store_double((double *)data_address, st0_ptr); + break; + case 013: /* fist m16int */ + clear_C1(); + reg_store_int16((short *)data_address, st0_ptr); + break; + case 014: /* fstp m32real */ + clear_C1(); + if ( reg_store_single((float *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 015: /* fistp m32int */ + clear_C1(); + if ( reg_store_int32((long *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 016: /* fstp m64real */ + clear_C1(); + if ( reg_store_double((double *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 017: /* fistp m16int */ + clear_C1(); + if ( reg_store_int16((short *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 020: /* fldenv m14/28byte */ + fldenv(addr_modes, (char *)data_address); + /* Ensure that the values just loaded are not changed by + fix-up operations. */ + return 1; + case 022: /* frstor m94/108byte */ + frstor(addr_modes, (char *)data_address); + /* Ensure that the values just loaded are not changed by + fix-up operations. */ + return 1; + case 023: /* fbld m80dec */ + clear_C1(); + reg_load_bcd((char *)data_address, st0_ptr); + break; + case 024: /* fldcw */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, data_address, 2); + control_word = get_fs_word((unsigned short *) data_address); + RE_ENTRANT_CHECK_ON; + if ( partial_status & ~control_word & CW_Exceptions ) + partial_status |= (SW_Summary | SW_Backward); + else + partial_status &= ~(SW_Summary | SW_Backward); +#ifdef PECULIAR_486 + control_word |= 0x40; /* An 80486 appears to always set this bit */ +#endif PECULIAR_486 + return 1; + case 025: /* fld m80real */ + clear_C1(); + reg_load_extended((long double *)data_address, st0_ptr); + break; + case 027: /* fild m64int */ + clear_C1(); + reg_load_int64((long long *)data_address, st0_ptr); + break; + case 030: /* fstenv m14/28byte */ + fstenv(addr_modes, (char *)data_address); + return 1; + case 032: /* fsave */ + fsave(addr_modes, (char *)data_address); + return 1; + case 033: /* fbstp m80dec */ + clear_C1(); + if ( reg_store_bcd((char *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 034: /* fstcw m16int */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,data_address,2); + put_fs_word(control_word, (short *) data_address); + RE_ENTRANT_CHECK_ON; + return 1; + case 035: /* fstp m80real */ + clear_C1(); + if ( reg_store_extended((long double *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 036: /* fstsw m2byte */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,data_address,2); + put_fs_word(status_word(),(short *) data_address); + RE_ENTRANT_CHECK_ON; + return 1; + case 037: /* fistp m64int */ + clear_C1(); + if ( reg_store_int64((long long *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + } + return 0; +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/mul_Xsig.S linux/arch/i386/math-emu/mul_Xsig.S --- v1.1.76/linux/arch/i386/math-emu/mul_Xsig.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/mul_Xsig.S Mon Aug 1 08:19:13 1994 @@ -0,0 +1,182 @@ +/*---------------------------------------------------------------------------+ + | mul_Xsig.S | + | | + | Multiply a 12 byte fixed point number by another fixed point number. | + | | + | Copyright (C) 1992,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void mul32_Xsig(Xsig *x, unsigned b) | + | | + | void mul64_Xsig(Xsig *x, unsigned long long *b) | + | | + | void mul_Xsig_Xsig(Xsig *x, unsigned *b) | + | | + | The result is neither rounded nor normalized, and the ls bit or so may | + | be wrong. | + | | + +---------------------------------------------------------------------------*/ + .file "mul_Xsig.S" + + +#include "fpu_asm.h" + +.text + .align 2,144 +.globl _mul32_Xsig +_mul32_Xsig: + pushl %ebp + movl %esp,%ebp + subl $16,%esp + pushl %esi + + movl PARAM1,%esi + movl PARAM2,%ecx + + xor %eax,%eax + movl %eax,-4(%ebp) + movl %eax,-8(%ebp) + + movl (%esi),%eax /* lsl of Xsig */ + mull %ecx /* msl of b */ + movl %edx,-12(%ebp) + + movl 4(%esi),%eax /* midl of Xsig */ + mull %ecx /* msl of b */ + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull %ecx /* msl of b */ + addl %eax,-8(%ebp) + adcl %edx,-4(%ebp) + + movl -12(%ebp),%eax + movl %eax,(%esi) + movl -8(%ebp),%eax + movl %eax,4(%esi) + movl -4(%ebp),%eax + movl %eax,8(%esi) + + popl %esi + leave + ret + + + .align 2,144 +.globl _mul64_Xsig +_mul64_Xsig: + pushl %ebp + movl %esp,%ebp + subl $16,%esp + pushl %esi + + movl PARAM1,%esi + movl PARAM2,%ecx + + xor %eax,%eax + movl %eax,-4(%ebp) + movl %eax,-8(%ebp) + + movl (%esi),%eax /* lsl of Xsig */ + mull 4(%ecx) /* msl of b */ + movl %edx,-12(%ebp) + + movl 4(%esi),%eax /* midl of Xsig */ + mull (%ecx) /* lsl of b */ + addl %edx,-12(%ebp) + adcl $0,-8(%ebp) + adcl $0,-4(%ebp) + + movl 4(%esi),%eax /* midl of Xsig */ + mull 4(%ecx) /* msl of b */ + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull (%ecx) /* lsl of b */ + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull 4(%ecx) /* msl of b */ + addl %eax,-8(%ebp) + adcl %edx,-4(%ebp) + + movl -12(%ebp),%eax + movl %eax,(%esi) + movl -8(%ebp),%eax + movl %eax,4(%esi) + movl -4(%ebp),%eax + movl %eax,8(%esi) + + popl %esi + leave + ret + + + + .align 2,144 +.globl _mul_Xsig_Xsig +_mul_Xsig_Xsig: + pushl %ebp + movl %esp,%ebp + subl $16,%esp + pushl %esi + + movl PARAM1,%esi + movl PARAM2,%ecx + + xor %eax,%eax + movl %eax,-4(%ebp) + movl %eax,-8(%ebp) + + movl (%esi),%eax /* lsl of Xsig */ + mull 8(%ecx) /* msl of b */ + movl %edx,-12(%ebp) + + movl 4(%esi),%eax /* midl of Xsig */ + mull 4(%ecx) /* midl of b */ + addl %edx,-12(%ebp) + adcl $0,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull (%ecx) /* lsl of b */ + addl %edx,-12(%ebp) + adcl $0,-8(%ebp) + adcl $0,-4(%ebp) + + movl 4(%esi),%eax /* midl of Xsig */ + mull 8(%ecx) /* msl of b */ + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull 4(%ecx) /* midl of b */ + addl %eax,-12(%ebp) + adcl %edx,-8(%ebp) + adcl $0,-4(%ebp) + + movl 8(%esi),%eax /* msl of Xsig */ + mull 8(%ecx) /* msl of b */ + addl %eax,-8(%ebp) + adcl %edx,-4(%ebp) + + movl -12(%ebp),%edx + movl %edx,(%esi) + movl -8(%ebp),%edx + movl %edx,4(%esi) + movl -4(%ebp),%edx + movl %edx,8(%esi) + + popl %esi + leave + ret + diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/poly.h linux/arch/i386/math-emu/poly.h --- v1.1.76/linux/arch/i386/math-emu/poly.h Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/poly.h Mon Aug 1 08:19:14 1994 @@ -0,0 +1,116 @@ +/*---------------------------------------------------------------------------+ + | poly.h | + | | + | Header file for the FPU-emu poly*.c source files. | + | | + | Copyright (C) 1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Declarations and definitions for functions operating on Xsig (12-byte | + | extended-significand) quantities. | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _POLY_H +#define _POLY_H + +/* This 12-byte structure is used to improve the accuracy of computation + of transcendental functions. + Intended to be used to get results better than 8-byte computation + allows. 9-byte would probably be sufficient. + */ +typedef struct { + unsigned long lsw; + unsigned long midw; + unsigned long msw; +} Xsig; + +asmlinkage void mul64(unsigned long long const *a, unsigned long long const *b, + unsigned long long *result); +asmlinkage void polynomial_Xsig(Xsig *, const unsigned long long *x, + const unsigned long long terms[], const int n); + +asmlinkage void mul32_Xsig(Xsig *, const unsigned long mult); +asmlinkage void mul64_Xsig(Xsig *, const unsigned long long *mult); +asmlinkage void mul_Xsig_Xsig(Xsig *dest, const Xsig *mult); + +asmlinkage void shr_Xsig(Xsig *, const int n); +asmlinkage int round_Xsig(Xsig *); +asmlinkage int norm_Xsig(Xsig *); +asmlinkage void div_Xsig(Xsig *x1, const Xsig *x2, const Xsig *dest); + +/* Macro to extract the most significant 32 bits from a long long */ +#define LL_MSW(x) (((unsigned long *)&x)[1]) + +/* Macro to initialize an Xsig struct */ +#define MK_XSIG(a,b,c) { c, b, a } + +/* Macro to access the 8 ms bytes of an Xsig as a long long */ +#define XSIG_LL(x) (*(unsigned long long *)&x.midw) + + +/* + Need to run gcc with optimizations on to get these to + actually be in-line. + */ + +/* Multiply two fixed-point 32 bit numbers. */ +extern inline void mul_32_32(const unsigned long arg1, + const unsigned long arg2, + unsigned long *out) +{ + asm volatile ("movl %1,%%eax; mull %2; movl %%edx,%0" \ + :"=g" (*out) \ + :"g" (arg1), "g" (arg2) \ + :"ax","dx"); +} + + +/* Add the 12 byte Xsig x2 to Xsig dest, with no checks for overflow. */ +extern inline void add_Xsig_Xsig(Xsig *dest, const Xsig *x2) +{ + asm volatile ("movl %1,%%edi; movl %2,%%esi; + movl (%%esi),%%eax; addl %%eax,(%%edi); + movl 4(%%esi),%%eax; adcl %%eax,4(%%edi); + movl 8(%%esi),%%eax; adcl %%eax,8(%%edi);" + :"=g" (*dest):"g" (dest), "g" (x2) + :"ax","si","di"); +} + + +/* Add the 12 byte Xsig x2 to Xsig dest, adjust exp if overflow occurs. */ +/* Note: the constraints in the asm statement didn't always work properly + with gcc 2.5.8. Changing from using edi to using ecx got around the + problem, but keep fingers crossed! */ +extern inline int add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp) +{ + asm volatile ("movl %2,%%ecx; movl %3,%%esi; + movl (%%esi),%%eax; addl %%eax,(%%ecx); + movl 4(%%esi),%%eax; adcl %%eax,4(%%ecx); + movl 8(%%esi),%%eax; adcl %%eax,8(%%ecx); + jnc 0f; + rcrl 8(%%ecx); rcrl 4(%%ecx); rcrl (%%ecx) + movl %4,%%ecx; incl (%%ecx) + movl $1,%%eax; jmp 1f; + 0: xorl %%eax,%%eax; + 1:" + :"=g" (*exp), "=g" (*dest) + :"g" (dest), "g" (x2), "g" (exp) + :"cx","si","ax"); +} + + +/* Negate (subtract from 1.0) the 12 byte Xsig */ +/* This is faster in a loop on my 386 than using the "neg" instruction. */ +extern inline void negate_Xsig(Xsig *x) +{ + asm volatile("movl %1,%%esi; " + "xorl %%ecx,%%ecx; " + "movl %%ecx,%%eax; subl (%%esi),%%eax; movl %%eax,(%%esi); " + "movl %%ecx,%%eax; sbbl 4(%%esi),%%eax; movl %%eax,4(%%esi); " + "movl %%ecx,%%eax; sbbl 8(%%esi),%%eax; movl %%eax,8(%%esi); " + :"=g" (*x):"g" (x):"si","ax","cx"); +} + +#endif _POLY_H diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/poly_2xm1.c linux/arch/i386/math-emu/poly_2xm1.c --- v1.1.76/linux/arch/i386/math-emu/poly_2xm1.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/poly_2xm1.c Mon Aug 1 08:19:14 1994 @@ -0,0 +1,152 @@ +/*---------------------------------------------------------------------------+ + | poly_2xm1.c | + | | + | Function to compute 2^x-1 by a polynomial approximation. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "poly.h" + + +#define HIPOWER 11 +static const unsigned long long lterms[HIPOWER] = +{ + 0x0000000000000000LL, /* This term done separately as 12 bytes */ + 0xf5fdeffc162c7543LL, + 0x1c6b08d704a0bfa6LL, + 0x0276556df749cc21LL, + 0x002bb0ffcf14f6b8LL, + 0x0002861225ef751cLL, + 0x00001ffcbfcd5422LL, + 0x00000162c005d5f1LL, + 0x0000000da96ccb1bLL, + 0x0000000078d1b897LL, + 0x000000000422b029LL +}; + +static const Xsig hiterm = MK_XSIG(0xb17217f7, 0xd1cf79ab, 0xc8a39194); + +/* Four slices: 0.0 : 0.25 : 0.50 : 0.75 : 1.0, + These numbers are 2^(1/4), 2^(1/2), and 2^(3/4) + */ +static const Xsig shiftterm0 = MK_XSIG(0, 0, 0); +static const Xsig shiftterm1 = MK_XSIG(0x9837f051, 0x8db8a96f, 0x46ad2318); +static const Xsig shiftterm2 = MK_XSIG(0xb504f333, 0xf9de6484, 0x597d89b3); +static const Xsig shiftterm3 = MK_XSIG(0xd744fcca, 0xd69d6af4, 0x39a68bb9); + +static const Xsig *shiftterm[] = { &shiftterm0, &shiftterm1, + &shiftterm2, &shiftterm3 }; + + +/*--- poly_2xm1() -----------------------------------------------------------+ + | Requires an argument which is TW_Valid and < 1. | + +---------------------------------------------------------------------------*/ +int poly_2xm1(FPU_REG const *arg, FPU_REG *result) +{ + long int exponent, shift; + unsigned long long Xll; + Xsig accumulator, Denom, argSignif; + + + exponent = arg->exp - EXP_BIAS; + +#ifdef PARANOID + if ( (exponent >= 0) /* Don't want a |number| >= 1.0 */ + || (arg->tag != TW_Valid) ) + { + /* Number negative, too large, or not Valid. */ + EXCEPTION(EX_INTERNAL|0x127); + return 1; + } +#endif PARANOID + + argSignif.lsw = 0; + XSIG_LL(argSignif) = Xll = significand(arg); + + if ( exponent == -1 ) + { + shift = (argSignif.msw & 0x40000000) ? 3 : 2; + /* subtract 0.5 or 0.75 */ + exponent -= 2; + XSIG_LL(argSignif) <<= 2; + Xll <<= 2; + } + else if ( exponent == -2 ) + { + shift = 1; + /* subtract 0.25 */ + exponent--; + XSIG_LL(argSignif) <<= 1; + Xll <<= 1; + } + else + shift = 0; + + if ( exponent < -2 ) + { + /* Shift the argument right by the required places. */ + if ( shrx(&Xll, -2-exponent) >= 0x80000000U ) + Xll++; /* round up */ + } + + accumulator.lsw = accumulator.midw = accumulator.msw = 0; + polynomial_Xsig(&accumulator, &Xll, lterms, HIPOWER-1); + mul_Xsig_Xsig(&accumulator, &argSignif); + shr_Xsig(&accumulator, 3); + + mul_Xsig_Xsig(&argSignif, &hiterm); /* The leading term */ + add_two_Xsig(&accumulator, &argSignif, &exponent); + + if ( shift ) + { + /* The argument is large, use the identity: + f(x+a) = f(a) * (f(x) + 1) - 1; + */ + shr_Xsig(&accumulator, - exponent); + accumulator.msw |= 0x80000000; /* add 1.0 */ + mul_Xsig_Xsig(&accumulator, shiftterm[shift]); + accumulator.msw &= 0x3fffffff; /* subtract 1.0 */ + exponent = 1; + } + + if ( arg->sign != SIGN_POS ) + { + /* The argument is negative, use the identity: + f(-x) = -f(x) / (1 + f(x)) + */ + Denom.lsw = accumulator.lsw; + XSIG_LL(Denom) = XSIG_LL(accumulator); + if ( exponent < 0 ) + shr_Xsig(&Denom, - exponent); + else if ( exponent > 0 ) + { + /* exponent must be 1 here */ + XSIG_LL(Denom) <<= 1; + if ( Denom.lsw & 0x80000000 ) + XSIG_LL(Denom) |= 1; + (Denom.lsw) <<= 1; + } + Denom.msw |= 0x80000000; /* add 1.0 */ + div_Xsig(&accumulator, &Denom, &accumulator); + } + + /* Convert to 64 bit signed-compatible */ + exponent += round_Xsig(&accumulator); + + significand(result) = XSIG_LL(accumulator); + result->tag = TW_Valid; + result->exp = exponent + EXP_BIAS; + result->sign = arg->sign; + + return 0; + +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/poly_atan.c linux/arch/i386/math-emu/poly_atan.c --- v1.1.76/linux/arch/i386/math-emu/poly_atan.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/poly_atan.c Mon Aug 1 08:19:14 1994 @@ -0,0 +1,197 @@ +/*---------------------------------------------------------------------------+ + | poly_atan.c | + | | + | Compute the arctan of a FPU_REG, using a polynomial approximation. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "control_w.h" +#include "poly.h" + + +#define HIPOWERon 6 /* odd poly, negative terms */ +static const unsigned long long oddnegterms[HIPOWERon] = +{ + 0x0000000000000000LL, /* Dummy (not for - 1.0) */ + 0x015328437f756467LL, + 0x0005dda27b73dec6LL, + 0x0000226bf2bfb91aLL, + 0x000000ccc439c5f7LL, + 0x0000000355438407LL +} ; + +#define HIPOWERop 6 /* odd poly, positive terms */ +static const unsigned long long oddplterms[HIPOWERop] = +{ +/* 0xaaaaaaaaaaaaaaabLL, transferred to fixedpterm[] */ + 0x0db55a71875c9ac2LL, + 0x0029fce2d67880b0LL, + 0x0000dfd3908b4596LL, + 0x00000550fd61dab4LL, + 0x0000001c9422b3f9LL, + 0x000000003e3301e1LL +}; + +static const unsigned long long denomterm = 0xebd9b842c5c53a0eLL; + +static const Xsig fixedpterm = MK_XSIG(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa); + +static const Xsig pi_signif = MK_XSIG(0xc90fdaa2, 0x2168c234, 0xc4c6628b); + + +/*--- poly_atan() -----------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result) +{ + char transformed, inverted, + sign1 = arg1->sign, sign2 = arg2->sign; + long int exponent, dummy_exp; + Xsig accumulator, Numer, Denom, accumulatore, argSignif, + argSq, argSqSq; + + + arg1->sign = arg2->sign = SIGN_POS; + if ( (compare(arg2) & ~COMP_Denormal) == COMP_A_lt_B ) + { + inverted = 1; + exponent = arg1->exp - arg2->exp; + Numer.lsw = Denom.lsw = 0; + XSIG_LL(Numer) = significand(arg1); + XSIG_LL(Denom) = significand(arg2); + } + else + { + inverted = 0; + exponent = arg2->exp - arg1->exp; + Numer.lsw = Denom.lsw = 0; + XSIG_LL(Numer) = significand(arg2); + XSIG_LL(Denom) = significand(arg1); + } + div_Xsig(&Numer, &Denom, &argSignif); + exponent += norm_Xsig(&argSignif); + + if ( (exponent >= -1) + || ((exponent == -2) && (argSignif.msw > 0xd413ccd0)) ) + { + /* The argument is greater than sqrt(2)-1 (=0.414213562...) */ + /* Convert the argument by an identity for atan */ + transformed = 1; + + if ( exponent >= 0 ) + { +#ifdef PARANOID + if ( !( (exponent == 0) && + (argSignif.lsw == 0) && (argSignif.midw == 0) && + (argSignif.msw == 0x80000000) ) ) + { + EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */ + return; + } +#endif PARANOID + argSignif.msw = 0; /* Make the transformed arg -> 0.0 */ + } + else + { + Numer.lsw = Denom.lsw = argSignif.lsw; + XSIG_LL(Numer) = XSIG_LL(Denom) = XSIG_LL(argSignif); + + if ( exponent < -1 ) + shr_Xsig(&Numer, -1-exponent); + negate_Xsig(&Numer); + + shr_Xsig(&Denom, -exponent); + Denom.msw |= 0x80000000; + + div_Xsig(&Numer, &Denom, &argSignif); + + exponent = -1 + norm_Xsig(&argSignif); + } + } + else + { + transformed = 0; + } + + argSq.lsw = argSignif.lsw; argSq.midw = argSignif.midw; + argSq.msw = argSignif.msw; + mul_Xsig_Xsig(&argSq, &argSq); + + argSqSq.lsw = argSq.lsw; argSqSq.midw = argSq.midw; argSqSq.msw = argSq.msw; + mul_Xsig_Xsig(&argSqSq, &argSqSq); + + accumulatore.lsw = argSq.lsw; + XSIG_LL(accumulatore) = XSIG_LL(argSq); + + shr_Xsig(&argSq, 2*(-1-exponent-1)); + shr_Xsig(&argSqSq, 4*(-1-exponent-1)); + + /* Now have argSq etc with binary point at the left + .1xxxxxxxx */ + + /* Do the basic fixed point polynomial evaluation */ + accumulator.msw = accumulator.midw = accumulator.lsw = 0; + polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), + oddplterms, HIPOWERop-1); + mul64_Xsig(&accumulator, &XSIG_LL(argSq)); + negate_Xsig(&accumulator); + polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddnegterms, HIPOWERon-1); + negate_Xsig(&accumulator); + add_two_Xsig(&accumulator, &fixedpterm, &dummy_exp); + + mul64_Xsig(&accumulatore, &denomterm); + shr_Xsig(&accumulatore, 1 + 2*(-1-exponent)); + accumulatore.msw |= 0x80000000; + + div_Xsig(&accumulator, &accumulatore, &accumulator); + + mul_Xsig_Xsig(&accumulator, &argSignif); + mul_Xsig_Xsig(&accumulator, &argSq); + + shr_Xsig(&accumulator, 3); + negate_Xsig(&accumulator); + add_Xsig_Xsig(&accumulator, &argSignif); + + if ( transformed ) + { + /* compute pi/4 - accumulator */ + shr_Xsig(&accumulator, -1-exponent); + negate_Xsig(&accumulator); + add_Xsig_Xsig(&accumulator, &pi_signif); + exponent = -1; + } + + if ( inverted ) + { + /* compute pi/2 - accumulator */ + shr_Xsig(&accumulator, -exponent); + negate_Xsig(&accumulator); + add_Xsig_Xsig(&accumulator, &pi_signif); + exponent = 0; + } + + if ( sign1 ) + { + /* compute pi - accumulator */ + shr_Xsig(&accumulator, 1 - exponent); + negate_Xsig(&accumulator); + add_Xsig_Xsig(&accumulator, &pi_signif); + exponent = 1; + } + + exponent += round_Xsig(&accumulator); + significand(result) = XSIG_LL(accumulator); + result->exp = exponent + EXP_BIAS; + result->tag = TW_Valid; + result->sign = sign2; + +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/poly_l2.c linux/arch/i386/math-emu/poly_l2.c --- v1.1.76/linux/arch/i386/math-emu/poly_l2.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/poly_l2.c Mon Aug 1 08:19:14 1994 @@ -0,0 +1,255 @@ +/*---------------------------------------------------------------------------+ + | poly_l2.c | + | | + | Compute the base 2 log of a FPU_REG, using a polynomial approximation. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "poly.h" + + + +static void log2_kernel(FPU_REG const *arg, + Xsig *accum_result, long int *expon); + + +/*--- poly_l2() -------------------------------------------------------------+ + | Base 2 logarithm by a polynomial approximation. | + +---------------------------------------------------------------------------*/ +void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result) +{ + long int exponent, expon, expon_expon; + Xsig accumulator, expon_accum, yaccum; + char sign; + FPU_REG x; + + + exponent = arg->exp - EXP_BIAS; + + /* From arg, make a number > sqrt(2)/2 and < sqrt(2) */ + if ( arg->sigh > (unsigned)0xb504f334 ) + { + /* Treat as sqrt(2)/2 < arg < 1 */ + significand(&x) = - significand(arg); + x.sign = SIGN_NEG; + x.tag = TW_Valid; + x.exp = EXP_BIAS-1; + exponent++; + normalize(&x); + } + else + { + /* Treat as 1 <= arg < sqrt(2) */ + x.sigh = arg->sigh - 0x80000000; + x.sigl = arg->sigl; + x.sign = SIGN_POS; + x.tag = TW_Valid; + x.exp = EXP_BIAS; + normalize(&x); + } + + if ( x.tag == TW_Zero ) + { + expon = 0; + accumulator.msw = accumulator.midw = accumulator.lsw = 0; + } + else + { + log2_kernel(&x, &accumulator, &expon); + } + + sign = exponent < 0; + if ( sign ) exponent = -exponent; + expon_accum.msw = exponent; expon_accum.midw = expon_accum.lsw = 0; + if ( exponent ) + { + expon_expon = 31 + norm_Xsig(&expon_accum); + shr_Xsig(&accumulator, expon_expon - expon); + + if ( sign ^ (x.sign == SIGN_NEG) ) + negate_Xsig(&accumulator); + add_Xsig_Xsig(&accumulator, &expon_accum); + } + else + { + expon_expon = expon; + sign = x.sign; + } + + yaccum.lsw = 0; XSIG_LL(yaccum) = significand(y); + mul_Xsig_Xsig(&accumulator, &yaccum); + + expon_expon += round_Xsig(&accumulator); + + if ( accumulator.msw == 0 ) + { + reg_move(&CONST_Z, y); + } + else + { + result->exp = expon_expon + y->exp + 1; + significand(result) = XSIG_LL(accumulator); + result->tag = TW_Valid; /* set the tags to Valid */ + result->sign = sign ^ y->sign; + } + + return; +} + + +/*--- poly_l2p1() -----------------------------------------------------------+ + | Base 2 logarithm by a polynomial approximation. | + | log2(x+1) | + +---------------------------------------------------------------------------*/ +int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result) +{ + char sign; + long int exponent; + Xsig accumulator, yaccum; + + + sign = arg->sign; + + if ( arg->exp < EXP_BIAS ) + { + log2_kernel(arg, &accumulator, &exponent); + + yaccum.lsw = 0; + XSIG_LL(yaccum) = significand(y); + mul_Xsig_Xsig(&accumulator, &yaccum); + + exponent += round_Xsig(&accumulator); + + result->exp = exponent + y->exp + 1; + significand(result) = XSIG_LL(accumulator); + result->tag = TW_Valid; /* set the tags to Valid */ + result->sign = sign ^ y->sign; + + return 0; + } + else + { + /* The magnitude of arg is far too large. */ + reg_move(y, result); + if ( sign != SIGN_POS ) + { + /* Trying to get the log of a negative number. */ + return 1; + } + else + { + return 0; + } + } + +} + + + + +#undef HIPOWER +#define HIPOWER 10 +static const unsigned long long logterms[HIPOWER] = +{ + 0x2a8eca5705fc2ef0LL, + 0xf6384ee1d01febceLL, + 0x093bb62877cdf642LL, + 0x006985d8a9ec439bLL, + 0x0005212c4f55a9c8LL, + 0x00004326a16927f0LL, + 0x0000038d1d80a0e7LL, + 0x0000003141cc80c6LL, + 0x00000002b1668c9fLL, + 0x000000002c7a46aaLL +}; + +static const unsigned long leadterm = 0xb8000000; + + +/*--- log2_kernel() ---------------------------------------------------------+ + | Base 2 logarithm by a polynomial approximation. | + | log2(x+1) | + +---------------------------------------------------------------------------*/ +static void log2_kernel(FPU_REG const *arg, Xsig *accum_result, + long int *expon) +{ + char sign; + long int exponent, adj; + unsigned long long Xsq; + Xsig accumulator, Numer, Denom, argSignif, arg_signif; + + sign = arg->sign; + + exponent = arg->exp - EXP_BIAS; + Numer.lsw = Denom.lsw = 0; + XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg); + if ( sign == SIGN_POS ) + { + shr_Xsig(&Denom, 2 - (1 + exponent)); + Denom.msw |= 0x80000000; + div_Xsig(&Numer, &Denom, &argSignif); + } + else + { + shr_Xsig(&Denom, 1 - (1 + exponent)); + negate_Xsig(&Denom); + if ( Denom.msw & 0x80000000 ) + { + div_Xsig(&Numer, &Denom, &argSignif); + exponent ++; + } + else + { + /* Denom must be 1.0 */ + argSignif.lsw = Numer.lsw; argSignif.midw = Numer.midw; + argSignif.msw = Numer.msw; + } + } + +#ifndef PECULIAR_486 + /* Should check here that |local_arg| is within the valid range */ + if ( exponent >= -2 ) + { + if ( (exponent > -2) || + (argSignif.msw > (unsigned)0xafb0ccc0) ) + { + /* The argument is too large */ + } + } +#endif PECULIAR_486 + + arg_signif.lsw = argSignif.lsw; XSIG_LL(arg_signif) = XSIG_LL(argSignif); + adj = norm_Xsig(&argSignif); + accumulator.lsw = argSignif.lsw; XSIG_LL(accumulator) = XSIG_LL(argSignif); + mul_Xsig_Xsig(&accumulator, &accumulator); + shr_Xsig(&accumulator, 2*(-1 - (1 + exponent + adj))); + Xsq = XSIG_LL(accumulator); + if ( accumulator.lsw & 0x80000000 ) + Xsq++; + + accumulator.msw = accumulator.midw = accumulator.lsw = 0; + /* Do the basic fixed point polynomial evaluation */ + polynomial_Xsig(&accumulator, &Xsq, logterms, HIPOWER-1); + + mul_Xsig_Xsig(&accumulator, &argSignif); + shr_Xsig(&accumulator, 6 - adj); + + mul32_Xsig(&arg_signif, leadterm); + add_two_Xsig(&accumulator, &arg_signif, &exponent); + + *expon = exponent + 1; + accum_result->lsw = accumulator.lsw; + accum_result->midw = accumulator.midw; + accum_result->msw = accumulator.msw; + +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/poly_sin.c linux/arch/i386/math-emu/poly_sin.c --- v1.1.76/linux/arch/i386/math-emu/poly_sin.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/poly_sin.c Mon Aug 1 08:19:15 1994 @@ -0,0 +1,408 @@ +/*---------------------------------------------------------------------------+ + | poly_sin.c | + | | + | Computation of an approximation of the sin function and the cosine | + | function by a polynomial. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "poly.h" + + +#define N_COEFF_P 4 +#define N_COEFF_N 4 + +static const unsigned long long pos_terms_l[N_COEFF_P] = +{ + 0xaaaaaaaaaaaaaaabLL, + 0x00d00d00d00cf906LL, + 0x000006b99159a8bbLL, + 0x000000000d7392e6LL +}; + +static const unsigned long long neg_terms_l[N_COEFF_N] = +{ + 0x2222222222222167LL, + 0x0002e3bc74aab624LL, + 0x0000000b09229062LL, + 0x00000000000c7973LL +}; + + + +#define N_COEFF_PH 4 +#define N_COEFF_NH 4 +static const unsigned long long pos_terms_h[N_COEFF_PH] = +{ + 0x0000000000000000LL, + 0x05b05b05b05b0406LL, + 0x000049f93edd91a9LL, + 0x00000000c9c9ed62LL +}; + +static const unsigned long long neg_terms_h[N_COEFF_NH] = +{ + 0xaaaaaaaaaaaaaa98LL, + 0x001a01a01a019064LL, + 0x0000008f76c68a77LL, + 0x0000000000d58f5eLL +}; + + +/*--- poly_sine() -----------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +void poly_sine(FPU_REG const *arg, FPU_REG *result) +{ + int exponent, echange; + Xsig accumulator, argSqrd, argTo4; + unsigned long fix_up, adj; + unsigned long long fixed_arg; + + +#ifdef PARANOID + if ( arg->tag == TW_Zero ) + { + /* Return 0.0 */ + reg_move(&CONST_Z, result); + return; + } +#endif PARANOID + + exponent = arg->exp - EXP_BIAS; + + accumulator.lsw = accumulator.midw = accumulator.msw = 0; + + /* Split into two ranges, for arguments below and above 1.0 */ + /* The boundary between upper and lower is approx 0.88309101259 */ + if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xe21240aa)) ) + { + /* The argument is <= 0.88309101259 */ + + argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; + mul64_Xsig(&argSqrd, &significand(arg)); + shr_Xsig(&argSqrd, 2*(-1-exponent)); + argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; + argTo4.lsw = argSqrd.lsw; + mul_Xsig_Xsig(&argTo4, &argTo4); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_l, + N_COEFF_N-1); + mul_Xsig_Xsig(&accumulator, &argSqrd); + negate_Xsig(&accumulator); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_l, + N_COEFF_P-1); + + shr_Xsig(&accumulator, 2); /* Divide by four */ + accumulator.msw |= 0x80000000; /* Add 1.0 */ + + mul64_Xsig(&accumulator, &significand(arg)); + mul64_Xsig(&accumulator, &significand(arg)); + mul64_Xsig(&accumulator, &significand(arg)); + + /* Divide by four, FPU_REG compatible, etc */ + exponent = 3*exponent + EXP_BIAS; + + /* The minimum exponent difference is 3 */ + shr_Xsig(&accumulator, arg->exp - exponent); + + negate_Xsig(&accumulator); + XSIG_LL(accumulator) += significand(arg); + + echange = round_Xsig(&accumulator); + + result->exp = arg->exp + echange; + } + else + { + /* The argument is > 0.88309101259 */ + /* We use sin(arg) = cos(pi/2-arg) */ + + fixed_arg = significand(arg); + + if ( exponent == 0 ) + { + /* The argument is >= 1.0 */ + + /* Put the binary point at the left. */ + fixed_arg <<= 1; + } + /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ + fixed_arg = 0x921fb54442d18469LL - fixed_arg; + + XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0; + mul64_Xsig(&argSqrd, &fixed_arg); + + XSIG_LL(argTo4) = XSIG_LL(argSqrd); argTo4.lsw = argSqrd.lsw; + mul_Xsig_Xsig(&argTo4, &argTo4); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_h, + N_COEFF_NH-1); + mul_Xsig_Xsig(&accumulator, &argSqrd); + negate_Xsig(&accumulator); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_h, + N_COEFF_PH-1); + negate_Xsig(&accumulator); + + mul64_Xsig(&accumulator, &fixed_arg); + mul64_Xsig(&accumulator, &fixed_arg); + + shr_Xsig(&accumulator, 3); + negate_Xsig(&accumulator); + + add_Xsig_Xsig(&accumulator, &argSqrd); + + shr_Xsig(&accumulator, 1); + + accumulator.lsw |= 1; /* A zero accumulator here would cause problems */ + negate_Xsig(&accumulator); + + /* The basic computation is complete. Now fix the answer to + compensate for the error due to the approximation used for + pi/2 + */ + + /* This has an exponent of -65 */ + fix_up = 0x898cc517; + /* The fix-up needs to be improved for larger args */ + if ( argSqrd.msw & 0xffc00000 ) + { + /* Get about 32 bit precision in these: */ + mul_32_32(0x898cc517, argSqrd.msw, &adj); + fix_up -= adj/6; + } + mul_32_32(fix_up, LL_MSW(fixed_arg), &fix_up); + + adj = accumulator.lsw; /* temp save */ + accumulator.lsw -= fix_up; + if ( accumulator.lsw > adj ) + XSIG_LL(accumulator) --; + + echange = round_Xsig(&accumulator); + + result->exp = EXP_BIAS - 1 + echange; + } + + significand(result) = XSIG_LL(accumulator); + result->tag = TW_Valid; + result->sign = arg->sign; + +#ifdef PARANOID + if ( (result->exp >= EXP_BIAS) + && (significand(result) > 0x8000000000000000LL) ) + { + EXCEPTION(EX_INTERNAL|0x150); + } +#endif PARANOID + +} + + + +/*--- poly_cos() ------------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +void poly_cos(FPU_REG const *arg, FPU_REG *result) +{ + long int exponent, exp2, echange; + Xsig accumulator, argSqrd, fix_up, argTo4; + unsigned long adj; + unsigned long long fixed_arg; + + +#ifdef PARANOID + if ( arg->tag == TW_Zero ) + { + /* Return 1.0 */ + reg_move(&CONST_1, result); + return; + } + + if ( (arg->exp > EXP_BIAS) + || ((arg->exp == EXP_BIAS) + && (significand(arg) > 0xc90fdaa22168c234LL)) ) + { + EXCEPTION(EX_Invalid); + reg_move(&CONST_QNaN, result); + return; + } +#endif PARANOID + + exponent = arg->exp - EXP_BIAS; + + accumulator.lsw = accumulator.midw = accumulator.msw = 0; + + if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xb00d6f54)) ) + { + /* arg is < 0.687705 */ + + argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; + mul64_Xsig(&argSqrd, &significand(arg)); + + if ( exponent < -1 ) + { + /* shift the argument right by the required places */ + shr_Xsig(&argSqrd, 2*(-1-exponent)); + } + + argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; + argTo4.lsw = argSqrd.lsw; + mul_Xsig_Xsig(&argTo4, &argTo4); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_h, + N_COEFF_NH-1); + mul_Xsig_Xsig(&accumulator, &argSqrd); + negate_Xsig(&accumulator); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_h, + N_COEFF_PH-1); + negate_Xsig(&accumulator); + + mul64_Xsig(&accumulator, &significand(arg)); + mul64_Xsig(&accumulator, &significand(arg)); + shr_Xsig(&accumulator, -2*(1+exponent)); + + shr_Xsig(&accumulator, 3); + negate_Xsig(&accumulator); + + add_Xsig_Xsig(&accumulator, &argSqrd); + + shr_Xsig(&accumulator, 1); + + /* It doesn't matter if accumulator is all zero here, the + following code will work ok */ + negate_Xsig(&accumulator); + + if ( accumulator.lsw & 0x80000000 ) + XSIG_LL(accumulator) ++; + if ( accumulator.msw == 0 ) + { + /* The result is 1.0 */ + reg_move(&CONST_1, result); + } + else + { + significand(result) = XSIG_LL(accumulator); + + /* will be a valid positive nr with expon = -1 */ + *(short *)&(result->sign) = 0; + result->exp = EXP_BIAS - 1; + } + } + else + { + fixed_arg = significand(arg); + + if ( exponent == 0 ) + { + /* The argument is >= 1.0 */ + + /* Put the binary point at the left. */ + fixed_arg <<= 1; + } + /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ + fixed_arg = 0x921fb54442d18469LL - fixed_arg; + + exponent = -1; + exp2 = -1; + + /* A shift is needed here only for a narrow range of arguments, + i.e. for fixed_arg approx 2^-32, but we pick up more... */ + if ( !(LL_MSW(fixed_arg) & 0xffff0000) ) + { + fixed_arg <<= 16; + exponent -= 16; + exp2 -= 16; + } + + XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0; + mul64_Xsig(&argSqrd, &fixed_arg); + + if ( exponent < -1 ) + { + /* shift the argument right by the required places */ + shr_Xsig(&argSqrd, 2*(-1-exponent)); + } + + argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; + argTo4.lsw = argSqrd.lsw; + mul_Xsig_Xsig(&argTo4, &argTo4); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_l, + N_COEFF_N-1); + mul_Xsig_Xsig(&accumulator, &argSqrd); + negate_Xsig(&accumulator); + + polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_l, + N_COEFF_P-1); + + shr_Xsig(&accumulator, 2); /* Divide by four */ + accumulator.msw |= 0x80000000; /* Add 1.0 */ + + mul64_Xsig(&accumulator, &fixed_arg); + mul64_Xsig(&accumulator, &fixed_arg); + mul64_Xsig(&accumulator, &fixed_arg); + + /* Divide by four, FPU_REG compatible, etc */ + exponent = 3*exponent; + + /* The minimum exponent difference is 3 */ + shr_Xsig(&accumulator, exp2 - exponent); + + negate_Xsig(&accumulator); + XSIG_LL(accumulator) += fixed_arg; + + /* The basic computation is complete. Now fix the answer to + compensate for the error due to the approximation used for + pi/2 + */ + + /* This has an exponent of -65 */ + XSIG_LL(fix_up) = 0x898cc51701b839a2ll; + fix_up.lsw = 0; + + /* The fix-up needs to be improved for larger args */ + if ( argSqrd.msw & 0xffc00000 ) + { + /* Get about 32 bit precision in these: */ + mul_32_32(0x898cc517, argSqrd.msw, &adj); + fix_up.msw -= adj/2; + mul_32_32(0x898cc517, argTo4.msw, &adj); + fix_up.msw += adj/24; + } + + exp2 += norm_Xsig(&accumulator); + shr_Xsig(&accumulator, 1); /* Prevent overflow */ + exp2++; + shr_Xsig(&fix_up, 65 + exp2); + + add_Xsig_Xsig(&accumulator, &fix_up); + + echange = round_Xsig(&accumulator); + + result->exp = exp2 + EXP_BIAS + echange; + *(short *)&(result->sign) = 0; /* Is a valid positive nr */ + significand(result) = XSIG_LL(accumulator); + } + +#ifdef PARANOID + if ( (result->exp >= EXP_BIAS) + && (significand(result) > 0x8000000000000000LL) ) + { + EXCEPTION(EX_INTERNAL|0x151); + } +#endif PARANOID + +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/poly_tan.c linux/arch/i386/math-emu/poly_tan.c --- v1.1.76/linux/arch/i386/math-emu/poly_tan.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/poly_tan.c Fri Aug 19 08:54:01 1994 @@ -0,0 +1,213 @@ +/*---------------------------------------------------------------------------+ + | poly_tan.c | + | | + | Compute the tan of a FPU_REG, using a polynomial approximation. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "poly.h" + + +#define HiPOWERop 3 /* odd poly, positive terms */ +static const unsigned long long oddplterm[HiPOWERop] = +{ + 0x0000000000000000LL, + 0x0051a1cf08fca228LL, + 0x0000000071284ff7LL +}; + +#define HiPOWERon 2 /* odd poly, negative terms */ +static const unsigned long long oddnegterm[HiPOWERon] = +{ + 0x1291a9a184244e80LL, + 0x0000583245819c21LL +}; + +#define HiPOWERep 2 /* even poly, positive terms */ +static const unsigned long long evenplterm[HiPOWERep] = +{ + 0x0e848884b539e888LL, + 0x00003c7f18b887daLL +}; + +#define HiPOWERen 2 /* even poly, negative terms */ +static const unsigned long long evennegterm[HiPOWERen] = +{ + 0xf1f0200fd51569ccLL, + 0x003afb46105c4432LL +}; + +static const unsigned long long twothirds = 0xaaaaaaaaaaaaaaabLL; + + +/*--- poly_tan() ------------------------------------------------------------+ + | | + +---------------------------------------------------------------------------*/ +void poly_tan(FPU_REG const *arg, FPU_REG *result) +{ + long int exponent; + int invert; + Xsig argSq, argSqSq, accumulatoro, accumulatore, accum, + argSignif, fix_up; + unsigned long adj; + + exponent = arg->exp - EXP_BIAS; + +#ifdef PARANOID + if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */ + { arith_invalid(result); return; } /* Need a positive number */ +#endif PARANOID + + /* Split the problem into two domains, smaller and larger than pi/4 */ + if ( (exponent == 0) || ((exponent == -1) && (arg->sigh > 0xc90fdaa2)) ) + { + /* The argument is greater than (approx) pi/4 */ + invert = 1; + accum.lsw = 0; + XSIG_LL(accum) = significand(arg); + + if ( exponent == 0 ) + { + /* The argument is >= 1.0 */ + /* Put the binary point at the left. */ + XSIG_LL(accum) <<= 1; + } + /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ + XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum); + + argSignif.lsw = accum.lsw; + XSIG_LL(argSignif) = XSIG_LL(accum); + exponent = -1 + norm_Xsig(&argSignif); + } + else + { + invert = 0; + argSignif.lsw = 0; + XSIG_LL(accum) = XSIG_LL(argSignif) = significand(arg); + + if ( exponent < -1 ) + { + /* shift the argument right by the required places */ + if ( shrx(&XSIG_LL(accum), -1-exponent) >= 0x80000000U ) + XSIG_LL(accum) ++; /* round up */ + } + } + + XSIG_LL(argSq) = XSIG_LL(accum); argSq.lsw = accum.lsw; + mul_Xsig_Xsig(&argSq, &argSq); + XSIG_LL(argSqSq) = XSIG_LL(argSq); argSqSq.lsw = argSq.lsw; + mul_Xsig_Xsig(&argSqSq, &argSqSq); + + /* Compute the negative terms for the numerator polynomial */ + accumulatoro.msw = accumulatoro.midw = accumulatoro.lsw = 0; + polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddnegterm, HiPOWERon-1); + mul_Xsig_Xsig(&accumulatoro, &argSq); + negate_Xsig(&accumulatoro); + /* Add the positive terms */ + polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddplterm, HiPOWERop-1); + + + /* Compute the positive terms for the denominator polynomial */ + accumulatore.msw = accumulatore.midw = accumulatore.lsw = 0; + polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evenplterm, HiPOWERep-1); + mul_Xsig_Xsig(&accumulatore, &argSq); + negate_Xsig(&accumulatore); + /* Add the negative terms */ + polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evennegterm, HiPOWERen-1); + /* Multiply by arg^2 */ + mul64_Xsig(&accumulatore, &XSIG_LL(argSignif)); + mul64_Xsig(&accumulatore, &XSIG_LL(argSignif)); + /* de-normalize and divide by 2 */ + shr_Xsig(&accumulatore, -2*(1+exponent) + 1); + negate_Xsig(&accumulatore); /* This does 1 - accumulator */ + + /* Now find the ratio. */ + if ( accumulatore.msw == 0 ) + { + /* accumulatoro must contain 1.0 here, (actually, 0) but it + really doesn't matter what value we use because it will + have negligible effect in later calculations + */ + XSIG_LL(accum) = 0x8000000000000000LL; + accum.lsw = 0; + } + else + { + div_Xsig(&accumulatoro, &accumulatore, &accum); + } + + /* Multiply by 1/3 * arg^3 */ + mul64_Xsig(&accum, &XSIG_LL(argSignif)); + mul64_Xsig(&accum, &XSIG_LL(argSignif)); + mul64_Xsig(&accum, &XSIG_LL(argSignif)); + mul64_Xsig(&accum, &twothirds); + shr_Xsig(&accum, -2*(exponent+1)); + + /* tan(arg) = arg + accum */ + add_two_Xsig(&accum, &argSignif, &exponent); + + if ( invert ) + { + /* We now have the value of tan(pi_2 - arg) where pi_2 is an + approximation for pi/2 + */ + /* The next step is to fix the answer to compensate for the + error due to the approximation used for pi/2 + */ + + /* This is (approx) delta, the error in our approx for pi/2 + (see above). It has an exponent of -65 + */ + XSIG_LL(fix_up) = 0x898cc51701b839a2LL; + fix_up.lsw = 0; + + if ( exponent == 0 ) + adj = 0xffffffff; /* We want approx 1.0 here, but + this is close enough. */ + else if ( exponent > -30 ) + { + adj = accum.msw >> -(exponent+1); /* tan */ + mul_32_32(adj, adj, &adj); /* tan^2 */ + } + else + adj = 0; + mul_32_32(0x898cc517, adj, &adj); /* delta * tan^2 */ + + fix_up.msw += adj; + if ( !(fix_up.msw & 0x80000000) ) /* did fix_up overflow ? */ + { + /* Yes, we need to add an msb */ + shr_Xsig(&fix_up, 1); + fix_up.msw |= 0x80000000; + shr_Xsig(&fix_up, 64 + exponent); + } + else + shr_Xsig(&fix_up, 65 + exponent); + + add_two_Xsig(&accum, &fix_up, &exponent); + + /* accum now contains tan(pi/2 - arg). + Use tan(arg) = 1.0 / tan(pi/2 - arg) + */ + accumulatoro.lsw = accumulatoro.midw = 0; + accumulatoro.msw = 0x80000000; + div_Xsig(&accumulatoro, &accum, &accum); + exponent = - exponent - 1; + } + + /* Transfer the result */ + round_Xsig(&accum); + *(short *)&(result->sign) = 0; + significand(result) = XSIG_LL(accum); + result->exp = EXP_BIAS + exponent; + +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/polynom_Xsig.S linux/arch/i386/math-emu/polynom_Xsig.S --- v1.1.76/linux/arch/i386/math-emu/polynom_Xsig.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/polynom_Xsig.S Mon Aug 1 08:19:15 1994 @@ -0,0 +1,137 @@ +/*---------------------------------------------------------------------------+ + | polynomial_Xsig.S | + | | + | Fixed point arithmetic polynomial evaluation. | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void polynomial_Xsig(Xsig *accum, unsigned long long x, | + | unsigned long long terms[], int n) | + | | + | Computes: | + | terms[0] + (terms[1] + (terms[2] + ... + (terms[n-1]*x)*x)*x)*x) ... )*x | + | and adds the result to the 12 byte Xsig. | + | The terms[] are each 8 bytes, but all computation is performed to 12 byte | + | precision. | + | | + | This function must be used carefully: most overflow of intermediate | + | results is controlled, but overflow of the result is not. | + | | + +---------------------------------------------------------------------------*/ + .file "polynomial_Xsig.S" + +#include "fpu_asm.h" + + +#define TERM_SIZE $8 +#define SUM_MS -20(%ebp) /* sum ms long */ +#define SUM_MIDDLE -24(%ebp) /* sum middle long */ +#define SUM_LS -28(%ebp) /* sum ls long */ +#define ACCUM_MS -4(%ebp) /* accum ms long */ +#define ACCUM_MIDDLE -8(%ebp) /* accum middle long */ +#define ACCUM_LS -12(%ebp) /* accum ls long */ +#define OVERFLOWED -16(%ebp) /* addition overflow flag */ + +.text + .align 2,144 +.globl _polynomial_Xsig +_polynomial_Xsig: + pushl %ebp + movl %esp,%ebp + subl $32,%esp + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM2,%esi /* x */ + movl PARAM3,%edi /* terms */ + + movl TERM_SIZE,%eax + mull PARAM4 /* n */ + addl %eax,%edi + + movl 4(%edi),%edx /* terms[n] */ + movl %edx,SUM_MS + movl (%edi),%edx /* terms[n] */ + movl %edx,SUM_MIDDLE + xor %eax,%eax + movl %eax,SUM_LS + movb %al,OVERFLOWED + + subl TERM_SIZE,%edi + decl PARAM4 + js L_accum_done + +L_accum_loop: + xor %eax,%eax + movl %eax,ACCUM_MS + movl %eax,ACCUM_MIDDLE + + movl SUM_MIDDLE,%eax + mull (%esi) /* x ls long */ + movl %edx,ACCUM_LS + + movl SUM_MIDDLE,%eax + mull 4(%esi) /* x ms long */ + addl %eax,ACCUM_LS + adcl %edx,ACCUM_MIDDLE + adcl $0,ACCUM_MS + + movl SUM_MS,%eax + mull (%esi) /* x ls long */ + addl %eax,ACCUM_LS + adcl %edx,ACCUM_MIDDLE + adcl $0,ACCUM_MS + + movl SUM_MS,%eax + mull 4(%esi) /* x ms long */ + addl %eax,ACCUM_MIDDLE + adcl %edx,ACCUM_MS + + testb $0xff,OVERFLOWED + jz L_no_overflow + + movl (%esi),%eax + addl %eax,ACCUM_MIDDLE + movl 4(%esi),%eax + adcl %eax,ACCUM_MS /* This could overflow too */ + +L_no_overflow: + +/* + * Now put the sum of next term and the accumulator + * into the sum register + */ + movl ACCUM_LS,%eax + addl (%edi),%eax /* term ls long */ + movl %eax,SUM_LS + movl ACCUM_MIDDLE,%eax + adcl (%edi),%eax /* term ls long */ + movl %eax,SUM_MIDDLE + movl ACCUM_MS,%eax + adcl 4(%edi),%eax /* term ms long */ + movl %eax,SUM_MS + sbbb %al,%al + movb %al,OVERFLOWED /* Used in the next iteration */ + + subl TERM_SIZE,%edi + decl PARAM4 + jns L_accum_loop + +L_accum_done: + movl PARAM1,%edi /* accum */ + movl SUM_LS,%eax + addl %eax,(%edi) + movl SUM_MIDDLE,%eax + adcl %eax,4(%edi) + movl SUM_MS,%eax + adcl %eax,8(%edi) + + popl %ebx + popl %edi + popl %esi + leave + ret diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_add_sub.c linux/arch/i386/math-emu/reg_add_sub.c --- v1.1.76/linux/arch/i386/math-emu/reg_add_sub.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_add_sub.c Wed Dec 1 14:44:16 1993 @@ -0,0 +1,318 @@ +/*---------------------------------------------------------------------------+ + | 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 billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | 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" + + +int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w) +{ + char saved_sign = dest->sign; + int diff; + + if ( !(a->tag | b->tag) ) + { + /* Both registers are valid */ + if (!(a->sign ^ b->sign)) + { + /* signs are the same */ + dest->sign = a->sign; + if ( reg_u_add(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + return 0; + } + + /* 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) + { + dest->sign = a->sign; + if ( reg_u_sub(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + } + else if ( diff == 0 ) + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(&CONST_Z, dest); + /* sign depends upon rounding mode */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } + else + { + dest->sign = b->sign; + if ( reg_u_sub(b, a, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + } + return 0; + } + else + { + if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) + { return real_2op_NaN(a, b, dest); } + else if (a->tag == TW_Zero) + { + if (b->tag == TW_Zero) + { + char different_signs = a->sign ^ b->sign; + /* Both are zero, result will be zero. */ + reg_move(a, dest); + if (different_signs) + { + /* Signs are different. */ + /* Sign of answer depends upon rounding mode. */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } + } + else + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(b, dest); + } + return 0; + } + else if (b->tag == TW_Zero) + { +#ifdef DENORM_OPERAND + if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(a, dest); return 0; + } + else if (a->tag == TW_Infinity) + { + if (b->tag != TW_Infinity) + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(a, dest); return 0; + } + if (a->sign == b->sign) + { + /* They are both + or - infinity */ + reg_move(a, dest); return 0; + } + return arith_invalid(dest); /* Infinity-Infinity is undefined. */ + } + else if (b->tag == TW_Infinity) + { +#ifdef DENORM_OPERAND + if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(b, dest); return 0; + } + } +#ifdef PARANOID + EXCEPTION(EX_INTERNAL|0x101); +#endif + return 1; +} + + +/* Subtract b from a. (a-b) -> dest */ +int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w) +{ + char saved_sign = dest->sign; + 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) + { + /* |a| > |b| */ + dest->sign = a->sign; + if ( reg_u_sub(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + return 0; + } + else if ( diff == 0 ) + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(&CONST_Z, dest); + /* sign depends upon rounding mode */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } + else + { + dest->sign = a->sign ^ SIGN_POS^SIGN_NEG; + if ( reg_u_sub(b, a, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + } + break; + case 1: /* P - N */ + dest->sign = SIGN_POS; + if ( reg_u_add(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + break; + case 2: /* N - P */ + dest->sign = SIGN_NEG; + if ( reg_u_add(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + break; + } + return 0; + } + else + { + if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) + { return real_2op_NaN(b, a, dest); } + else if (b->tag == TW_Zero) + { + if (a->tag == TW_Zero) + { + char same_signs = !(a->sign ^ b->sign); + /* Both are zero, result will be zero. */ + reg_move(a, dest); /* Answer for different signs. */ + if (same_signs) + { + /* Sign depends upon rounding mode */ + dest->sign = ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG; + } + } + else + { +#ifdef DENORM_OPERAND + if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(a, dest); + } + return 0; + } + else if (a->tag == TW_Zero) + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(b, dest); + dest->sign ^= SIGN_POS^SIGN_NEG; + return 0; + } + 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 1; +#endif DENORM_OPERAND + reg_move(a, dest); return 0; + } + /* Both args are Infinity */ + if (a->sign == b->sign) + { + /* Infinity-Infinity is undefined. */ + return arith_invalid(dest); + } + reg_move(a, dest); + return 0; + } + else if (b->tag == TW_Infinity) + { +#ifdef DENORM_OPERAND + if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(b, dest); + dest->sign ^= SIGN_POS^SIGN_NEG; + return 0; + } + } +#ifdef PARANOID + EXCEPTION(EX_INTERNAL|0x110); +#endif + return 1; +} + diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_compare.c linux/arch/i386/math-emu/reg_compare.c --- v1.1.76/linux/arch/i386/math-emu/reg_compare.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_compare.c Thu Jun 2 10:28:27 1994 @@ -0,0 +1,378 @@ +/*---------------------------------------------------------------------------+ + | reg_compare.c | + | | + | Compare two floating point registers | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | compare() is the core FPU_REG comparison function | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "exception.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "status_w.h" + + +int compare(FPU_REG const *b) +{ + int diff; + char st0_tag; + FPU_REG *st0_ptr; + + st0_ptr = &st(0); + st0_tag = st0_ptr->tag; + + if ( st0_tag | b->tag ) + { + if ( st0_tag == TW_Zero ) + { + if ( b->tag == TW_Zero ) return COMP_A_eq_B; + if ( b->tag == TW_Valid ) + { + return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) +#ifdef DENORM_OPERAND + | ((b->exp <= EXP_UNDER) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + } + else if ( b->tag == TW_Zero ) + { + if ( st0_tag == TW_Valid ) + { + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B + : COMP_A_lt_B) +#ifdef DENORM_OPERAND + | ((st0_ptr->exp <= EXP_UNDER ) + ? COMP_Denormal : 0 ) +#endif DENORM_OPERAND + ; + } + } + + if ( st0_tag == TW_Infinity ) + { + if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) ) + { + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B + : COMP_A_lt_B) +#ifdef DENORM_OPERAND + | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0 ) +#endif DENORM_OPERAND +; + } + else if ( b->tag == TW_Infinity ) + { + /* The 80486 book says that infinities can be equal! */ + return (st0_ptr->sign == b->sign) ? COMP_A_eq_B : + ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); + } + /* Fall through to the NaN code */ + } + else if ( b->tag == TW_Infinity ) + { + if ( (st0_tag == TW_Valid) || (st0_tag == TW_Zero) ) + { + return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) +#ifdef DENORM_OPERAND + | (((st0_tag == TW_Valid) + && (st0_ptr->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + /* Fall through to the NaN code */ + } + + /* The only possibility now should be that one of the arguments + is a NaN */ + if ( (st0_tag == TW_NaN) || (b->tag == TW_NaN) ) + { + if ( ((st0_tag == TW_NaN) && !(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 (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); + if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); +#endif PARANOID + + + if (st0_ptr->sign != b->sign) + { + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) +#ifdef DENORM_OPERAND + | + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + + diff = st0_ptr->exp - b->exp; + if ( diff == 0 ) + { + diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are + identical */ + if ( diff == 0 ) + { + diff = st0_ptr->sigl > b->sigl; + if ( diff == 0 ) + diff = -(st0_ptr->sigl < b->sigl); + } + } + + if ( diff > 0 ) + { + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) +#ifdef DENORM_OPERAND + | + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + if ( diff < 0 ) + { + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) +#ifdef DENORM_OPERAND + | + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + } + + return COMP_A_eq_B +#ifdef DENORM_OPERAND + | + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + COMP_Denormal : 0) +#endif DENORM_OPERAND + ; + +} + + +/* This function requires that st(0) is not empty */ +int compare_st_data(FPU_REG const *loaded_data) +{ + int f, c; + + c = compare(loaded_data); + + if (c & COMP_NaN) + { + EXCEPTION(EX_Invalid); + f = SW_C3 | SW_C2 | SW_C0; + } + else + switch (c & 7) + { + 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); + if (c & COMP_Denormal) + { + return denormal_operand(); + } + return 0; +} + + +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) + { + setcc(SW_C3 | SW_C2 | SW_C0); + EXCEPTION(EX_Invalid); + return !(control_word & CW_Invalid); + } + else + switch (c & 7) + { + 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); + if (c & COMP_Denormal) + { + return denormal_operand(); + } + return 0; +} + + +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) + { + 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 0; + } + else + switch (c & 7) + { + 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); + if (c & COMP_Denormal) + { + return denormal_operand(); + } + return 0; +} + +/*---------------------------------------------------------------------------*/ + +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) + { + FPU_illegal(); + return; + } + if ( !compare_st_st(1) ) + poppop(); +} + + +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) ) + poppop(); + } + else + FPU_illegal(); +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_constant.c linux/arch/i386/math-emu/reg_constant.c --- v1.1.76/linux/arch/i386/math-emu/reg_constant.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_constant.c Thu Jun 2 10:28:27 1994 @@ -0,0 +1,116 @@ +/*---------------------------------------------------------------------------+ + | reg_constant.c | + | | + | All of the constant FPU_REGs | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_system.h" +#include "fpu_emu.h" +#include "status_w.h" +#include "reg_constant.h" + + +FPU_REG const CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS, + 0x00000000, 0x80000000 }; +FPU_REG const CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1, + 0x00000000, 0x80000000 }; +FPU_REG const CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1, + 0x00000000, 0x80000000 }; +FPU_REG const CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1, + 0xcd1b8afe, 0xd49a784b }; +FPU_REG const CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS, + 0x5c17f0bc, 0xb8aa3b29 }; +FPU_REG const CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1, + 0x2168c235, 0xc90fdaa2 }; +FPU_REG const CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS, + 0x2168c235, 0xc90fdaa2 }; +FPU_REG const CONST_PI4 = { SIGN_POS, TW_Valid, EXP_BIAS-1, + 0x2168c235, 0xc90fdaa2 }; +FPU_REG const CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2, + 0xfbcff799, 0x9a209a84 }; +FPU_REG const CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1, + 0xd1cf79ac, 0xb17217f7 }; + +/* Extra bits to take pi/2 to more than 128 bits precision. */ +FPU_REG const CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66, + 0xfc8f8cbb, 0xece675d1 }; + +/* Only the sign (and tag) is used in internal zeroes */ +FPU_REG const CONST_Z = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 }; + +/* Only the sign and significand (and tag) are used in internal NaNs */ +/* The 80486 never generates one of these +FPU_REG const CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 }; + */ +/* This is the real indefinite QNaN */ +FPU_REG const CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 }; + +/* Only the sign (and tag) is used in internal infinities */ +FPU_REG const CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 }; + + + +static void fld_const(FPU_REG const *c) +{ + FPU_REG *st_new_ptr; + + if ( STACK_OVERFLOW ) + { + stack_overflow(); + return; + } + push(); + reg_move(c, st_new_ptr); + clear_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, FPU_illegal +}; + +void fconst(void) +{ + (constants_table[FPU_rm])(); +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_constant.h linux/arch/i386/math-emu/reg_constant.h --- v1.1.76/linux/arch/i386/math-emu/reg_constant.h Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_constant.h Wed Dec 1 14:44:16 1993 @@ -0,0 +1,31 @@ +/*---------------------------------------------------------------------------+ + | reg_constant.h | + | | + | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _REG_CONSTANT_H_ +#define _REG_CONSTANT_H_ + +#include "fpu_emu.h" + +extern FPU_REG const CONST_1; +extern FPU_REG const CONST_2; +extern FPU_REG const CONST_HALF; +extern FPU_REG const CONST_L2T; +extern FPU_REG const CONST_L2E; +extern FPU_REG const CONST_PI; +extern FPU_REG const CONST_PI2; +extern FPU_REG const CONST_PI2extra; +extern FPU_REG const CONST_PI4; +extern FPU_REG const CONST_LG2; +extern FPU_REG const CONST_LN2; +extern FPU_REG const CONST_Z; +extern FPU_REG const CONST_PINF; +extern FPU_REG const CONST_INF; +extern FPU_REG const CONST_MINF; +extern FPU_REG const CONST_QNaN; + +#endif _REG_CONSTANT_H_ diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_div.S linux/arch/i386/math-emu/reg_div.S --- v1.1.76/linux/arch/i386/math-emu/reg_div.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_div.S Thu Jun 2 10:28:27 1994 @@ -0,0 +1,251 @@ + .file "reg_div.S" +/*---------------------------------------------------------------------------+ + | reg_div.S | + | | + | Divide one FPU_REG by another and put the result in a destination FPU_REG.| + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, | + | unsigned int control_word) | + | | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "fpu_asm.h" + + +.text + .align 2 + +.globl _reg_div +_reg_div: + pushl %ebp + movl %esp,%ebp +#ifndef NON_REENTRANT_FPU + subl $28,%esp /* Needed by divide_kernel */ +#endif NON_REENTRANT_FPU + + pushl %esi + pushl %edi + pushl %ebx + + movl PARAM1,%esi + movl PARAM2,%ebx + movl PARAM3,%edi + + movb TAG(%esi),%al + orb TAG(%ebx),%al + + jne L_div_special /* Not (both numbers TW_Valid) */ + +#ifdef DENORM_OPERAND +/* Check for denormals */ + cmpl EXP_UNDER,EXP(%esi) + jg xL_arg1_not_denormal + + call _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 %esi + pushl %ebx /* Ordering is important here */ + 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) + +LDiv_set_result_sign: + movb SIGN(%esi),%cl + cmpb %cl,SIGN(%ebx) + jne LDiv_negative_result + + movb SIGN_POS,SIGN(%edi) + xorl %eax,%eax /* Valid result */ + jmp LDiv_exit + +LDiv_negative_result: + movb SIGN_NEG,SIGN(%edi) + xorl %eax,%eax /* Valid result */ + +LDiv_exit: +#ifndef NON_REENTRANT_FPU + leal -40(%ebp),%esp +#else + leal -12(%ebp),%esp +#endif NON_REENTRANT_FPU + + popl %ebx + popl %edi + popl %esi + leave + ret + + +L_return_zero: + xorl %eax,%eax + movl %eax,SIGH(%edi) + movl %eax,SIGL(%edi) + movl EXP_UNDER,EXP(%edi) + movb TW_Zero,TAG(%edi) + jmp LDiv_set_result_sign + +#ifdef PARANOID +L_unknown_tags: + pushl EX_INTERNAL | 0x208 + call EXCEPTION + + /* Generate a NaN for unknown tags */ + movl _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 /* %eax is nz */ +#endif PARANOID diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_ld_str.c linux/arch/i386/math-emu/reg_ld_str.c --- v1.1.76/linux/arch/i386/math-emu/reg_ld_str.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_ld_str.c Fri Aug 19 08:54:01 1994 @@ -0,0 +1,1438 @@ +/*---------------------------------------------------------------------------+ + | reg_ld_str.c | + | | + | All of the functions which transfer data between user memory and FPU_REGs.| + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | 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 + +#include "fpu_system.h" +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" +#include "status_w.h" + + +#define EXTENDED_Ebias 0x3fff +#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */ + +#define DOUBLE_Emax 1023 /* largest valid exponent */ +#define DOUBLE_Ebias 1023 +#define DOUBLE_Emin (-1022) /* smallest valid exponent */ + +#define SINGLE_Emax 127 /* largest valid exponent */ +#define SINGLE_Ebias 127 +#define SINGLE_Emin (-126) /* smallest valid exponent */ + +static void write_to_extended(FPU_REG *rp, char *d); + + +/* Get a long double from user memory */ +int reg_load_extended(long double *s, FPU_REG *loaded_data) +{ + unsigned long sigl, sigh, exp; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, s, 10); + sigl = get_fs_long((unsigned long *) s); + sigh = get_fs_long(1 + (unsigned long *) s); + exp = get_fs_word(4 + (unsigned short *) s); + RE_ENTRANT_CHECK_ON; + + loaded_data->tag = TW_Valid; /* Default */ + loaded_data->sigl = sigl; + loaded_data->sigh = sigh; + if (exp & 0x8000) + loaded_data->sign = SIGN_NEG; + else + loaded_data->sign = SIGN_POS; + exp &= 0x7fff; + loaded_data->exp = exp - EXTENDED_Ebias + EXP_BIAS; + + if ( exp == 0 ) + { + if ( !(sigh | sigl) ) + { + loaded_data->tag = TW_Zero; + return 0; + } + /* The number is a de-normal or pseudodenormal. */ + if (sigh & 0x80000000) + { + /* Is a pseudodenormal. */ + /* Convert it for internal use. */ + /* This is non-80486 behaviour because the number + loses its 'denormal' identity. */ + loaded_data->exp++; + return 1; + } + else + { + /* Is a denormal. */ + /* Convert it for internal use. */ + loaded_data->exp++; + normalize_nuo(loaded_data); + return 0; + } + } + else if ( exp == 0x7fff ) + { + if ( !((sigh ^ 0x80000000) | sigl) ) + { + /* Matches the bit pattern for Infinity. */ + loaded_data->exp = EXP_Infinity; + loaded_data->tag = TW_Infinity; + return 0; + } + + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; + if ( !(sigh & 0x80000000) ) + { + /* NaNs have the ms bit set to 1. */ + /* This is therefore an Unsupported NaN data type. */ + /* This is non 80486 behaviour */ + /* This should generate an Invalid Operand exception + later, so we convert it to a SNaN */ + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000001; + loaded_data->sign = SIGN_NEG; + return 1; + } + return 0; + } + + if ( !(sigh & 0x80000000) ) + { + /* Unsupported data type. */ + /* Valid numbers have the ms bit set to 1. */ + /* Unnormal. */ + /* Convert it for internal use. */ + /* This is non-80486 behaviour */ + /* This should generate an Invalid Operand exception + later, so we convert it to a SNaN */ + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000001; + loaded_data->sign = SIGN_NEG; + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; + return 1; + } + return 0; +} + + +/* Get a double from user memory */ +int reg_load_double(double *dfloat, FPU_REG *loaded_data) +{ + int exp; + unsigned m64, l64; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, dfloat, 8); + m64 = get_fs_long(1 + (unsigned long *) dfloat); + l64 = get_fs_long((unsigned long *) dfloat); + RE_ENTRANT_CHECK_ON; + + if (m64 & 0x80000000) + loaded_data->sign = SIGN_NEG; + else + 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 */ + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000000; + loaded_data->exp = EXP_Infinity; + loaded_data->tag = TW_Infinity; + return 0; + } + else + { + /* Must be a signaling or quiet NaN */ + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; + loaded_data->sigh = (m64 << 11) | 0x80000000; + loaded_data->sigh |= l64 >> 21; + loaded_data->sigl = l64 << 11; + return 0; /* The calling function must look for NaNs */ + } + } + else if ( exp < DOUBLE_Emin ) + { + /* Zero or de-normal */ + if ((m64 == 0) && (l64 == 0)) + { + /* Zero */ + int c = loaded_data->sign; + reg_move(&CONST_Z, loaded_data); + loaded_data->sign = c; + return 0; + } + else + { + /* De-normal */ + loaded_data->exp = DOUBLE_Emin + EXP_BIAS; + loaded_data->tag = TW_Valid; + loaded_data->sigh = m64 << 11; + loaded_data->sigh |= l64 >> 21; + loaded_data->sigl = l64 << 11; + normalize_nuo(loaded_data); + return denormal_operand(); + } + } + else + { + loaded_data->exp = exp + EXP_BIAS; + loaded_data->tag = TW_Valid; + loaded_data->sigh = (m64 << 11) | 0x80000000; + loaded_data->sigh |= l64 >> 21; + loaded_data->sigl = l64 << 11; + + return 0; + } +} + + +/* Get a float from user memory */ +int reg_load_single(float *single, FPU_REG *loaded_data) +{ + unsigned m32; + int exp; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, single, 4); + m32 = get_fs_long((unsigned long *) single); + RE_ENTRANT_CHECK_ON; + + if (m32 & 0x80000000) + loaded_data->sign = SIGN_NEG; + else + loaded_data->sign = SIGN_POS; + if (!(m32 & 0x7fffffff)) + { + /* Zero */ + int c = loaded_data->sign; + reg_move(&CONST_Z, loaded_data); + loaded_data->sign = c; + return 0; + } + exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias; + m32 = (m32 & 0x7fffff) << 8; + if ( exp < SINGLE_Emin ) + { + /* De-normals */ + loaded_data->exp = SINGLE_Emin + EXP_BIAS; + loaded_data->tag = TW_Valid; + loaded_data->sigh = m32; + loaded_data->sigl = 0; + normalize_nuo(loaded_data); + return denormal_operand(); + } + else if ( exp > SINGLE_Emax ) + { + /* Infinity or NaN */ + if ( m32 == 0 ) + { + /* +- infinity */ + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000000; + loaded_data->exp = EXP_Infinity; + loaded_data->tag = TW_Infinity; + return 0; + } + else + { + /* Must be a signaling or quiet NaN */ + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; + loaded_data->sigh = m32 | 0x80000000; + loaded_data->sigl = 0; + return 0; /* The calling function must look for NaNs */ + } + } + else + { + loaded_data->exp = exp + EXP_BIAS; + loaded_data->sigh = m32 | 0x80000000; + loaded_data->sigl = 0; + loaded_data->tag = TW_Valid; + return 0; + } +} + + +/* Get a long long from user memory */ +void reg_load_int64(long long *_s, FPU_REG *loaded_data) +{ + int e; + long long s; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, _s, 8); + ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s); + ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s); + RE_ENTRANT_CHECK_ON; + + if (s == 0) + { reg_move(&CONST_Z, loaded_data); return; } + + if (s > 0) + loaded_data->sign = SIGN_POS; + else + { + s = -s; + loaded_data->sign = SIGN_NEG; + } + + e = EXP_BIAS + 63; + significand(loaded_data) = s; + loaded_data->exp = e; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); +} + + +/* Get a long from user memory */ +void reg_load_int32(long *_s, FPU_REG *loaded_data) +{ + long s; + int e; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, _s, 4); + s = (long)get_fs_long((unsigned long *) _s); + RE_ENTRANT_CHECK_ON; + + if (s == 0) + { reg_move(&CONST_Z, loaded_data); return; } + + if (s > 0) + loaded_data->sign = SIGN_POS; + else + { + s = -s; + loaded_data->sign = SIGN_NEG; + } + + e = EXP_BIAS + 31; + loaded_data->sigh = s; + loaded_data->sigl = 0; + loaded_data->exp = e; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); +} + + +/* Get a short from user memory */ +void reg_load_int16(short *_s, FPU_REG *loaded_data) +{ + int s, e; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, _s, 2); + /* Cast as short to get the sign extended. */ + s = (short)get_fs_word((unsigned short *) _s); + RE_ENTRANT_CHECK_ON; + + if (s == 0) + { reg_move(&CONST_Z, loaded_data); return; } + + if (s > 0) + loaded_data->sign = SIGN_POS; + else + { + s = -s; + loaded_data->sign = SIGN_NEG; + } + + e = EXP_BIAS + 15; + loaded_data->sigh = s << 16; + + loaded_data->sigl = 0; + loaded_data->exp = e; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); +} + + +/* Get a packed bcd array from user memory */ +void reg_load_bcd(char *s, FPU_REG *loaded_data) +{ + int pos; + unsigned char bcd; + long long l=0; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, s, 10); + RE_ENTRANT_CHECK_ON; + for ( pos = 8; pos >= 0; pos--) + { + l *= 10; + RE_ENTRANT_CHECK_OFF; + bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos); + RE_ENTRANT_CHECK_ON; + l += bcd >> 4; + l *= 10; + l += bcd & 0x0f; + } + + RE_ENTRANT_CHECK_OFF; + loaded_data->sign = + ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ? + SIGN_NEG : SIGN_POS; + RE_ENTRANT_CHECK_ON; + + if (l == 0) + { + char sign = loaded_data->sign; + reg_move(&CONST_Z, loaded_data); + loaded_data->sign = sign; + } + else + { + significand(loaded_data) = l; + loaded_data->exp = EXP_BIAS + 63; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); + } +} + +/*===========================================================================*/ + +/* Put a long double into user memory */ +int reg_store_extended(long double *d, FPU_REG *st0_ptr) +{ + /* + The only exception raised by an attempt to store to an + extended format is the Invalid Stack exception, i.e. + attempting to store from an empty register. + */ + + if ( st0_ptr->tag != TW_Empty ) + { + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE, d, 10); + RE_ENTRANT_CHECK_ON; + write_to_extended(st0_ptr, (char *) d); + return 1; + } + + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + if ( control_word & CW_Invalid ) + { + /* The masked response */ + /* Put out the QNaN indefinite */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,10); + put_fs_long(0, (unsigned long *) d); + put_fs_long(0xc0000000, 1 + (unsigned long *) d); + put_fs_word(0xffff, 4 + (short *) d); + RE_ENTRANT_CHECK_ON; + return 1; + } + else + return 0; + +} + + +/* Put a double into user memory */ +int reg_store_double(double *dfloat, FPU_REG *st0_ptr) +{ + unsigned long l[2]; + unsigned long increment = 0; /* avoid gcc warnings */ + char st0_tag = st0_ptr->tag; + + if (st0_tag == TW_Valid) + { + int exp; + FPU_REG tmp; + + reg_move(st0_ptr, &tmp); + exp = tmp.exp - EXP_BIAS; + + if ( exp < DOUBLE_Emin ) /* It may be a denormal */ + { + int precision_loss; + + /* A denormal will always underflow. */ +#ifndef PECULIAR_486 + /* An 80486 is supposed to be able to generate + a denormal exception here, but... */ + if ( st0_ptr->exp <= EXP_UNDER ) + { + /* Underflow has priority. */ + if ( control_word & CW_Underflow ) + denormal_operand(); + } +#endif PECULIAR_486 + + 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) && + (st0_ptr->sigl & 0x000007ff)) ) +#endif PECULIAR_486 + { + EXCEPTION(EX_Underflow); + /* This is a special case: see sec 16.2.5.1 of + the 80486 book */ + if ( !(control_word & CW_Underflow) ) + return 0; + } + EXCEPTION(precision_loss); + if ( !(control_word & CW_Precision) ) + return 0; + } + l[0] = tmp.sigl; + l[1] = tmp.sigh; + } + else + { + if ( tmp.sigl & 0x000007ff ) + { + 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); + if ( !(control_word & CW_Overflow) ) + return 0; + set_precision_flag_up(); + if ( !(control_word & CW_Precision) ) + return 0; + + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + /* Overflow to infinity */ + l[0] = 0x00000000; /* Set to */ + l[1] = 0x7ff00000; /* + INF */ + } + else + { + /* Add the exponent */ + l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20); + } + } + } + else if (st0_tag == TW_Zero) + { + /* Number is zero */ + l[0] = 0; + l[1] = 0; + } + else if (st0_tag == TW_Infinity) + { + l[0] = 0; + l[1] = 0x7ff00000; + } + else if (st0_tag == TW_NaN) + { + /* See if we can get a valid NaN from the FPU_REG */ + l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21); + l[1] = ((st0_ptr->sigh >> 11) & 0xfffff); + if ( !(st0_ptr->sigh & 0x40000000) ) + { + /* It is a signalling NaN */ + EXCEPTION(EX_Invalid); + if ( !(control_word & CW_Invalid) ) + return 0; + l[1] |= (0x40000000 >> 11); + } + l[1] |= 0x7ff00000; + } + else if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + if ( control_word & CW_Invalid ) + { + /* The masked response */ + /* Put out the QNaN indefinite */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8); + put_fs_long(0, (unsigned long *) dfloat); + put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat); + RE_ENTRANT_CHECK_ON; + return 1; + } + else + return 0; + } + if ( st0_ptr->sign ) + l[1] |= 0x80000000; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8); + put_fs_long(l[0], (unsigned long *)dfloat); + put_fs_long(l[1], 1 + (unsigned long *)dfloat); + RE_ENTRANT_CHECK_ON; + + return 1; +} + + +/* Put a float into user memory */ +int reg_store_single(float *single, FPU_REG *st0_ptr) +{ + long templ; + unsigned long increment = 0; /* avoid gcc warnings */ + char st0_tag = st0_ptr->tag; + + if (st0_tag == TW_Valid) + { + int exp; + FPU_REG tmp; + + reg_move(st0_ptr, &tmp); + exp = tmp.exp - EXP_BIAS; + + if ( exp < SINGLE_Emin ) + { + int precision_loss; + + /* A denormal will always underflow. */ +#ifndef PECULIAR_486 + /* An 80486 is supposed to be able to generate + a denormal exception here, but... */ + if ( st0_ptr->exp <= EXP_UNDER ) + { + /* Underflow has priority. */ + if ( control_word & CW_Underflow ) + denormal_operand(); + } +#endif PECULIAR_486 + + 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) && + ((st0_ptr->sigh & 0x000000ff) || st0_ptr->sigl)) ) +#endif PECULIAR_486 + { + EXCEPTION(EX_Underflow); + /* This is a special case: see sec 16.2.5.1 of + the 80486 book */ + if ( !(control_word & EX_Underflow) ) + return 0; + } + EXCEPTION(precision_loss); + if ( !(control_word & EX_Precision) ) + return 0; + } + templ = tmp.sigl; + } + else + { + if ( tmp.sigl | (tmp.sigh & 0x000000ff) ) + { + 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); + if ( !(control_word & CW_Overflow) ) + return 0; + set_precision_flag_up(); + if ( !(control_word & CW_Precision) ) + return 0; + + /* This is a special case: see sec 16.2.5.1 of the 80486 book. */ + /* Masked response is overflow to infinity. */ + templ = 0x7f800000; + } + else + templ |= ((exp+SINGLE_Ebias) & 0xff) << 23; + } + } + else if (st0_tag == TW_Zero) + { + templ = 0; + } + else if (st0_tag == TW_Infinity) + { + templ = 0x7f800000; + } + else if (st0_tag == TW_NaN) + { + /* See if we can get a valid NaN from the FPU_REG */ + templ = st0_ptr->sigh >> 8; + if ( !(st0_ptr->sigh & 0x40000000) ) + { + /* It is a signalling NaN */ + EXCEPTION(EX_Invalid); + if ( !(control_word & CW_Invalid) ) + return 0; + templ |= (0x40000000 >> 8); + } + templ |= 0x7f800000; + } + else if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + if ( control_word & EX_Invalid ) + { + /* The masked response */ + /* Put out the QNaN indefinite */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,(void *)single,4); + put_fs_long(0xffc00000, (unsigned long *) single); + RE_ENTRANT_CHECK_ON; + return 1; + } + else + return 0; + } +#ifdef PARANOID + else + { + EXCEPTION(EX_INTERNAL|0x163); + return 0; + } +#endif + if (st0_ptr->sign) + templ |= 0x80000000; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,(void *)single,4); + put_fs_long(templ,(unsigned long *) single); + RE_ENTRANT_CHECK_ON; + + return 1; +} + + +/* Put a long long into user memory */ +int reg_store_int64(long long *d, FPU_REG *st0_ptr) +{ + FPU_REG t; + long long tll; + int precision_loss; + char st0_tag = st0_ptr->tag; + + if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + goto invalid_operand; + } + else if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; + } + + reg_move(st0_ptr, &t); + precision_loss = round_to_int(&t); + ((long *)&tll)[0] = t.sigl; + ((long *)&tll)[1] = t.sigh; + if ( (precision_loss == 1) || + ((t.sigh & 0x80000000) && + !((t.sigh == 0x80000000) && (t.sigl == 0) && + (t.sign == SIGN_NEG))) ) + { + EXCEPTION(EX_Invalid); + /* This is a special case: see sec 16.2.5.1 of the 80486 book */ + invalid_operand: + if ( control_word & EX_Invalid ) + { + /* Produce something like QNaN "indefinite" */ + tll = 0x8000000000000000LL; + } + else + return 0; + } + else + { + if ( precision_loss ) + set_precision_flag(precision_loss); + if ( t.sign ) + tll = - tll; + } + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,(void *)d,8); + put_fs_long(((long *)&tll)[0],(unsigned long *) d); + put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d); + RE_ENTRANT_CHECK_ON; + + return 1; +} + + +/* Put a long into user memory */ +int reg_store_int32(long *d, FPU_REG *st0_ptr) +{ + FPU_REG t; + int precision_loss; + char st0_tag = st0_ptr->tag; + + if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + goto invalid_operand; + } + else if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; + } + + reg_move(st0_ptr, &t); + precision_loss = round_to_int(&t); + 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 */ + invalid_operand: + if ( control_word & EX_Invalid ) + { + /* Produce something like QNaN "indefinite" */ + t.sigl = 0x80000000; + } + else + return 0; + } + else + { + if ( precision_loss ) + set_precision_flag(precision_loss); + if ( t.sign ) + t.sigl = -(long)t.sigl; + } + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,4); + put_fs_long(t.sigl, (unsigned long *) d); + RE_ENTRANT_CHECK_ON; + + return 1; +} + + +/* Put a short into user memory */ +int reg_store_int16(short *d, FPU_REG *st0_ptr) +{ + FPU_REG t; + int precision_loss; + char st0_tag = st0_ptr->tag; + + if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + goto invalid_operand; + } + else if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; + } + + reg_move(st0_ptr, &t); + precision_loss = round_to_int(&t); + 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 */ + invalid_operand: + if ( control_word & EX_Invalid ) + { + /* Produce something like QNaN "indefinite" */ + t.sigl = 0x8000; + } + else + return 0; + } + else + { + if ( precision_loss ) + set_precision_flag(precision_loss); + if ( t.sign ) + t.sigl = -t.sigl; + } + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,2); + put_fs_word((short)t.sigl,(short *) d); + RE_ENTRANT_CHECK_ON; + + return 1; +} + + +/* Put a packed bcd array into user memory */ +int reg_store_bcd(char *d, FPU_REG *st0_ptr) +{ + FPU_REG t; + unsigned long long ll; + unsigned char b; + int i, precision_loss; + unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0; + char st0_tag = st0_ptr->tag; + + if ( st0_tag == TW_Empty ) + { + /* Empty register (stack underflow) */ + EXCEPTION(EX_StackUnder); + goto invalid_operand; + } + + reg_move(st0_ptr, &t); + precision_loss = round_to_int(&t); + ll = significand(&t); + + /* 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 */ + invalid_operand: + if ( control_word & CW_Invalid ) + { + /* Produce the QNaN "indefinite" */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,10); + for ( i = 0; i < 7; i++) + put_fs_byte(0, (unsigned char *) d+i); /* These bytes "undefined" */ + put_fs_byte(0xc0, (unsigned char *) d+7); /* This byte "undefined" */ + put_fs_byte(0xff, (unsigned char *) d+8); + put_fs_byte(0xff, (unsigned char *) d+9); + RE_ENTRANT_CHECK_ON; + return 1; + } + else + return 0; + } + else if ( precision_loss ) + { + /* Precision loss doesn't stop the data transfer */ + set_precision_flag(precision_loss); + } + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,10); + RE_ENTRANT_CHECK_ON; + for ( i = 0; i < 9; i++) + { + b = div_small(&ll, 10); + b |= (div_small(&ll, 10)) << 4; + RE_ENTRANT_CHECK_OFF; + put_fs_byte(b,(unsigned char *) d+i); + RE_ENTRANT_CHECK_ON; + } + RE_ENTRANT_CHECK_OFF; + put_fs_byte(sign,(unsigned char *) d+9); + RE_ENTRANT_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 + largest possible value */ +int round_to_int(FPU_REG *r) +{ + char very_big; + unsigned eax; + + if (r->tag == TW_Zero) + { + /* Make sure that zero is returned */ + significand(r) = 0; + return 0; /* o.k. */ + } + + if (r->exp > EXP_BIAS + 63) + { + r->sigl = r->sigh = ~0; /* The largest representable number */ + return 1; /* overflow */ + } + + eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp); + very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */ +#define half_or_more (eax & 0x80000000) +#define frac_part (eax) +#define more_than_half ((eax & 0x80000001) == 0x80000001) + switch (control_word & CW_RC) + { + case RC_RND: + if ( more_than_half /* nearest */ + || (half_or_more && (r->sigl & 1)) ) /* odd -> even */ + { + if ( very_big ) return 1; /* overflow */ + significand(r) ++; + return PRECISION_LOST_UP; + } + break; + case RC_DOWN: + if (frac_part && r->sign) + { + if ( very_big ) return 1; /* overflow */ + significand(r) ++; + return PRECISION_LOST_UP; + } + break; + case RC_UP: + if (frac_part && !r->sign) + { + if ( very_big ) return 1; /* overflow */ + significand(r) ++; + return PRECISION_LOST_UP; + } + break; + case RC_CHOP: + break; + } + + return eax ? PRECISION_LOST_DOWN : 0; + +} + +/*===========================================================================*/ + +char *fldenv(fpu_addr_modes addr_modes, char *s) +{ + unsigned short tag_word = 0; + unsigned char tag; + int i; + + if ( (addr_modes.default_mode == VM86) || + ((addr_modes.default_mode == PM16) + ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) ) + { + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, s, 0x0e); + control_word = get_fs_word((unsigned short *) s); + partial_status = get_fs_word((unsigned short *) (s+2)); + tag_word = get_fs_word((unsigned short *) (s+4)); + instruction_address.offset = get_fs_word((unsigned short *) (s+6)); + instruction_address.selector = get_fs_word((unsigned short *) (s+8)); + operand_address.offset = get_fs_word((unsigned short *) (s+0x0a)); + operand_address.selector = get_fs_word((unsigned short *) (s+0x0c)); + RE_ENTRANT_CHECK_ON; + s += 0x0e; + if ( addr_modes.default_mode == VM86 ) + { + instruction_address.offset + += (instruction_address.selector & 0xf000) << 4; + operand_address.offset += (operand_address.selector & 0xf000) << 4; + } + } + else + { + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, s, 0x1c); + control_word = get_fs_word((unsigned short *) s); + partial_status = get_fs_word((unsigned short *) (s+4)); + tag_word = get_fs_word((unsigned short *) (s+8)); + instruction_address.offset = get_fs_long((unsigned long *) (s+0x0c)); + instruction_address.selector = get_fs_word((unsigned short *) (s+0x10)); + instruction_address.opcode = get_fs_word((unsigned short *) (s+0x12)); + operand_address.offset = get_fs_long((unsigned long *) (s+0x14)); + operand_address.selector = get_fs_long((unsigned long *) (s+0x18)); + RE_ENTRANT_CHECK_ON; + s += 0x1c; + } + +#ifdef PECULIAR_486 + control_word &= ~0xe080; +#endif PECULIAR_486 + + top = (partial_status >> SW_Top_Shift) & 7; + + if ( partial_status & ~control_word & CW_Exceptions ) + partial_status |= (SW_Summary | SW_Backward); + else + partial_status &= ~(SW_Summary | SW_Backward); + + for ( i = 0; i < 8; i++ ) + { + tag = tag_word & 3; + tag_word >>= 2; + + if ( tag == 3 ) + /* New tag is empty. Accept it */ + regs[i].tag = TW_Empty; + else if ( regs[i].tag == TW_Empty ) + { + /* Old tag is empty and new tag is not empty. New tag is determined + by old reg contents */ + if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias ) + { + if ( !(regs[i].sigl | regs[i].sigh) ) + regs[i].tag = TW_Zero; + else + regs[i].tag = TW_Valid; + } + else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias ) + { + if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) ) + regs[i].tag = TW_Infinity; + else + regs[i].tag = TW_NaN; + } + else + regs[i].tag = TW_Valid; + } + /* Else old tag is not empty and new tag is not empty. Old tag + remains correct */ + } + + return s; +} + + +void frstor(fpu_addr_modes addr_modes, char *data_address) +{ + int i, stnr; + unsigned char tag; + char *s = fldenv(addr_modes, data_address); + + for ( i = 0; i < 8; i++ ) + { + /* Load each register. */ + stnr = (i+top) & 7; + tag = regs[stnr].tag; /* Derived from the fldenv() loaded tag word. */ + reg_load_extended((long double *)(s+i*10), ®s[stnr]); + if ( tag == TW_Empty ) /* The loaded data over-rides all other cases. */ + regs[stnr].tag = tag; + } + +} + + +unsigned short tag_word(void) +{ + unsigned short word = 0; + unsigned char tag; + int i; + + for ( i = 7; i >= 0; i-- ) + { + switch ( tag = regs[i].tag ) + { + case TW_Valid: + if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) ) + tag = 2; + break; + case TW_Infinity: + case TW_NaN: + tag = 2; + break; + case TW_Empty: + tag = 3; + break; + /* TW_Zero already has the correct value */ + } + word <<= 2; + word |= tag; + } + return word; +} + + +char *fstenv(fpu_addr_modes addr_modes, char *d) +{ + if ( (addr_modes.default_mode == VM86) || + ((addr_modes.default_mode == PM16) + ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) ) + { + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,14); +#ifdef PECULIAR_486 + put_fs_long(control_word & ~0xe080, (unsigned short *) d); +#else + put_fs_word(control_word, (unsigned short *) d); +#endif PECULIAR_486 + put_fs_word(status_word(), (unsigned short *) (d+2)); + put_fs_word(tag_word(), (unsigned short *) (d+4)); + put_fs_word(instruction_address.offset, (unsigned short *) (d+6)); + put_fs_word(operand_address.offset, (unsigned short *) (d+0x0a)); + if ( addr_modes.default_mode == VM86 ) + { + put_fs_word((instruction_address.offset & 0xf0000) >> 4, + (unsigned short *) (d+8)); + put_fs_word((operand_address.offset & 0xf0000) >> 4, + (unsigned short *) (d+0x0c)); + } + else + { + put_fs_word(instruction_address.selector, (unsigned short *) (d+8)); + put_fs_word(operand_address.selector, (unsigned short *) (d+0x0c)); + } + RE_ENTRANT_CHECK_ON; + d += 0x0e; + } + else + { + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,28); +#ifdef PECULIAR_486 + /* An 80486 sets all the reserved bits to 1. */ + put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d); + put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4)); + put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8)); +#else + put_fs_word(control_word, (unsigned short *) d); + put_fs_word(status_word(), (unsigned short *) (d+4)); + put_fs_word(tag_word(), (unsigned short *) (d+8)); +#endif PECULIAR_486 + put_fs_long(instruction_address.offset, (unsigned long *) (d+0x0c)); + put_fs_word(instruction_address.selector, (unsigned short *) (d+0x10)); + put_fs_word(instruction_address.opcode, (unsigned short *) (d+0x12)); + put_fs_long(operand_address.offset, (unsigned long *) (d+0x14)); +#ifdef PECULIAR_486 + /* An 80486 sets all the reserved bits to 1. */ + put_fs_word(operand_address.selector, (unsigned short *) (d+0x18)); + put_fs_word(0xffff, (unsigned short *) (d+0x1a)); +#else + put_fs_long(operand_address.selector, (unsigned long *) (d+0x18)); +#endif PECULIAR_486 + RE_ENTRANT_CHECK_ON; + d += 0x1c; + } + + control_word |= CW_Exceptions; + partial_status &= ~(SW_Summary | SW_Backward); + + return d; +} + + +void fsave(fpu_addr_modes addr_modes, char *data_address) +{ + char *d; + int i; + + d = fstenv(addr_modes, data_address); + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE,d,80); + RE_ENTRANT_CHECK_ON; + for ( i = 0; i < 8; i++ ) + write_to_extended(®s[(top + i) & 7], d + 10 * i); + + finit(); + +} + +/*===========================================================================*/ + +/* + A call to this function must be preceded by a call to + FPU_verify_area() to verify access to the 10 bytes at d + */ +static void write_to_extended(FPU_REG *rp, char *d) +{ + long e; + FPU_REG tmp; + + e = rp->exp - EXP_BIAS + EXTENDED_Ebias; + +#ifdef PARANOID + switch ( rp->tag ) + { + case TW_Zero: + if ( rp->sigh | rp->sigl | e ) + EXCEPTION(EX_INTERNAL | 0x160); + break; + case TW_Infinity: + case TW_NaN: + if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) ) + EXCEPTION(EX_INTERNAL | 0x161); + break; + default: + if (e > 0x7fff || e < -63) + EXCEPTION(EX_INTERNAL | 0x162); + } +#endif PARANOID + + /* + All numbers except denormals are stored internally in a + format which is compatible with the extended real number + format. + */ + if ( e > 0 ) + { + /* just copy the reg */ + RE_ENTRANT_CHECK_OFF; + put_fs_long(rp->sigl, (unsigned long *) d); + put_fs_long(rp->sigh, (unsigned long *) (d + 4)); + RE_ENTRANT_CHECK_ON; + } + else + { + /* + The number is a de-normal stored as a normal using our + extra exponent range, or is Zero. + Convert it back to a de-normal, or leave it as Zero. + */ + reg_move(rp, &tmp); + tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 63 */ + round_to_int(&tmp); + e = 0; + RE_ENTRANT_CHECK_OFF; + put_fs_long(tmp.sigl, (unsigned long *) d); + put_fs_long(tmp.sigh, (unsigned long *) (d + 4)); + RE_ENTRANT_CHECK_ON; + } + e |= rp->sign == SIGN_POS ? 0 : 0x8000; + RE_ENTRANT_CHECK_OFF; + put_fs_word(e, (unsigned short *) (d + 8)); + RE_ENTRANT_CHECK_ON; +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_mul.c linux/arch/i386/math-emu/reg_mul.c --- v1.1.76/linux/arch/i386/math-emu/reg_mul.c Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_mul.c Fri Feb 25 14:42:46 1994 @@ -0,0 +1,105 @@ +/*---------------------------------------------------------------------------+ + | 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 billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | 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 */ +int reg_mul(FPU_REG const *a, FPU_REG const *b, + FPU_REG *dest, unsigned int control_w) +{ + char saved_sign = dest->sign; + char sign = (a->sign ^ b->sign); + + if (!(a->tag | b->tag)) + { + /* Both regs Valid, this should be the most common case. */ + dest->sign = sign; + if ( reg_u_mul(a, b, dest, control_w) ) + { + dest->sign = saved_sign; + return 1; + } + return 0; + } + 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 1; + } +#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); + /* The 80486 book says that the answer is +0, but a real + 80486 behaves this way. + IEEE-754 apparently says it should be this way. */ + dest->sign = sign; + return 0; + } + else + { + /* Must have infinities, NaNs, etc */ + if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) + { return real_2op_NaN(a, b, dest); } + else if (a->tag == TW_Infinity) + { + if (b->tag == TW_Zero) + { return arith_invalid(dest); } /* Zero*Infinity is invalid */ + else + { +#ifdef DENORM_OPERAND + if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(a, dest); + dest->sign = sign; + } + return 0; + } + else if (b->tag == TW_Infinity) + { + if (a->tag == TW_Zero) + { return arith_invalid(dest); } /* Zero*Infinity is invalid */ + else + { +#ifdef DENORM_OPERAND + if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && + denormal_operand() ) + return 1; +#endif DENORM_OPERAND + reg_move(b, dest); + dest->sign = sign; + } + return 0; + } +#ifdef PARANOID + else + { + EXCEPTION(EX_INTERNAL|0x102); + return 1; + } +#endif PARANOID + } +} diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_norm.S linux/arch/i386/math-emu/reg_norm.S --- v1.1.76/linux/arch/i386/math-emu/reg_norm.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_norm.S Tue Jan 11 11:10:49 1994 @@ -0,0 +1,150 @@ +/*---------------------------------------------------------------------------+ + | reg_norm.S | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Normalize the value in a FPU_REG. | + | | + | Call from C as: | + | void normalize(FPU_REG *n) | + | | + | void normalize_nuo(FPU_REG *n) | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_asm.h" + + +.text + + .align 2,144 +.globl _normalize + +_normalize: + pushl %ebp + movl %esp,%ebp + pushl %ebx + + movl PARAM1,%ebx + +#ifdef PARANOID + cmpb TW_Valid,TAG(%ebx) + je L_ok + + pushl $0x220 + call _exception + addl $4,%esp + +L_ok: +#endif PARANOID + + 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 */ + + 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 + +#ifdef PARANOID + cmpb TW_Valid,TAG(%ebx) + je L_ok_nuo + + pushl $0x221 + call _exception + addl $4,%esp + +L_ok_nuo: +#endif PARANOID + + movl SIGH(%ebx),%edx + movl SIGL(%ebx),%eax + + orl %edx,%edx /* ms bits */ + js L_exit /* Already normalized */ + jnz L_nuo_shift_1 /* Shift left 1 - 31 bits */ + + orl %eax,%eax + jz L_zero /* The contents are zero */ + + 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 -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_round.S linux/arch/i386/math-emu/reg_round.S --- v1.1.76/linux/arch/i386/math-emu/reg_round.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_round.S Fri Aug 19 14:06:50 1994 @@ -0,0 +1,701 @@ + .file "reg_round.S" +/*---------------------------------------------------------------------------+ + | reg_round.S | + | | + | Rounding/truncation/etc for FPU basic arithmetic functions. | + | | + | Copyright (C) 1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | This code has four possible entry points. | + | The following must be entered by a jmp instruction: | + | fpu_reg_round, fpu_reg_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) | + | | + | For correct "up" and "down" rounding, the argument must have the correct | + | sign. | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Four entry points. | + | | + | Needed by both the fpu_reg_round and fpu_reg_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_reg_round_sqrt entry point: | + | %cx A control word in the same format as the FPU control word. | + | Otherwise, PARAM4 must give such a value. | + | | + | | + | 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" + +/* Flags for FPU_bits_lost */ +#define LOST_DOWN $1 +#define LOST_UP $2 + +/* Flags for FPU_denormal */ +#define DENORMAL $1 +#define UNMASKED_UNDERFLOW $2 + + +#ifndef NON_REENTRANT_FPU +/* Make the code re-entrant by putting + local storage on the stack: */ +#define FPU_bits_lost (%esp) +#define FPU_denormal 1(%esp) + +#else +/* Not re-entrant, so we can gain speed by putting + local storage in a static area: */ +.data + .align 2,0 +FPU_bits_lost: + .byte 0 +FPU_denormal: + .byte 0 +#endif NON_REENTRANT_FPU + + +.text + .align 2,144 +.globl fpu_reg_round +.globl fpu_reg_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_reg_round_sqrt + +fpu_reg_round: /* Normal entry point */ + movl PARAM4,%ecx + +fpu_reg_round_sqrt: /* Entry point from wm_sqrt.S */ + +#ifndef NON_REENTRANT_FPU + pushl %ebx /* adjust the stack pointer */ +#endif NON_REENTRANT_FPU + +#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 PECULIAR_486 +/* With the precision control bits set to 01 "(reserved)", a real 80486 + behaves as if the precision control bits were set to 11 "64 bits" */ + cmpl PR_RESERVED_BITS,%ecx + je LRound_To_64 +#ifdef PARANOID + jmp L_bugged_denorm_486 +#endif PARANOID +#else +#ifdef PARANOID + jmp L_bugged_denorm /* There is no bug, just a bad control word */ +#endif PARANOID +#endif PECULIAR_486 + + +/* 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_round24 +#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_round53 +#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_round64 +#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 + + /* Overflow, adjust the result (significand 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: + /* store the result */ + movb TW_Valid,TAG(%edi) + +xL_Store_significand: + movl %eax,SIGH(%edi) + movl %ebx,SIGL(%edi) + + xorl %eax,%eax /* No errors detected. */ + + cmpl EXP_OVER,EXP(%edi) + jge L_overflow + +fpu_reg_round_exit: +#ifndef NON_REENTRANT_FPU + popl %ebx /* adjust the stack pointer */ +#endif NON_REENTRANT_FPU + +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_UNDER+1,%ecx + subl EXP(%edi),%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 /* Save whether %edx is non-zero */ + 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: + 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 + +#ifdef PECULIAR_486 + /* + * This implements a special feature of 80486 behaviour. + * Underflow will be signalled even if the number is + * not a denormal after rounding. + * This difference occurs only for masked underflow, and not + * in the unmasked case. + * Actual 80486 behaviour differs from this in some circumstances. + */ + orl %eax,%eax /* ms bits */ + js LNormalise_shift_done /* Will be masked underflow */ +#endif PECULIAR_486 + + orl %eax,%eax /* ms bits */ + js xL_Normalised /* No longer a denormal */ + + 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 + +/* Reduce the exponent to EXP_UNDER */ + movl EXP_UNDER,EXP(%edi) + 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_reg_round_exit + + +xSignal_underflow: + /* The number may have been changed to a non-denormal */ + /* by the rounding operations. */ + cmpl EXP_UNDER,EXP(%edi) + jle xDo_unmasked_underflow + + jmp xL_Normalised + +xDo_unmasked_underflow: + /* Increase the exponent by the magic number */ + addl $(3*(1<<13)),EXP(%edi) + push %eax + pushl EX_Underflow + call EXCEPTION + popl %eax + popl %eax + jmp xL_Normalised + + +#ifdef PARANOID +#ifdef PECULIAR_486 +L_bugged_denorm_486: + pushl EX_INTERNAL|0x236 + call EXCEPTION + popl %ebx + jmp L_exception_exit +#else +L_bugged_denorm: + pushl EX_INTERNAL|0x230 + call EXCEPTION + popl %ebx + jmp L_exception_exit +#endif PECULIAR_486 + +L_bugged_round24: + pushl EX_INTERNAL|0x231 + call EXCEPTION + popl %ebx + jmp L_exception_exit + +L_bugged_round53: + pushl EX_INTERNAL|0x232 + call EXCEPTION + popl %ebx + jmp L_exception_exit + +L_bugged_round64: + pushl EX_INTERNAL|0x233 + call EXCEPTION + popl %ebx + jmp L_exception_exit + +L_norm_bugged: + pushl EX_INTERNAL|0x234 + call EXCEPTION + popl %ebx + jmp L_exception_exit + +L_entry_bugged: + pushl EX_INTERNAL|0x235 + call EXCEPTION + popl %ebx +L_exception_exit: + mov $1,%eax + jmp fpu_reg_round_exit +#endif PARANOID diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_u_add.S linux/arch/i386/math-emu/reg_u_add.S --- v1.1.76/linux/arch/i386/math-emu/reg_u_add.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_u_add.S Wed Dec 1 14:44:16 1993 @@ -0,0 +1,189 @@ + .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. | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | + | int control_w) | + | | + +---------------------------------------------------------------------------*/ + +/* + | 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 + 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 + + movl EXP(%esi),%ecx + subl EXP(%edi),%ecx /* exp1 - exp2 */ + 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_reg_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 -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_u_div.S linux/arch/i386/math-emu/reg_u_div.S --- v1.1.76/linux/arch/i386/math-emu/reg_u_div.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_u_div.S Thu Jun 2 10:28:28 1994 @@ -0,0 +1,477 @@ + .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 billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | 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) */ + + +#ifndef NON_REENTRANT_FPU +/* + Local storage on the stack: + Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 + Overflow flag: ovfl_flag + */ +#define FPU_accum_3 -4(%ebp) +#define FPU_accum_2 -8(%ebp) +#define FPU_accum_1 -12(%ebp) +#define FPU_accum_0 -16(%ebp) +#define FPU_result_1 -20(%ebp) +#define FPU_result_2 -24(%ebp) +#define FPU_ovfl_flag -28(%ebp) + +#else +.data +/* + Local storage in a static area: + Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 + Overflow flag: ovfl_flag + */ + .align 2,0 +FPU_accum_3: + .long 0 +FPU_accum_2: + .long 0 +FPU_accum_1: + .long 0 +FPU_accum_0: + .long 0 +FPU_result_1: + .long 0 +FPU_result_2: + .long 0 +FPU_ovfl_flag: + .byte 0 +#endif NON_REENTRANT_FPU + + +.text + .align 2,144 + +.globl _reg_u_div + +.globl _divide_kernel + +_reg_u_div: + pushl %ebp + movl %esp,%ebp +#ifndef NON_REENTRANT_FPU + subl $28,%esp +#endif NON_REENTRANT_FPU + + 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 FPU_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,FPU_result_2 + + /* Work on the remainder of the first division */ + xorl %eax,%eax + divl %ecx + movl %eax,FPU_result_1 + + /* Work on the remainder of the 64 bit division */ + xorl %eax,%eax + divl %ecx + + testb $255,FPU_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 FPU_result_2 + rcrl FPU_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,FPU_accum_2 + movl SIGH(%esi),%eax + movl %eax,FPU_accum_3 + xorl %eax,%eax + movl %eax,FPU_accum_1 /* zero the extension */ + movl %eax,FPU_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,FPU_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 FPU_ovfl_flag /* Keep a record */ + + subl SIGL(%ebx),%eax + sbbl SIGH(%ebx),%edx /* Prevent the overflow */ + movl %eax,FPU_accum_2 + movl %edx,FPU_accum_3 + +LLess_than_1: +/* At this point, we have a dividend < divisor, with a record of + adjustment in FPU_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,FPU_result_2 /* Put the result in the answer */ + + mull SIGH(%ebx) /* mul by the ms dw of the denom */ + + subl %eax,FPU_accum_2 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_3 + + movl FPU_result_2,%eax /* Get the result back */ + mull SIGL(%ebx) /* now mul the ls dw of the denom */ + + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + sbbl $0,FPU_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 FPU_result_2 /* Correct the answer */ + + movl SIGL(%ebx),%eax + movl SIGH(%ebx),%edx + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + +#ifdef PARANOID + sbbl $0,FPU_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, FPU_accum_0 not used from now on */ +LDo_2nd_32_bits: + movl FPU_accum_2,%edx /* get the reduced num */ + movl FPU_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,FPU_accum_2 + movl %eax,FPU_accum_1 + + incl FPU_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,FPU_result_1 /* Put the result in the answer */ + + mull SIGH(%ebx) /* mul by the ms dw of the denom */ + + subl %eax,FPU_accum_1 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 +#endif PARANOID + + movl FPU_result_1,%eax /* Get the result back */ + mull SIGL(%ebx) /* now mul the ls dw of the denom */ + + subl %eax,FPU_accum_0 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */ + sbbl $0,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 +#endif PARANOID + + jz LDo_3rd_32_bits + +#ifdef PARANOID + cmpl $1,FPU_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,FPU_accum_0 /* Subtract from the num local reg */ + sbbl %edx,FPU_accum_1 + sbbl $0,FPU_accum_2 + +#ifdef PARANOID + jc L_bugged_2 + jne L_bugged_2 +#endif PARANOID + + addl $1,FPU_result_1 /* Correct the answer */ + adcl $0,FPU_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 FPU_accum_1,%edx /* get the reduced num */ + movl FPU_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,FPU_accum_1 + movl %eax,FPU_accum_0 + + addl $1,FPU_result_1 /* Reflect the subtraction in the answer */ + adcl $0,FPU_result_2 + jne LRound_prep + jnc LRound_prep + + /* This is a tricky spot, there is an overflow of the answer */ + movb $255,FPU_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 FPU_accum_0,%ecx + movl FPU_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,FPU_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 FPU_result_2 + rcrl FPU_result_1 + rcrl %eax + +/* Round the result as required */ +LRound_precision: + decl EXP(%edi) /* binary point between 1st & 2nd bits */ + + movl %eax,%edx + movl FPU_result_1,%ebx + movl FPU_result_2,%eax + jmp fpu_reg_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 -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_u_mul.S linux/arch/i386/math-emu/reg_u_mul.S --- v1.1.76/linux/arch/i386/math-emu/reg_u_mul.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_u_mul.S Thu Jun 2 10:28:28 1994 @@ -0,0 +1,163 @@ + .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 billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | 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" + + + +#ifndef NON_REENTRANT_FPU +/* Local storage on the stack: */ +#define FPU_accum_0 -4(%ebp) /* ms word */ +#define FPU_accum_1 -8(%ebp) + +#else +/* Local storage in a static area: */ +.data + .align 4,0 +FPU_accum_0: + .long 0 +FPU_accum_1: + .long 0 +#endif NON_REENTRANT_FPU + + +.text + .align 2,144 + +.globl _reg_u_mul +_reg_u_mul: + pushl %ebp + movl %esp,%ebp +#ifndef NON_REENTRANT_FPU + subl $8,%esp +#endif NON_REENTRANT_FPU + + 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,FPU_accum_0 + movl %edx,FPU_accum_1 + + movl SIGL(%esi),%eax + mull SIGH(%edi) + addl %eax,FPU_accum_1 + adcl %edx,%ebx +/* adcl $0,%ecx // overflow here is not possible */ + + movl SIGH(%esi),%eax + mull SIGL(%edi) + addl %eax,FPU_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,FPU_accum_0 + rcll $1,FPU_accum_1 + rcll $1,%ebx + rcll $1,%ecx + decl EXP(%edi) + +LResult_Normalised: + movl FPU_accum_0,%eax + movl FPU_accum_1,%edx + orl %eax,%eax + jz L_extent_zero + + orl $1,%edx + +L_extent_zero: + movl %ecx,%eax + jmp fpu_reg_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 -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/reg_u_sub.S linux/arch/i386/math-emu/reg_u_sub.S --- v1.1.76/linux/arch/i386/math-emu/reg_u_sub.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/reg_u_sub.S Wed Dec 1 14:44:16 1993 @@ -0,0 +1,292 @@ + .file "reg_u_sub.S" +/*---------------------------------------------------------------------------+ + | reg_u_sub.S | + | | + | Core floating point subtraction routine. | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | + | int control_w) | + | | + +---------------------------------------------------------------------------*/ + +/* + | 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 + + movl EXP(%esi),%ecx + subl EXP(%edi),%ecx /* exp1 - exp2 */ + +#ifdef PARANOID + /* source 2 is always smaller than source 1 */ + js L_bugged_1 + + testl $0x80000000,SIGH(%edi) /* The args are assumed to be 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) + xchg %edx,%eax + jmp fpu_reg_round + +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 /* %eax contains zero */ + +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_reg_round /* Round the result */ + + +#ifdef PARANOID +L_bugged_1: + pushl EX_INTERNAL|0x206 + call EXCEPTION + pop %ebx + jmp L_error_exit + +L_bugged_2: + pushl EX_INTERNAL|0x209 + call EXCEPTION + pop %ebx + jmp L_error_exit + +L_bugged_3: + pushl EX_INTERNAL|0x210 + call EXCEPTION + pop %ebx + jmp L_error_exit + +L_bugged_4: + pushl EX_INTERNAL|0x211 + call EXCEPTION + pop %ebx + jmp L_error_exit + +L_bugged: + pushl EX_INTERNAL|0x212 + call EXCEPTION + pop %ebx + jmp L_error_exit +#endif PARANOID + + +L_error_exit: + movl $1,%eax +L_exit: + popl %ebx + popl %edi + popl %esi + leave + ret diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/round_Xsig.S linux/arch/i386/math-emu/round_Xsig.S --- v1.1.76/linux/arch/i386/math-emu/round_Xsig.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/round_Xsig.S Mon Aug 1 08:19:15 1994 @@ -0,0 +1,148 @@ +/*---------------------------------------------------------------------------+ + | round_Xsig.S | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Normalize and round a 12 byte quantity. | + | Call from C as: | + | int round_Xsig(Xsig *n) | + | | + | Normalize a 12 byte quantity. | + | Call from C as: | + | int norm_Xsig(Xsig *n) | + | | + | Each function returns the size of the shift (nr of bits). | + | | + +---------------------------------------------------------------------------*/ + .file "round_Xsig.S" + +#include "fpu_asm.h" + + +.text + + .align 2,144 +.globl _round_Xsig + +_round_Xsig: + pushl %ebp + movl %esp,%ebp + pushl %ebx /* Reserve some space */ + pushl %ebx + pushl %esi + + movl PARAM1,%esi + + movl 8(%esi),%edx + movl 4(%esi),%ebx + movl (%esi),%eax + + movl $0,-4(%ebp) + + orl %edx,%edx /* ms bits */ + js L_round /* Already normalized */ + jnz L_shift_1 /* Shift left 1 - 31 bits */ + + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + movl $-32,-4(%ebp) + +/* 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 + subl %ecx,-4(%ebp) + shld %cl,%ebx,%edx + shld %cl,%eax,%ebx + shl %cl,%eax + +L_round: + testl $0x80000000,%eax + jz L_exit + + addl $1,%ebx + adcl $0,%edx + jnz L_exit + + movl $0x80000000,%edx + incl -4(%ebp) + +L_exit: + movl %edx,8(%esi) + movl %ebx,4(%esi) + movl %eax,(%esi) + + movl -4(%ebp),%eax + + popl %esi + popl %ebx + leave + ret + + + + + .align 2,144 +.globl _norm_Xsig + +_norm_Xsig: + pushl %ebp + movl %esp,%ebp + pushl %ebx /* Reserve some space */ + pushl %ebx + pushl %esi + + movl PARAM1,%esi + + movl 8(%esi),%edx + movl 4(%esi),%ebx + movl (%esi),%eax + + movl $0,-4(%ebp) + + orl %edx,%edx /* ms bits */ + js L_n_exit /* Already normalized */ + jnz L_n_shift_1 /* Shift left 1 - 31 bits */ + + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + movl $-32,-4(%ebp) + + orl %edx,%edx /* ms bits */ + js L_n_exit /* Normalized now */ + jnz L_n_shift_1 /* Shift left 1 - 31 bits */ + + movl %ebx,%edx + movl %eax,%ebx + xorl %eax,%eax + addl $-32,-4(%ebp) + jmp L_n_exit /* Might not be normalized, + but shift no more. */ + +/* We need to shift left by 1 - 31 bits */ +L_n_shift_1: + bsrl %edx,%ecx /* get the required shift in %ecx */ + subl $31,%ecx + negl %ecx + subl %ecx,-4(%ebp) + shld %cl,%ebx,%edx + shld %cl,%eax,%ebx + shl %cl,%eax + +L_n_exit: + movl %edx,8(%esi) + movl %ebx,4(%esi) + movl %eax,(%esi) + + movl -4(%ebp),%eax + + popl %esi + popl %ebx + leave + ret + diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/shr_Xsig.S linux/arch/i386/math-emu/shr_Xsig.S --- v1.1.76/linux/arch/i386/math-emu/shr_Xsig.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/shr_Xsig.S Mon Aug 1 08:19:16 1994 @@ -0,0 +1,90 @@ + .file "shr_Xsig.S" +/*---------------------------------------------------------------------------+ + | shr_Xsig.S | + | | + | 12 byte right shift function | + | | + | Copyright (C) 1992,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void shr_Xsig(Xsig *arg, unsigned nr) | + | | + | Extended shift right function. | + | Fastest for small shifts. | + | Shifts the 12 byte quantity pointed to by the first arg (arg) | + | right by the number of bits specified by the second arg (nr). | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_asm.h" + +.text + .align 2,144 + + .globl _shr_Xsig +_shr_Xsig: + 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),%eax /* lsl */ + movl 4(%esi),%ebx /* midl */ + movl 8(%esi),%edx /* msl */ + shrd %cl,%ebx,%eax + shrd %cl,%edx,%ebx + shr %cl,%edx + movl %eax,(%esi) + movl %ebx,4(%esi) + movl %edx,8(%esi) + popl %ebx + popl %esi + leave + ret + +L_more_than_31: + cmpl $64,%ecx + jnc L_more_than_63 + + subb $32,%cl + movl 4(%esi),%eax /* midl */ + movl 8(%esi),%edx /* msl */ + shrd %cl,%edx,%eax + shr %cl,%edx + movl %eax,(%esi) + movl %edx,4(%esi) + movl $0,8(%esi) + popl %esi + leave + ret + +L_more_than_63: + cmpl $96,%ecx + jnc L_more_than_95 + + subb $64,%cl + movl 8(%esi),%eax /* msl */ + shr %cl,%eax + xorl %edx,%edx + movl %eax,(%esi) + movl %edx,4(%esi) + movl %edx,8(%esi) + popl %esi + leave + ret + +L_more_than_95: + xorl %eax,%eax + movl %eax,(%esi) + movl %eax,4(%esi) + movl %eax,8(%esi) + popl %esi + leave + ret diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/status_w.h linux/arch/i386/math-emu/status_w.h --- v1.1.76/linux/arch/i386/math-emu/status_w.h Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/status_w.h Wed Dec 1 14:44:16 1993 @@ -0,0 +1,65 @@ +/*---------------------------------------------------------------------------+ + | status_w.h | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + +---------------------------------------------------------------------------*/ + +#ifndef _STATUS_H_ +#define _STATUS_H_ + +#include "fpu_emu.h" /* for definition of PECULIAR_486 */ + +#ifdef __ASSEMBLER__ +#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 __ASSEMBLER__ + +#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 status_word() \ + ((partial_status & ~SW_Top & 0xffff) | ((top << SW_Top_Shift) & SW_Top)) +#define setcc(cc) ({ \ + partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \ + partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); }) + +#ifdef PECULIAR_486 + /* Default, this conveys no information, but an 80486 does it. */ + /* Clear the SW_C1 bit, "other bits undefined". */ +# define clear_C1() { partial_status &= ~SW_C1; } +# else +# define clear_C1() +#endif PECULIAR_486 + +#endif __ASSEMBLER__ + +#endif _STATUS_H_ diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/version.h linux/arch/i386/math-emu/version.h --- v1.1.76/linux/arch/i386/math-emu/version.h Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/version.h Mon Aug 1 08:19:16 1994 @@ -0,0 +1,12 @@ +/*---------------------------------------------------------------------------+ + | version.h | + | | + | | + | Copyright (C) 1992,1993,1994 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#define FPU_VERSION "wm-FPU-emu version 1.20" diff -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/wm_shrx.S linux/arch/i386/math-emu/wm_shrx.S --- v1.1.76/linux/arch/i386/math-emu/wm_shrx.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/wm_shrx.S Wed Dec 1 14:44:16 1993 @@ -0,0 +1,208 @@ + .file "wm_shrx.S" +/*---------------------------------------------------------------------------+ + | wm_shrx.S | + | | + | 64 bit right shift functions | + | | + | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | unsigned shrx(void *arg1, unsigned arg2) | + | and | + | unsigned shrxs(void *arg1, unsigned arg2) | + | | + +---------------------------------------------------------------------------*/ + +#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 -u --recursive --new-file v1.1.76/linux/arch/i386/math-emu/wm_sqrt.S linux/arch/i386/math-emu/wm_sqrt.S --- v1.1.76/linux/arch/i386/math-emu/wm_sqrt.S Thu Jan 1 02:00:00 1970 +++ linux/arch/i386/math-emu/wm_sqrt.S Thu Jun 2 10:28:28 1994 @@ -0,0 +1,474 @@ + .file "wm_sqrt.S" +/*---------------------------------------------------------------------------+ + | wm_sqrt.S | + | | + | Fixed point arithmetic square root evaluation. | + | | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | | + | Call from C as: | + | void wm_sqrt(FPU_REG *n, unsigned int control_word) | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | 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" + + +#ifndef NON_REENTRANT_FPU +/* Local storage on the stack: */ +#define FPU_accum_3 -4(%ebp) /* ms word */ +#define FPU_accum_2 -8(%ebp) +#define FPU_accum_1 -12(%ebp) +#define FPU_accum_0 -16(%ebp) + +/* + * 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 + */ +#define FPU_fsqrt_arg_2 -20(%ebp) /* ms word */ +#define FPU_fsqrt_arg_1 -24(%ebp) +#define FPU_fsqrt_arg_0 -28(%ebp) /* ls word, at most the ms bit is set */ + +#else +/* Local storage in a static area: */ +.data + .align 4,0 +FPU_accum_3: + .long 0 /* ms word */ +FPU_accum_2: + .long 0 +FPU_accum_1: + .long 0 +FPU_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 + */ +FPU_fsqrt_arg_2: + .long 0 /* ms word */ +FPU_fsqrt_arg_1: + .long 0 +FPU_fsqrt_arg_0: + .long 0 /* ls word, at most the ms bit is set */ +#endif NON_REENTRANT_FPU + + +.text + .align 2,144 + +.globl _wm_sqrt +_wm_sqrt: + pushl %ebp + movl %esp,%ebp +#ifndef NON_REENTRANT_FPU + subl $28,%esp +#endif NON_REENTRANT_FPU + 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,FPU_fsqrt_arg_2 /* ms word of n */ + movl %ecx,FPU_fsqrt_arg_1 + movl %edx,FPU_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 FPU_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 FPU_fsqrt_arg_1,%ecx + subl %ecx,%eax + movl FPU_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,FPU_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,FPU_accum_1 + + movl %esi,%eax + mull %esi + movl %edx,FPU_accum_3 + movl %eax,FPU_accum_2 + + movl %edi,%eax + mull %esi + addl %eax,FPU_accum_1 + adcl %edx,FPU_accum_2 + adcl $0,FPU_accum_3 + +/* movl %esi,%eax */ +/* mull %edi */ + addl %eax,FPU_accum_1 + adcl %edx,FPU_accum_2 + adcl $0,FPU_accum_3 + +/* guess^2 now in FPU_accum_3:FPU_accum_2:FPU_accum_1 */ + + movl FPU_fsqrt_arg_0,%eax /* get normalized n */ + subl %eax,FPU_accum_1 + movl FPU_fsqrt_arg_1,%eax + sbbl %eax,FPU_accum_2 + movl FPU_fsqrt_arg_2,%eax /* ms word of normalized n */ + sbbl %eax,FPU_accum_3 + jnc sqrt_stage_3_positive + +/* Subtraction gives a negative result, + negate the result before division */ + notl FPU_accum_1 + notl FPU_accum_2 + notl FPU_accum_3 + addl $1,FPU_accum_1 + adcl $0,FPU_accum_2 + +#ifdef PARANOID + adcl $0,FPU_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 FPU_accum_2,%edx + movl FPU_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 FPU_accum_2,%edx + movl FPU_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_reg_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 diff -u --recursive --new-file v1.1.76/linux/arch/i386/traps.c linux/arch/i386/traps.c --- v1.1.76/linux/arch/i386/traps.c Mon Jan 2 15:20:20 1995 +++ linux/arch/i386/traps.c Thu Jan 1 02:00:00 1970 @@ -1,310 +0,0 @@ -/* - * linux/arch/i386/traps.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * 'Traps.c' handles hardware traps and faults after we have saved some - * state in 'asm.s'. Currently mostly a debugging-aid, will be extended - * to mainly kill the offending process (probably by giving it a signal, - * but possibly by killing it outright if necessary). - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -asmlinkage int system_call(void); -asmlinkage void lcall7(void); -struct desc_struct default_ldt; - -static inline void console_verbose(void) -{ - extern int console_loglevel; - console_loglevel = 15; -} - -#define DO_ERROR(trapnr, signr, str, name, tsk) \ -asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ -{ \ - tsk->tss.error_code = error_code; \ - tsk->tss.trap_no = trapnr; \ - if (signr == SIGTRAP && current->flags & PF_PTRACED) \ - current->blocked &= ~(1 << (SIGTRAP-1)); \ - send_sig(signr, tsk, 1); \ - die_if_kernel(str,regs,error_code); \ -} - -#define get_seg_byte(seg,addr) ({ \ -register unsigned char __res; \ -__asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \ - :"=a" (__res):"0" (seg),"m" (*(addr))); \ -__res;}) - -#define get_seg_long(seg,addr) ({ \ -register unsigned long __res; \ -__asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \ - :"=a" (__res):"0" (seg),"m" (*(addr))); \ -__res;}) - -#define _fs() ({ \ -register unsigned short __res; \ -__asm__("mov %%fs,%%ax":"=a" (__res):); \ -__res;}) - -void page_exception(void); - -asmlinkage void divide_error(void); -asmlinkage void debug(void); -asmlinkage void nmi(void); -asmlinkage void int3(void); -asmlinkage void overflow(void); -asmlinkage void bounds(void); -asmlinkage void invalid_op(void); -asmlinkage void device_not_available(void); -asmlinkage void double_fault(void); -asmlinkage void coprocessor_segment_overrun(void); -asmlinkage void invalid_TSS(void); -asmlinkage void segment_not_present(void); -asmlinkage void stack_segment(void); -asmlinkage void general_protection(void); -asmlinkage void page_fault(void); -asmlinkage void coprocessor_error(void); -asmlinkage void reserved(void); -asmlinkage void alignment_check(void); - -/*static*/ void die_if_kernel(char * str, struct pt_regs * regs, long err) -{ - int i; - unsigned long esp; - unsigned short ss; - - esp = (unsigned long) ®s->esp; - ss = KERNEL_DS; - if ((regs->eflags & VM_MASK) || (3 & regs->cs) == 3) - return; - if (regs->cs & 3) { - esp = regs->esp; - ss = regs->ss; - } - console_verbose(); - printk("%s: %04lx\n", str, err & 0xffff); - printk("EIP: %04x:%08lx\nEFLAGS: %08lx\n", 0xffff & regs->cs,regs->eip,regs->eflags); - printk("eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n", - regs->eax, regs->ebx, regs->ecx, regs->edx); - printk("esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n", - regs->esi, regs->edi, regs->ebp, esp); - printk("ds: %04x es: %04x fs: %04x gs: %04x ss: %04x\n", - regs->ds, regs->es, regs->fs, regs->gs, ss); - store_TR(i); - if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page) - printk("Corrupted stack page\n"); - printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)\nStack: ", - current->comm, current->pid, 0xffff & i, current->kernel_stack_page); - for(i=0;i<5;i++) - printk("%08lx ", get_seg_long(ss,(i+(unsigned long *)esp))); - printk("\nCode: "); - for(i=0;i<20;i++) - printk("%02x ",0xff & get_seg_byte(regs->cs,(i+(char *)regs->eip))); - printk("\n"); - do_exit(SIGSEGV); -} - -DO_ERROR( 0, SIGFPE, "divide error", divide_error, current) -DO_ERROR( 3, SIGTRAP, "int3", int3, current) -DO_ERROR( 4, SIGSEGV, "overflow", overflow, current) -DO_ERROR( 5, SIGSEGV, "bounds", bounds, current) -DO_ERROR( 6, SIGILL, "invalid operand", invalid_op, current) -DO_ERROR( 7, SIGSEGV, "device not available", device_not_available, current) -DO_ERROR( 8, SIGSEGV, "double fault", double_fault, current) -DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun, last_task_used_math) -DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS, current) -DO_ERROR(11, SIGBUS, "segment not present", segment_not_present, current) -DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) -DO_ERROR(15, SIGSEGV, "reserved", reserved, current) -DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) - -asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) -{ - int signr = SIGSEGV; - - if (regs->eflags & VM_MASK) { - handle_vm86_fault((struct vm86_regs *) regs, error_code); - return; - } - die_if_kernel("general protection",regs,error_code); - switch (get_seg_byte(regs->cs, (char *)regs->eip)) { - case 0xCD: /* INT */ - case 0xF4: /* HLT */ - case 0xFA: /* CLI */ - case 0xFB: /* STI */ - signr = SIGILL; - } - current->tss.error_code = error_code; - current->tss.trap_no = 13; - send_sig(signr, current, 1); -} - -asmlinkage void do_nmi(struct pt_regs * regs, long error_code) -{ - printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n"); - printk("You probably have a hardware problem with your RAM chips\n"); -} - -asmlinkage void do_debug(struct pt_regs * regs, long error_code) -{ - if (regs->eflags & VM_MASK) { - handle_vm86_debug((struct vm86_regs *) regs, error_code); - return; - } - if (current->flags & PF_PTRACED) - current->blocked &= ~(1 << (SIGTRAP-1)); - send_sig(SIGTRAP, current, 1); - current->tss.trap_no = 1; - current->tss.error_code = error_code; - if ((regs->cs & 3) == 0) { - /* If this is a kernel mode trap, then reset db7 and allow us to continue */ - __asm__("movl %0,%%db7" - : /* no output */ - : "r" (0)); - return; - } - die_if_kernel("debug",regs,error_code); -} - -/* - * Allow the process which triggered the interrupt to recover the error - * condition. - * - the status word is saved in the cs selector. - * - the tag word is saved in the operand selector. - * - the status word is then cleared and the tags all set to Empty. - * - * This will give sufficient information for complete recovery provided that - * the affected process knows or can deduce the code and data segments - * which were in force when the exception condition arose. - * - * Note that we play around with the 'TS' bit to hopefully get - * the correct behaviour even in the presence of the asynchronous - * IRQ13 behaviour - */ -void math_error(void) -{ - struct i387_hard_struct * env; - - clts(); - if (!last_task_used_math) { - __asm__("fnclex"); - return; - } - env = &last_task_used_math->tss.i387.hard; - send_sig(SIGFPE, last_task_used_math, 1); - last_task_used_math->tss.trap_no = 16; - last_task_used_math->tss.error_code = 0; - __asm__ __volatile__("fnsave %0":"=m" (*env)); - last_task_used_math = NULL; - stts(); - env->fcs = (env->swd & 0x0000ffff) | (env->fcs & 0xffff0000); - env->fos = env->twd; - env->swd &= 0xffff3800; - env->twd = 0xffffffff; -} - -asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code) -{ - ignore_irq13 = 1; - math_error(); -} - -/* - * 'math_state_restore()' saves the current math information in the - * old math state array, and gets the new ones from the current task - * - * Careful.. There are problems with IBM-designed IRQ13 behaviour. - * Don't touch unless you *really* know how it works. - */ -asmlinkage void math_state_restore(void) -{ - __asm__ __volatile__("clts"); - if (last_task_used_math == current) - return; - timer_table[COPRO_TIMER].expires = jiffies+50; - timer_active |= 1<tss.i387)); - else - __asm__("fnclex"); - last_task_used_math = current; - if (current->used_math) { - __asm__("frstor %0": :"m" (current->tss.i387)); - } else { - __asm__("fninit"); - current->used_math=1; - } - timer_active &= ~(1<comm); - send_sig(SIGFPE,current,1); - schedule(); -} - -#endif /* CONFIG_MATH_EMULATION */ - -void trap_init(void) -{ - int i; - struct desc_struct * p; - - set_call_gate(&default_ldt,lcall7); - set_trap_gate(0,÷_error); - set_trap_gate(1,&debug); - set_trap_gate(2,&nmi); - set_system_gate(3,&int3); /* int3-5 can be called from all */ - set_system_gate(4,&overflow); - set_system_gate(5,&bounds); - set_trap_gate(6,&invalid_op); - set_trap_gate(7,&device_not_available); - set_trap_gate(8,&double_fault); - set_trap_gate(9,&coprocessor_segment_overrun); - set_trap_gate(10,&invalid_TSS); - set_trap_gate(11,&segment_not_present); - set_trap_gate(12,&stack_segment); - set_trap_gate(13,&general_protection); - set_trap_gate(14,&page_fault); - set_trap_gate(15,&reserved); - set_trap_gate(16,&coprocessor_error); - set_trap_gate(17,&alignment_check); - for (i=18;i<48;i++) - set_trap_gate(i,&reserved); - set_system_gate(0x80,&system_call); -/* set up GDT task & ldt entries */ - p = gdt+FIRST_TSS_ENTRY; - set_tss_desc(p, &init_task.tss); - p++; - set_ldt_desc(p, &default_ldt, 1); - p++; - for(i=1 ; ia=p->b=0; - p++; - p->a=p->b=0; - p++; - } -/* Clear NT, so that we won't have troubles with that later on */ - __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); - load_TR(0); - load_ldt(0); -} diff -u --recursive --new-file v1.1.76/linux/arch/sparc/Makefile linux/arch/sparc/Makefile --- v1.1.76/linux/arch/sparc/Makefile Thu Jan 1 02:00:00 1970 +++ linux/arch/sparc/Makefile Thu Jan 5 09:38:43 1995 @@ -0,0 +1,27 @@ +# +# sparc/Makefile +# +# Makefile for the architecture dependant flags and dependancies on the +# Sparc. +# +# Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) +# + +# +# How to link, we send the linker the address at which the text section +# is to start. The prom loads us at 0x0-kernel_size. There is also an +# alias of this address space at 0xf8000000-(0xf8000000+kernel_size) but +# I ignore it and eliminate those mappings during vm initialization and +# just leave the low mapping. +# +LINKFLAGS = -N -Ttext 0x00004000 +CFLAGS := $(CFLAGS) -pipe + +HEAD := arch/sparc/kernel/head.o + +SUBDIRS := $(SUBDIRS) arch/sparc/kernel +ARCHIVES := arch/sparc/kernel/kernel.o $(ARCHIVES) + +archclean: + +archdep: diff -u --recursive --new-file v1.1.76/linux/arch/sparc/boot/boot.S linux/arch/sparc/boot/boot.S --- v1.1.76/linux/arch/sparc/boot/boot.S Mon Dec 12 20:41:32 1994 +++ linux/arch/sparc/boot/boot.S Thu Jan 1 02:00:00 1970 @@ -1,703 +0,0 @@ -/* boot.S: The initial boot code for the Sparc port of Linux. - - Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) - - This file has to serve three purposes. - - 1) determine the prom-version and cpu/architecture - 2) print enough useful info before we start to execute - c-code that I can possibly begin to debug things - 3) Hold the vector of trap entry points - - The Sparc offers many challenges to kernel design. Here I will - document those I have come across thus far. Upon bootup the boot - prom loads your a.out image into memory. This memory the prom has - already mapped for you, however as far as I can tell the virtual - address cache is not turned on although the MMU is translating - things. You get loaded at 0xf8004000 exactly. So, when you link - a boot-loadable object you want to do something like: - - ld -e start -T f8004000 -o mykernel myobj1.o myobj2.o .... - - to produce a proper image. - - At boot time you are given (as far as I can tell at this time) - one key to figure out what machine you are one and what devices - are available. The prom when it loads you leaves a pointer to - the 'rom vector' in register %o0 right before it jumps to your - starting address. This is a pointer to a struct that is full of - pointer to functions (ie. printf, halt, reboot), pointers to - linked lists (ie. memory mappings), and pointer to empirical - constants (ie. stdin and stdout magic cookies + rom version). - Starting with this piece of information you can figure out - just about anything you want about the machine you are on. -*/ - -#include "boot.h" -#include "version.h" - - .data - -/* First thing to go in the data segment is the interrupt stack. */ - - .globl _intstack - .globl _eintstack -_intstack: - .skip 4 * NBPG ! 16k = 128 128-byte stack frames -_eintstack: - - - -/* - The following are used with the prom_vector node-ops to figure out - the cpu-type -*/ - - .globl _cputyp - -_cputyp: - .word 1 - -_cputypval: - .asciz "sun4c" - .ascii " " - -/* - * Sun people can't spell worth damn. "compatability" indeed. - * At least we *know* we can't spell, and use a spell-checker. - */ -_cputypvar: - .asciz "compatability" - -_cputypvallen = _cputypvar - _cputypval - -/* This hold the prom-interface-version number for either v0 or v2. */ - - .align 4 - .globl _prom_iface_vers - -_prom_iface_vers: .skip 4 - -/* WARNING: evil messages follow */ - - .align 4 - -sun4_notsup: - .asciz "Sparc-Linux: sun4 support not implemented yet\n\n" - .align 4 - -sun4m_notsup: - .asciz "Sparc-Linux: sun4m support does not exist\n\n" - .align 4 - -sun4d_notsup: - .asciz "Sparc-Linux: sun4d support does not exist\n\n" - .align 4 - -you_lose: - .asciz "You lose..... Thanks for playing...\n" - .align 4 - -/* - Fill up the prom vector, note in particular the kind first element, - no joke. -*/ - - .globl _prom_vector_p - -_prom_vector_p: .skip 4 -prom_magic: .skip 4 ! magic mushroom, beware... -prom_rom_vers: .skip 4 ! interface version (v0 or v2) -prom_pluginvers: .skip 4 ! XXX help help help ??? -prom_revision: .skip 4 ! PROM revision (ie. 1.4) -prom_bootstr: .skip 4 ! what we are invoked with -prom_putchar: .skip 4 ! void putchar(int ch) BLOCKING. -prom_getchar: .skip 4 ! int getchar(void) BLOCKING. -prom_nputchar: .skip 4 ! int putchar(int ch) non-block -prom_ngetchar: .skip 4 ! int getchar(void) non-block -prom_halt: .skip 4 ! void halt(void) solaris friend -prom_eval: .skip 4 ! void eval(int len, char* string) -prom_v0mem_desc: .skip 4 ! V0 memory descriptor list ptr. -prom_nodefuncs: .skip 4 ! Magical Node functions -prom_v0devfuncs: .skip 4 ! V0 device operations -prom_putstring: .skip 4 ! prom putstring() -prom_bootme: .skip 4 ! reset() -prom_printf: .skip 4 ! minimal printf() - -/* The prom_abort pointer MUST be mapped in all contexts, because if you - don't then if a user process is running when you press the abort key - sequence, all sorts of bad things can happen -*/ - -prom_abort: .skip 4 ! "L1-A" magic cookie - ! must be mapped in ALL contexts -prom_ticks: .skip 4 ! number of ticks since reset - -/* prom_sync is a place where the kernel should place a pointer to a kernel - function that when called will sync all pending information to the drives - and then promptly return. If the kernel gets aborted with 'L1-A' one can - give the 'sync' command to the boot prompt and this magic cookie gets - executed. Nice feature eh? -*/ - -prom_sync: .skip 4 ! hook in prom for "sync" func -prom_v0bootarg: .skip 4 ! v0 prom boot arguments -prom_v2bootarg: .skip 4 ! same as above for v2 proms -prom_ethaddr_func: .skip 4 ! extract ethernet device address -prom_v2devfunc: .skip 4 ! ptr to v2 style device ops. -prom_xtra_array: .skip 4 ! who knows :-( help help -prom_setcontext: .skip 4 ! set context on sun4c -prom_stdin: .skip 4 ! prom stdin magic cookie -prom_stdout: .skip 4 ! prom stdout magic cookie - - - .align 4 - - .globl boot_msg - -/* memory descriptor property strings, v2 = yuk yuk yuk */ -/* XXX how to figure out vm mapped by prom? May have to scan magic addresses */ - -mem_prop_physavail: .asciz "available" -mem_prop_phystot: .asciz "reg" - -/* v2_memory descriptor struct kludged here for assembly, if it ain't broke */ - - .align 4 -v2_mem_struct: .skip 0xff - - .align 4 -v2_printf_physavail: .asciz "Physical Memory Available: 0x%x bytes" -v2_printf_phystot: .asciz "Physical Memory: 0x%x bytes" - -/* A place to store property strings returned from the prom 'node' funcs */ - - .align 4 -prop_string_buf: .skip 32 - -prop_name: .asciz "name" - .align 4 - -current_node: .skip 4 - .align 4 - - -/* nice little boot message */ - -boot_msg: - .ascii "Booting Sparc-Linux V0.00PRE-ALPHA " - .ascii WHO_COMPILED_ME - .asciz " \n" - .align 4 - - .globl boot_msg2 - -boot_msg2: - .asciz "Booting Sparclinux V0.00 PRE-ALPHA on a (SUN4C)\n\n" - - .align 4 - -pstring1: - .asciz "Prom Magic Cookie: 0x%x " - .align 4 - -pstring2: - .asciz "Interface Version: v%d\n" - .align 4 - -pstring3: - .asciz "Prom Revision: V%d\n\n" - .align 4 - -pstring4: - .ascii "Total Physical Memory: %d bytes\nVM mapped by Prom: %d bytes\n" - .asciz "Available Physical Memory: %d bytes\n" - .align 4 - - -newline: - .asciz "\n" - .align 4 - - .text - - .globl _msgbuf -msgbufsize = NBPG ! 1 page for msg buffer -_msgbuf = KERNBASE + NBPG - - -IE_reg_addr = _msgbuf + msgbufsize ! this page not used; points to IEreg - -/* - ignore the following variable settings, I used them when I had - no stinkin idea what the linker was doing with the symbols to - get them in the right place for load time -*/ - -whereis_kernbase = KERNBASE -whereis_prom_vector_p = _prom_vector_p-KERNBASE - -/* Ok, things start to get interesting. We get linked such that 'start' - is the entry symbol. However, it is real low in kernel address space - and as such a nifty place to place the trap table. We achieve this goal - by just jumping to 'dostart' for the first trap's entry as the sparc - never receives the zero trap as it is real special. - - Each trap entry point is the size of 4 sparc instructions (or 4 bytes - * 4 insns = 16 bytes). There are 128 hardware traps (some undefined - or unimplemented) and 128 software traps (ditto). - - One of the instructions must be a branch. More often than not this - will be to a trap handler entry point because it is completely - impossible to handle any trap in 4 insns. I welcome anyone to - challenge this theory. :-) - - On entry into this table the hardware has loaded the program counter - at which the trap occurred into register %l1 and the next program - counter into %l2, this way we can return from the trap with a simple - - jmp %l1; rett %l2 - - after properly servicing the trap. It wouldn't be a bad idea to load - some more information into the local regs since we have technically - 2 or 3 instructions to play with besides the jmp to the 'real' trap - handler (one can even go in the delay slot). For now I am going to put - the %psr (processor status register) and the trap-type value in %l0 - and %l3 respectively. - - TODO: Write cheesy macros to make this table more manageable. - Ugh, this shit is long... - -*/ - - .globl start - .globl _trapbase -start: -_trapbase: - b dostart; nop; nop; nop ! we never get trap #0 it is special - ! TRAP code should go here, TODO :> - -_msgbufmapped: - .word 1 - - -/* The following two things point to window management tables. The first - one is used to quickly look up how many user windows there are from - trap-land. The second is used in a trap handler to determine if a rett - instruction will land us smack inside the invalid window that possibly - the trap was called to fix-up. -*/ - - .data - .skip 32 ! alignment byte & negative indices -lnx_uw: .skip 32 ! u_char uwtab[-31..31]; -lnx_winmask: .skip 32 ! u_char wmask[0..31]; - - .text - - -/* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in - %g7 and at _prom_vector_p. And also quickly check whether we are on - a v0 or v2 prom. -*/ - -dostart: mov %o0, %g7 - st %o0, [_prom_vector_p] ! we will need it later - ld [%g7 + 0x4], %o2 - cmp %o2, 2 ! a v2 prom? - be found_v2 - nop - -/* Old sun4's pass our load address into %o0 instead of the prom - pointer. On sun4's you have to hard code the romvec pointer into - your code. Sun probably still does that because they don't even - trust their own "OpenBoot" specifications. -*/ - - set 0x4000, %g6 - cmp %o0, %g6 ! an old sun4? - beq no_sun4_here - nop - - st %g0, [_prom_iface_vers] ! useless, disappear soon - b not_v2 - nop - -found_v2: - set 0x2, %o5 - st %o5, [_prom_iface_vers] - -not_v2: - -/* Get the machine type via the mysterious romvec node operations. - Here we can find out whether we are on a sun4 sun4c, sun4m, or - a sun4m. The "nodes" are set up as a bunch of n-ary trees which - you can traverse to get information about devices and such. The - information acquisition happens via the node-ops which are defined - in the linux_openprom.h header file. Of particular interest is the - 'nextnode(int node)' function as it does the smart thing when - presented with a value of '0', it gives you the first node in the - tree. These node integers probably offset into some internal prom - pointer table the openboot has. It's completely undocumented, so - I'm not about to go sifting through the prom address space, but may - do so if I get suspicious enough. :-) -*/ - - mov 0, %o0 ! next_node(0) = first_node - ld [%g7 + 0x1c], %o4 - ld [%o4], %o4 - call %o4 - nop - - set _cputypvar, %o1 ! first node has cpu-arch - set _cputypval, %o2 ! information, the string - ld [%g7 + 0x1c], %o4 ! 'compatibility' tells - ld [%o4 + 0x0c], %o4 ! that we want 'sun4x' where - call %o4 ! x is one of '', 'c', 'm', - nop ! 'd' or 'e'. %o2 holds pointer - ! to a buf where above string - ! will get stored by the prom. - set _cputypval, %o2 - ldub [%o2 + 4], %o0 - cmp %o0, 'c' ! we already know we are not - beq is_sun4c ! on a plain sun4 because of - nop ! the check for 0x4000 in %o0 - cmp %o0, 'm' ! at start: - beq is_sun4m - nop - b no_sun4d_here ! god bless the person who - nop ! tried to run this on sun4d - -is_sun4m: -is_sun4c: ! OK, this is a sun4c, yippie - mov %g7, %g6 ! load up the promvec offsets - st %g6, [prom_magic] ! magic mushroom :> - add %g7, 0x4, %g6 - st %g6, [prom_rom_vers] - add %g7, 0x8, %g6 - st %g6, [prom_pluginvers] - add %g7, 0xc, %g6 - st %g6, [prom_revision] - add %g7, 0x10, %g6 - st %g6, [prom_v0mem_desc] - add %g7, 0x1c, %g6 - st %g6, [prom_nodefuncs] - add %g7, 0x20, %g6 - st %g6, [prom_bootstr] - add %g7, 0x24, %g6 - st %g6, [prom_v0devfuncs] - add %g7, 0x48, %g6 - st %g6, [prom_stdin] - add %g7, 0x4c, %g6 - st %g6, [prom_stdout] - add %g7, 0x54, %g6 - st %g6, [prom_putchar] - add %g7, 0x50, %g6 - st %g6, [prom_getchar] - add %g7, 0x5c, %g6 - st %g6, [prom_nputchar] - add %g7, 0x58, %g6 - st %g6, [prom_ngetchar] - add %g7, 0x60, %g6 - st %g6, [prom_putstring] - add %g7, 0x64, %g6 - st %g6, [prom_bootme] - add %g7, 0x68, %g6 - st %g6, [prom_printf] - add %g7, 0x6c, %g6 - st %g6, [prom_abort] - add %g7, 0x70, %g6 - st %g6, [prom_ticks] - add %g7, 0x74, %g6 - st %g6, [prom_halt] - add %g7, 0x78, %g6 - st %g6, [prom_sync] - add %g7, 0x7c, %g6 - st %g6, [prom_eval] - add %g7, 0x80, %g6 - st %g6, [prom_v0bootarg] - add %g7, 0x84, %g6 - st %g6, [prom_ethaddr_func] - add %g7, 0x88, %g6 - st %g6, [prom_v2bootarg] - add %g7, 0x98, %g6 - st %g6, [prom_v2devfunc] - add %g7, 0xc8, %g6 - st %g6, [prom_xtra_array] - add %g7, 0x104, %g6 - st %g6, [prom_setcontext] - -/* That was easy, now lets try to print some message on the screen. - We have to be careful because the prom addressed things weird and - we aren't really mapped into memory as far as the rom routines are - concerned. So all addresses we have ourselves and would like the - prom to actually use must be calculated as (addr - KERNBASE) in order - for anything to work at all. We will map ourselves later before we - call any c-code to avoid this hassle. -*/ - - set boot_msg-KERNBASE, %o0 - ld [prom_printf-KERNBASE], %o2 - ld [%o2], %o1 - call %o1 ! print boot message #1 - nop - -_newline: set newline-KERNBASE, %o0 - ld [prom_printf-KERNBASE], %o2 - ld [%o2], %o1 - call %o1 - nop - - b 0f - nop ! damn delay slots... - -0: nop ! duh - set pstring1-KERNBASE, %o0 - ld [prom_printf-KERNBASE], %o3 - ld [%o3], %o2 - ld [prom_magic-KERNBASE], %o3 - ld [%o3], %o1 - call %o2 - nop; nop; nop - - set pstring2-KERNBASE, %o0 - ld [prom_printf-KERNBASE], %o3 - ld [%o3], %o2 - ld [_prom_iface_vers], %o3 - ld [%o3], %o1 - call %o2 - nop; nop; nop - -/* Print out various bits of memory information. At this point - I just cycle through the documented v0_prom memory lists for - the values. They are linked lists and allow for description of - non-contiguous physical memory configurations, thus the 'memloop' - things to traverse the linked lists. -*/ - -/* Things are different for v0 and v2. v2 requires traversing the node trees - and that really sucks. -*/ - -/* Another Note: - The prom printf() function can take up to 5 arguments in registers - %o1 -- %o5 , the format string goes in %o0. It is your usual libc - printf() believe it or not. -*/ - - cmp %o0, 0x2 - be v2_mem_probe - nop - - set pstring4-KERNBASE, %o0 - ld [prom_printf-KERNBASE], %o5 - ld [%o5], %o4 - ld [_prom_vector_p], %l1 - ld [%l1+16], %l2 - ld [%l2], %l3 - ld [%l3 + 8], %o1 ! 'nbytes' memory accumulator - - ld [_prom_vector_p], %l1 - ld [%l1 + 16], %l2 - ld [%l2], %l3 - ld [%l3], %l4 - -memloop: - cmp %l4, 0 - be mv_to_vmprom ! is there more? - nop - - ld [%l4 + 0x8], %l6 ! apparently so... - add %o1, %l6, %o1 - b memloop - ld [%l4], %l4 - -mv_to_vmprom: - - ld [_prom_vector_p], %l0 - ld [%l0 + 20], %l1 - ld [%l1], %l2 - ld [%l2 + 8], %o2 ! memory accumulator - - ld [_prom_vector_p], %l0 - ld [%l0 + 20], %l1 - ld [%l1], %l2 - ld [%l2], %l4 - -memloop2: - cmp %l4, 0 - be mv_to_vmprom2 ! is there more? - nop - ld [%l4 + 0x8], %l6 ! apparently so... - add %o2, %l6, %o2 - b memloop2 - ld [%l4], %l4 - -mv_to_vmprom2: - - ld [_prom_vector_p], %l0 - ld [%l0 + 24], %l1 - ld [%l1], %l2 - ld [%l2 + 8], %o3 ! memory accumulator - - ld [_prom_vector_p], %l0 - ld [%l0 + 24], %l1 - ld [%l1], %l2 - ld [%l2], %l4 - -memloop3: - cmp %l4, 0 - be mv_to_vmprom3 ! is there more? - nop - ld [%l4 + 0x8], %l6 ! apparently so... - add %o3, %l6, %o3 - b memloop3 - ld [%l4], %l4 - -mv_to_vmprom3: - - call %o4 - nop; nop; nop - - - set newline-KERNBASE, %o0 - ld [prom_printf-KERNBASE], %o2 - ld [%o2], %o1 - call %o1 - nop - - b halt_me - nop - -no_sun4_here: - ld [%g7 + 0x68], %o1 - set sun4_notsup, %o0 - call %o1 - nop - b rest_of_boot ! next stage... - nop - -v2_mem_probe: - set you_lose-KERNBASE, %o0 ! I just print this - ld [prom_printf-KERNBASE], %o1 ! crap to debug my node - ld [%o1], %o2 ! routines :-) - call %o2 - nop - - st %g0, [current_node] - set prop_string_buf, %o2 - or %g0, %g0, %o0 - ld [prop_name], %o1 - or %g0, 31, %o3 - -node_find_loop: - ld [prom_nodefuncs], %o4 - ld [%o4 + 0xc], %o4 - call %o4 - nop - ld [prop_string_buf], %l3 - cmp %l3, 'm' - bne node_find_loop2 - ld [prop_string_buf + 1], %l3 - cmp %l3, 'e' - bne node_find_loop2 - ld [prop_string_buf + 2], %l3 - cmp %l3, 'm' - bne node_find_loop2 - nop - b found_mem_node - nop - -node_find_loop2: - ld [current_node], %o0 ! get next node - ld [prom_nodefuncs], %o1 - ld [%o1], %o1 - call %o1 - nop - st %o0, [current_node] - set prop_string_buf, %o2 - set prop_name, %o1 - b node_find_loop - or %g0, 31, %o3 - -found_mem_node: - set v2_mem_struct-KERNBASE, %o2 - set 0xff, %o3 - set mem_prop_physavail-KERNBASE, %o1 - ld [current_node], %o0 - ld [prom_nodefuncs], %o4 - ld [%o4 + 0xc], %o4 - call %o4 - nop - - set v2_printf_physavail-KERNBASE, %o0 - ld [v2_mem_struct + 0x8], %o1 - ld [prom_printf], %o4 - ld [%o4], %o4 - call %o4 - - set v2_mem_struct-KERNBASE, %o2 - set 0xff, %o3 - set mem_prop_phystot-KERNBASE, %o1 - ld [current_node], %o0 - ld [prom_nodefuncs], %o4 - ld [%o4 + 0xc], %o4 - call %o4 - nop - - set v2_printf_physavail-KERNBASE, %o0 - ld [v2_mem_struct + 0x8], %o1 - ld [prom_printf], %o4 - ld [%o4], %o4 - call %o4 - nop - - b rest_of_boot - nop - -rest_of_boot: - call halt_me - nop ! who cares at this point - -/* There, happy now adrian? */ - -no_sun4d_here: - ld [%g7 + 0x68], %o1 - set sun4d_notsup, %o0 - call %o1 - nop - b halt_me - nop - -halt_me: - ld [%g7 + 0x74], %o0 - call %o0 ! get us out of here... - nop ! apparently solaris is better - -_strlen: - mov %o0, %l1 - mov %g0, %l3 - ldub [%l1], %l2 - sll %l2, 24, %l2 - sra %l2, 24, %l2 -len_loop: - cmp %l2, 0 - be len_loop_end - nop - add %l3, 0x1, %l3 - add %l1, 0x1, %l1 - ldub [%l1], %l2 - sll %l2, 24, %l2 - sra %l2, 24, %l2 - b len_loop - nop - -len_loop_end: - mov %l3, %o0 - ret - nop - - - - - diff -u --recursive --new-file v1.1.76/linux/arch/sparc/boot/boot.h linux/arch/sparc/boot/boot.h --- v1.1.76/linux/arch/sparc/boot/boot.h Tue Dec 6 11:15:28 1994 +++ linux/arch/sparc/boot/boot.h Thu Jan 1 02:00:00 1970 @@ -1,6 +0,0 @@ -#define KERNSIZE 134217728 -#define KERNBASE -134217728 -#define NBPG 4096 -#define UPAGES 2 -#define PROM_BASE -1568768 - diff -u --recursive --new-file v1.1.76/linux/arch/sparc/boot/version.h linux/arch/sparc/boot/version.h --- v1.1.76/linux/arch/sparc/boot/version.h Tue Dec 6 11:15:28 1994 +++ linux/arch/sparc/boot/version.h Thu Jan 1 02:00:00 1970 @@ -1 +0,0 @@ -#define WHO_COMPILED_ME "someone@somewhere.domain" diff -u --recursive --new-file v1.1.76/linux/arch/sparc/config.in linux/arch/sparc/config.in --- v1.1.76/linux/arch/sparc/config.in Thu Jan 1 02:00:00 1970 +++ linux/arch/sparc/config.in Thu Jan 5 09:38:47 1995 @@ -0,0 +1,247 @@ +# +# arch/sparc/config.in +# +# Bare minimum configuration file for the Sparc. +# +# Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) +# +# For a description of the syntax of this configuration file, +# see the Configure script. +# + +comment 'Sparc Kernel setup' + +bool 'Sparc V8 kernel' CONFIG_SPARC_V8 y +bool 'Sparc SMP support' CONFIG_LINUX_SMP n +bool 'Networking support' CONFIG_NET y +bool 'Limit memory to low 16MB' CONFIG_MAX_16M n +bool 'System V IPC' CONFIG_SYSVIPC y + +if [ "$CONFIG_NET" = "y" ]; then +comment 'Networking options' +bool 'TCP/IP networking' CONFIG_INET y +if [ "$CONFIG_INET" "=" "y" ]; then +bool 'IP forwarding/gatewaying' CONFIG_IP_FORWARD n +bool 'IP multicasting (ALPHA)' CONFIG_IP_MULTICAST n +bool 'IP firewalling' CONFIG_IP_FIREWALL n +bool 'IP accounting' CONFIG_IP_ACCT n +comment '(it is safe to leave these untouched)' +bool 'PC/TCP compatibility mode' CONFIG_INET_PCTCP n +bool 'Reverse ARP' CONFIG_INET_RARP n +bool 'Assume subnets are local' CONFIG_INET_SNARL y +bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n +fi +bool 'The IPX protocol' CONFIG_IPX n +#bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n +fi + +comment 'SCSI support' + +bool 'SCSI support?' CONFIG_SCSI n + +if [ "$CONFIG_SCSI" = "n" ]; then + +comment 'Skipping SCSI configuration options...' + +else + +comment 'SCSI support type (disk, tape, CDrom)' + +bool 'Scsi disk support' CONFIG_BLK_DEV_SD y +bool 'Scsi tape support' CONFIG_CHR_DEV_ST n +bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR n +bool 'Scsi generic support' CONFIG_CHR_DEV_SG n + +comment 'SCSI low-level drivers' + +bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n +bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y +bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n +bool 'Adaptec AHA274X/284X support' CONFIG_SCSI_AHA274X n +bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n +bool 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F n +bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n +bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n +if [ "$CONFIG_PCI" = "y" ]; then + bool 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n +fi +bool 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 n +bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 n +bool 'QLOGIC SCSI support' CONFIG_SCSI_QLOGIC n +bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE n +bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 n +bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n +bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n +bool 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA n +#bool 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n +fi + + +if [ "$CONFIG_NET" = "y" ]; then + +comment 'Network device support' + +bool 'Network device support?' CONFIG_NETDEVICES y +if [ "$CONFIG_NETDEVICES" = "n" ]; then + +comment 'Skipping network driver configuration options...' + +else +bool 'Dummy net driver support' CONFIG_DUMMY n +bool 'SLIP (serial line) support' CONFIG_SLIP n +if [ "$CONFIG_SLIP" = "y" ]; then + bool ' CSLIP compressed headers' SL_COMPRESSED y + bool ' 16 channels instead of 4' SL_SLIP_LOTS n +# bool ' SLIP debugging on' SL_DUMP y +fi +bool 'PPP (point-to-point) support' CONFIG_PPP n +bool 'PLIP (parallel port) support' CONFIG_PLIP n +bool 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING n +bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n +bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n +if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then + bool 'WD80*3 support' CONFIG_WD80x3 n + bool 'SMC Ultra support' CONFIG_ULTRA n +fi +bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n +bool '3COM cards' CONFIG_NET_VENDOR_3COM y +if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then + bool '3c501 support' CONFIG_EL1 n + bool '3c503 support' CONFIG_EL2 n + if [ "$CONFIG_NET_ALPHA" = "y" ]; then + bool '3c505 support' CONFIG_ELPLUS n + bool '3c507 support' CONFIG_EL16 n + fi + bool '3c509/3c579 support' CONFIG_EL3 y +fi +bool 'Other ISA cards' CONFIG_NET_ISA n +if [ "$CONFIG_NET_ISA" = "y" ]; then + bool 'Cabletron E21xx support' CONFIG_E2100 n + bool 'DEPCA support' CONFIG_DEPCA n + bool 'EtherWorks 3 support' CONFIG_EWRK3 n + if [ "$CONFIG_NET_ALPHA" = "y" ]; then +# bool 'Arcnet support' CONFIG_ARCNET n + bool 'AT1700 support' CONFIG_AT1700 n +# bool 'EtherExpressPro support' CONFIG_EEXPRESS_PRO n + bool 'EtherExpress support' CONFIG_EEXPRESS n + bool 'NI5210 support' CONFIG_NI52 n + bool 'NI6510 support' CONFIG_NI65 n + fi + bool 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS n + bool 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN n + bool 'NE2000/NE1000 support' CONFIG_NE2000 y + bool 'SK_G16 support' CONFIG_SK_G16 n +fi +bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA n +if [ "$CONFIG_NET_EISA" = "y" ]; then + if [ "$CONFIG_NET_ALPHA" = "y" ]; then + bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n + fi + bool 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n +# bool 'DEC 21040 PCI support' CONFIG_DEC_ELCP n +# bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 n +# bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 n + bool 'Zenith Z-Note support' CONFIG_ZNET y +fi +bool 'Pocket and portable adaptors' CONFIG_NET_POCKET n +if [ "$CONFIG_NET_POCKET" = "y" ]; then + bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n + bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n + bool 'D-Link DE620 pocket adaptor support' CONFIG_DE620 n +# bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA n +# bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN n +# bool '3 Com 3c589 PCMCIA support' CONFIG_3C589 n +fi +fi +fi + +comment 'CD-ROM drivers' + +bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n +bool 'Mitsumi CDROM driver support' CONFIG_MCD n +bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n +if [ "$CONFIG_SBPCD" = "y" ]; then + bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n + if [ "$CONFIG_SBPCD2" = "y" ]; then + bool 'Matsushita/Panasonic third CDROM controller support' CONFIG_SBPCD3 n + if [ "$CONFIG_SBPCD3" = "y" ]; then + bool 'Matsushita/Panasonic fourth CDROM controller support' CONFIG_SBPCD4 n + fi + fi +fi + +comment 'Filesystems' + +bool 'Standard (minix) fs support' CONFIG_MINIX_FS y +bool 'Extended fs support' CONFIG_EXT_FS n +bool 'Second extended fs support' CONFIG_EXT2_FS y +bool 'xiafs filesystem support' CONFIG_XIA_FS n +bool 'msdos fs support' CONFIG_MSDOS_FS y +if [ "$CONFIG_MSDOS_FS" = "y" ]; then +bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n +fi +bool '/proc filesystem support' CONFIG_PROC_FS y +if [ "$CONFIG_INET" = "y" ]; then +bool 'NFS filesystem support' CONFIG_NFS_FS y +fi +if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" ]; then + bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y +else + bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n +fi +bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n +bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n + + +comment 'character devices' + +bool 'Cyclades async mux support' CONFIG_CYCLADES n +bool 'Parallel printer support' CONFIG_PRINTER n +bool 'Logitech busmouse support' CONFIG_BUSMOUSE n +bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n +if [ "$CONFIG_PSMOUSE" = "y" ]; then +bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y +fi +bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n +bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n +bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION n +bool 'VESA Power Saving Protocol Support' CONFIG_VESA_PSPM n +if [ "$CONFIG_VESA_PSPM" = "y" ]; then +bool 'VESA PSPM Force Off' CONFIG_PSPM_FORCE_OFF n +fi + + +bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n +if [ "$CONFIG_QIC02_TAPE" = "y" ]; then +bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y +if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then + +comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!' + +else + +comment '>>> Setting runtime QIC-02 configuration is done with qic02conf' +comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/' + +fi +fi + +bool 'QIC-117 tape support' CONFIG_FTAPE n +if [ "$CONFIG_FTAPE" = "y" ]; then +int ' number of ftape buffers' NR_FTAPE_BUFFERS 3 +fi + +comment 'Sound' + +bool 'Sound card support' CONFIG_SOUND n + +comment 'Kernel hacking' + +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n +bool 'Kernel profiling support' CONFIG_PROFILE n +if [ "$CONFIG_PROFILE" = "y" ]; then + int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 +fi +if [ "$CONFIG_SCSI" = "y" ]; then +bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y +fi diff -u --recursive --new-file v1.1.76/linux/arch/sparc/kernel/Makefile linux/arch/sparc/kernel/Makefile --- v1.1.76/linux/arch/sparc/kernel/Makefile Thu Jan 1 02:00:00 1970 +++ linux/arch/sparc/kernel/Makefile Thu Jan 5 09:38:32 1995 @@ -0,0 +1,46 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o + +OBJS = entry.o traps.o + +all: kernel.o head.o + +head.o: head.s + +head.s: head.S $(TOPDIR)/include/asm-sparc/head.h + $(CPP) -traditional -o $*.s $< + +kernel.o: $(OBJS) + $(LD) -r -o kernel.o $(OBJS) + sync + +dep: + $(CPP) -M *.c > .depend + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + + diff -u --recursive --new-file v1.1.76/linux/arch/sparc/kernel/entry.S linux/arch/sparc/kernel/entry.S --- v1.1.76/linux/arch/sparc/kernel/entry.S Thu Jan 1 02:00:00 1970 +++ linux/arch/sparc/kernel/entry.S Thu Jan 5 09:38:32 1995 @@ -0,0 +1,171 @@ +/* arch/sparc/kernel/entry.S: Sparc trap low-level entry points. + * + * Sparc traps are so ugly, this code is going to go through a lot + * of changes as I find out more interesting things. See head.S for + * the trap table and how it works, this will show you how we get + * to these routines. + * + * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) + */ + +#include + + .text + .align 4 + +/* Default trap handler */ + .globl my_trap_handler +my_trap_handler: + rd %wim, %l4 + or %g0, 0x1, %l5 + sll %l5, %l0, %l5 + cmp %l4, %l5 ! are we in the invalid register? + +#if 0 /* work in progress */ + be wash_trap_win +#endif + + nop + or %%g0, %l3, %o0 + call _do_hw_interrupt + or %%g0, %%g0, %o1 + wr %l0, 0x20, %psr ! re-enable traps and reset the condition codes + nop + nop + nop ! click our heels three times, "no place like home" + jmp %l1 + rett %l2 + +/* This is cheese and only works reliably if not coming from userland. */ + .globl fill_window_entry +fill_window_entry: + wr %g0, 0, %wim ! let us into the invalid window + ! without inducing another trap + restore + nop + nop ! no guarentees until 3 insns later + restore + restore %g0, 1, %l1 + rd %psr, %l0 + sll %l1, %l0, %l0 + wr %l0, 0, %wim + save %g0, %g0, %g0 + +/* load up the window */ + ldd [%sp], %l0 + ldd [%sp + 8], %l2 + ldd [%sp + 16], %l4 + ldd [%sp + 24], %l6 + ldd [%sp + 32], %i0 + ldd [%sp + 40], %i2 + ldd [%sp + 48], %i4 + ldd [%sp + 56], %i6 + + save %g0, %g0, %g0 + save %g0, %g0, %g0 + jmp %l1 + rett %l2 + +/* Cheese number two, give this a bad stack pointer and get scared. */ + .globl spill_window_entry +spill_window_entry: + save %g0, %g0, %g0 ! save into next 'valid' window + std %l0, [%sp] ! acquire some scratch registers + rd %psr, %l0 + or %g0, 0x1, %l1 + sll %l1, %l0, %l0 + wr %l0, 0, %wim ! make window we are saving the 'invalid' one + std %l2, [%sp + 8] + std %l4, [%sp + 16] + std %l6, [%sp + 24] + std %i0, [%sp + 32] + std %i2, [%sp + 40] + std %i4, [%sp + 48] + std %i6, [%sp + 56] + restore ! restore back into the window we want to use + jmp %l1 + rett %l2 ! return from spill handler + + +/* This is where most generic traps enter, the registers should be: + %l0 == %psr + %l1 == %pc + %l2 == %npc + %l4 == trap_type + %l7 == trap_handler +*/ + +trap_entry: + srl %l0, 0x6, %l5 ! shift over to previous priv bit + andcc %l5, 0x1, %g0 ! 1 == from kernel 0 == from user + bz 2f + nop ! we dont handle users yet ;-( + + or %g0, 0x1, %l5 + sll %l5, %l0, %l5 ! a trick, only least 5 bits are + ! significant in a register shift + ! count. + rd %wim, %l6 + andcc %l6, %l5, %g0 ! if (((1< +#include +#include +#include + + .data + +/* First thing to go in the data segment is the interrupt stack. */ + + .globl _intstack + .globl _eintstack +_intstack: + .skip 4 * PAGESIZE ! 16k = 128 128-byte stack frames +_eintstack: + + + +/* + The following are used with the prom_vector node-ops to figure out + the cpu-type +*/ + + .globl _cputyp + +_cputyp: + .word 1 + +_cputypval: + .asciz "sun4c" + .ascii " " + +/* + * Sun people can't spell worth damn. "compatability" indeed. + * At least we *know* we can't spell, and use a spell-checker. + */ + +/* Uh, actually Linus it is I who cannot spell. Too much murky + * Sparc assembly will does this to ya. + */ +_cputypvar: + .asciz "compatability" + +_cputypvallen = _cputypvar - _cputypval + +/* This hold the prom-interface-version number for either v0 or v2. */ + + .align 4 + .globl _prom_iface_vers + +_prom_iface_vers: .skip 4 + +/* WARNING: evil messages follow */ + + .align 4 + +sun4_notsup: + .asciz "Sparc-Linux: sun4 support not implemented yet\n\n" + .align 4 + +sun4m_notsup: + .asciz "Sparc-Linux: sun4m support does not exist\n\n" + .align 4 + +sun4d_notsup: + .asciz "Sparc-Linux: sun4d support does not exist\n\n" + .align 4 + +you_lose: + .asciz "You lose..... Thanks for playing...\n" + .align 4 + + + .globl boot_msg + +/* memory descriptor property strings, v2 = yuk yuk yuk */ +/* XXX how to figure out vm mapped by prom? May have to scan magic addresses */ + +mem_prop_physavail: .asciz "available" +mem_prop_phystot: .asciz "reg" + +/* v2_memory descriptor struct kludged here for assembly, if it ain't broke */ + + .align 4 +v2_mem_struct: .skip 0xff + + .align 4 +v2_printf_physavail: .asciz "Physical Memory Available: 0x%x bytes" +v2_printf_phystot: .asciz "Physical Memory: 0x%x bytes" + +/* A place to store property strings returned from the prom 'node' funcs */ + + .align 4 +prop_string_buf: .skip 32 + +prop_name: .asciz "name" + .align 4 + +current_node: .skip 4 + .align 4 + + +/* nice little boot message */ + +boot_msg: + .ascii "Booting Sparc-Linux V0.00PRE-ALPHA " + .ascii WHO_COMPILED_ME + .asciz " \n" + .align 4 + + .globl boot_msg2 + +boot_msg2: + .asciz "Booting Sparclinux V0.00 PRE-ALPHA on a (SUN4C)\n\n" + + .align 4 + +pstring1: + .asciz "Prom Magic Cookie: 0x%x " + .align 4 + +pstring2: + .asciz "Interface Version: v%d\n" + .align 4 + +pstring3: + .asciz "Prom Revision: V%d\n\n" + .align 4 + +pstring4: + .ascii "Total Physical Memory: %d bytes\nVM mapped by Prom: %d bytes\n" + .asciz "Available Physical Memory: %d bytes\n" + .align 4 + + +newline: + .asciz "\n" + .align 4 + + .text + + .globl _msgbuf +msgbufsize = PAGESIZE ! 1 page for msg buffer +_msgbuf = PAGESIZE + + +IE_reg_addr = _msgbuf + msgbufsize ! this page not used; points to IEreg + + +/* Ok, things start to get interesting. We get linked such that 'start' + is the entry symbol. However, it is real low in kernel address space + and as such a nifty place to place the trap table. We achieve this goal + by just jumping to 'dostart' for the first trap's entry as the sparc + never receives the zero trap as it is real special (hw reset). + + Each trap entry point is the size of 4 sparc instructions (or 4 bytes + * 4 insns = 16 bytes). There are 128 hardware traps (some undefined + or unimplemented) and 128 software traps (sys-calls, etc.). + + One of the instructions must be a branch. More often than not this + will be to a trap handler entry point because it is completely + impossible to handle any trap in 4 insns. I welcome anyone to + challenge this theory. :-) + + On entry into this table the hardware has loaded the program counter + at which the trap occurred into register %l1 and the next program + counter into %l2, this way we can return from the trap with a simple + + jmp %l1; rett %l2 ! poof... + + after properly servicing the trap. It wouldn't be a bad idea to load + some more information into the local regs since we have technically + 2 or 3 instructions to play with besides the jmp to the 'real' trap + handler (one can even go in the delay slot). For now I am going to put + the %psr (processor status register) and the trap-type value in %l0 + and %l3 respectively. Also, for IRQ's I'll put the level in %l4. + +*/ + + .globl start + .globl _trapbase +start: +_trapbase: + b gokernel; WRITE_PAUSE ! we never get trap #0 it is special + + TRAP_ENTRY(0x1, my_trap_handler) /* Instruction Access Exception */ + TRAP_ENTRY(0x2, my_trap_handler) /* Illegal Instruction */ + TRAP_ENTRY(0x3, my_trap_handler) /* Privileged Instruction */ + TRAP_ENTRY(0x4, my_trap_handler) /* Floating Point Disabled */ + TRAP_ENTRY(0x5, spill_window_entry) /* Window Overflow */ + TRAP_ENTRY(0x6, fill_window_entry) /* Window Underflow */ + TRAP_ENTRY(0x7, my_trap_handler) /* Memory Address Not Aligned */ + TRAP_ENTRY(0x8, my_trap_handler) /* Floating Point Exception */ + TRAP_ENTRY(0x9, my_trap_handler) /* Data Miss Exception */ + TRAP_ENTRY(0xa, my_trap_handler) /* Tagged Instruction Overflow */ + TRAP_ENTRY(0xb, my_trap_handler) /* Watchpoint Detected */ + TRAP_ENTRY(0xc, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0xd, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0xe, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0xf, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x10, my_trap_handler) /* Undefined... */ + +/* Level'd interrupt entry points, see macro defs above */ + + TRAP_ENTRY_INTERRUPT_SOFT(1, 0x101) /* Interrupt Level 1 */ + TRAP_ENTRY_INTERRUPT(2) /* Interrupt Level 2 */ + TRAP_ENTRY_INTERRUPT(3) /* Interrupt Level 3 */ + TRAP_ENTRY_INTERRUPT_SOFT(4, 0x104) /* Interrupt Level 4 */ + TRAP_ENTRY_INTERRUPT(5) /* Interrupt Level 5 */ + TRAP_ENTRY_INTERRUPT_SOFT(6, 0x106) /* Interrupt Level 6 */ + TRAP_ENTRY_INTERRUPT(7) /* Interrupt Level 7 */ + TRAP_ENTRY_INTERRUPT(8) /* Interrupt Level 8 */ + TRAP_ENTRY_INTERRUPT(9) /* Interrupt Level 9 */ + TRAP_ENTRY_INTERRUPT(10) /* Interrupt Level 10 */ + TRAP_ENTRY_INTERRUPT(11) /* Interrupt Level 11 */ + TRAP_ENTRY_INTERRUPT(12) /* Interrupt Level 12 */ + TRAP_ENTRY_INTERRUPT(13) /* Interrupt Level 13 */ + TRAP_ENTRY_INTERRUPT(14) /* Interrupt Level 14 */ + TRAP_ENTRY_INTERRUPT_NMI(15, linux_trap_nmi) /* Level 15 (nmi) */ + + TRAP_ENTRY(0x20, my_trap_handler) /* General Register Access Error */ + TRAP_ENTRY(0x21, my_trap_handler) /* Instruction Access Error */ + TRAP_ENTRY(0x22, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x23, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x24, my_trap_handler) /* Co-Processor Disabled */ + TRAP_ENTRY(0x25, my_trap_handler) /* Unimplemented FLUSH inst. */ + TRAP_ENTRY(0x26, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x27, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x28, my_trap_handler) /* Co-Processor Exception */ + TRAP_ENTRY(0x29, my_trap_handler) /* Data Access Error */ + TRAP_ENTRY(0x2a, my_trap_handler) /* Division by zero, you lose... */ + TRAP_ENTRY(0x2b, my_trap_handler) /* Data Store Error */ + TRAP_ENTRY(0x2c, my_trap_handler) /* Data Access MMU-Miss */ + TRAP_ENTRY(0x2d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x2e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x2f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x30, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x31, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x32, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x33, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x34, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x35, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x36, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x37, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x38, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x39, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3a, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3b, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3c, my_trap_handler) /* Instruction Access MMU-Miss */ + TRAP_ENTRY(0x3d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x40, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x41, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x42, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x43, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x44, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x45, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x46, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x47, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x48, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x49, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4a, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4b, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4c, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x50, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x51, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x52, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x53, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x54, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x55, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x56, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x57, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x58, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x59, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5a, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5b, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5c, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x60, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x61, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x62, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x63, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x64, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x65, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x66, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x67, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x68, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x69, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6a, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6b, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6c, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6d, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6e, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6f, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x70, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x71, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x72, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x73, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x74, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x75, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x76, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x77, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x78, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x79, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7a, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7b, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7c, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7d, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7e, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7f, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x80, my_trap_handler) /* SunOS System Call */ + TRAP_ENTRY(0x81, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x82, my_trap_handler) /* Divide by zero trap XXX */ + TRAP_ENTRY(0x83, my_trap_handler) /* Flush Windows Trap XXX */ + TRAP_ENTRY(0x84, my_trap_handler) /* Clean Windows Trap XXX */ + TRAP_ENTRY(0x85, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x86, my_trap_handler) /* Fix Unaligned Access Trap XXX */ + TRAP_ENTRY(0x87, my_trap_handler) /* Integer Overflow Trap XXX */ + TRAP_ENTRY(0x88, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x89, my_trap_handler) /* NetBSD System Call */ + TRAP_ENTRY(0x8a, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8b, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8c, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8d, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8e, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8f, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x90, my_trap_handler) /* SparcLinux System Call */ + TRAP_ENTRY(0x91, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x92, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x93, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x94, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x95, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x96, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x97, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x98, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x99, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9a, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9b, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9c, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9d, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9e, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9f, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xaa, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xab, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xac, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xad, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xae, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xaf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xba, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbe, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xca, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xce, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xda, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xde, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xea, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xeb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xec, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xed, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xee, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xef, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfa, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfe, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xff, my_trap_handler) /* Software Trap */ + +_msgbufmapped: + .word 1 + + +/* The following two things point to window management tables. The first + one is used to quickly look up how many user windows there are from + trap-land. The second is used in a trap handler to determine if a rett + instruction will land us smack inside the invalid window that possibly + the trap was called to fix-up. +*/ + + .data + .skip 32 ! alignment byte & negative indices +lnx_uw: .skip 32 ! u_char uwtab[-31..31]; +lnx_winmask: .skip 32 ! u_char wmask[0..31]; + + .text + + +/* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in + %g7 and at _prom_vector_p. And also quickly check whether we are on + a v0 or v2 prom. +*/ + +gokernel: mov %o0, %g7 + st %o0, [_prom_vector_p] ! we will need it later + rd %psr, %l2 + rd %wim, %l3 + rd %tbr, %l4 + or %g0, %o2, %l5 ! could be prom magic value... + +#if 0 /* You think I'm nutz? */ + cmp %l5, 0x0 ! check for magic SMP pointer + bne nosmp + nop + call %o2 ! call smp prom setup + nop +#endif /* I will be soon... */ + +/* Acquire boot time privileged register values, this will help debugging. + * I figure out and store nwindows later on. + */ + +nosmp: sethi %hi(_boot_psr), %l1 + st %l2, [%l1 + %lo(_boot_psr)] + sethi %hi(_boot_wim), %l1 + st %l3, [%l1 + %lo(_boot_wim)] + sethi %hi(_boot_tbr), %l1 + st %l4, [%l1 + %lo(_boot_tbr)] + sethi %hi(_boot_o_two), %l1 + st %l5, [%l1 + %lo(_boot_smp_ptr)] + + mov %o0, %g7 + sethi %hi(_prom_vector_p), %g5 + st %o0, [%g5 + %lo(_prom_vector_p)] ! we will need it later + + ld [%g7 + 0x4], %o3 + cmp %o3, 2 ! a v2 prom? + be found_v2 + nop + +/* Old sun4's pass our load address into %o0 instead of the prom + pointer. On sun4's you have to hard code the romvec pointer into + your code. Sun probably still does that because they don't even + trust their own "OpenBoot" specifications. +*/ + + sethi %hi(LOAD_ADDR), %g6 + cmp %o0, %g6 ! an old sun4? + beq no_sun4_here + nop + + st %g0, [_prom_iface_vers] + b not_v2 + nop + +found_v2: + or %%g0, 0x2, %o5 + st %o5, [_prom_iface_vers] + +not_v2: + +/* Get the machine type via the mysterious romvec node operations. + Here we can find out whether we are on a sun4 sun4c, sun4m, or + a sun4m. The "nodes" are set up as a bunch of n-ary trees which + you can traverse to get information about devices and such. The + information acquisition happens via the node-ops which are defined + in the linux_openprom.h header file. Of particular interest is the + 'nextnode(int node)' function as it does the smart thing when + presented with a value of '0', it gives you the first node in the + tree. These node integers probably offset into some internal prom + pointer table the openboot has. It's completely undocumented, so + I'm not about to go sifting through the prom address space, but may + do so if I get suspicious enough. :-) +*/ + + or %g0, %g7, %l1 + add %l1, 0x1c, %l1 + ld [%l1], %l0 + ld [%l0], %l0 + call %l0 + or %g0, %g0, %o0 ! next_node(0) = first_node + + set _cputypvar, %o1 ! first node has cpu-arch + set _cputypval, %o2 ! information, the string + ld [%l1], %l0 ! 'compatibility' tells + ld [%l0 + 0x0c], %l0 ! that we want 'sun4x' where + call %l0 ! x is one of '', 'c', 'm', + nop ! 'd' or 'e'. %o2 holds pointer + ! to a buf where above string + ! will get stored by the prom. + + set _cputypval, %o2 ! better safe than sorry + ldub [%o2 + 4], %o0 + cmp %o0, 'c' ! we already know we are not + beq is_sun4c ! on a plain sun4 because of + nop ! the check for 0x4000 in %o0 + cmp %o0, 'm' ! at start: + beq is_sun4m + nop + b no_sun4d_here ! god bless the person who + nop ! tried to run this on sun4d + +is_sun4m: +is_sun4c: ! OK, this is a sun4c, yippie + mov %g7, %g6 ! load up the promvec offsets + sethi %hi(prom_magic), %g5 ! magic mushroom :> + st %g6, [%g5 + %lo(prom_magic)] + add %g7, 0x4, %g6 + sethi %hi(prom_rom_vers), %g5 + st %g6, [%g5 + %lo(prom_rom_vers)] + add %g7, 0x8, %g6 + sethi %hi(prom_pluginvers), %g5 + st %g6, [%g5 + %lo(prom_pluginvers)] + add %g7, 0xc, %g6 + sethi %hi(prom_revision), %g5 + st %g6, [%g5 + %lo(prom_revision)] + add %g7, 0x10, %g6 + sethi %hi(prom_v0mem_desc), %g5 + st %g6, [%g5 + %lo(prom_v0mem_desc)] + add %g7, 0x1c, %g6 + sethi %hi(prom_nodefuncs), %g5 + st %g6, [%g5 + %lo(prom_nodefuncs)] + add %g7, 0x68, %g6 + sethi %hi(prom_printf), %g5 + st %g6, [%g5 + %lo(prom_printf)] + add %g7, 0x6c, %g6 + sethi %hi(prom_abort), %g5 + st %g6, [%g5 + %lo(prom_abort)] + add %g7, 0x74, %g6 + sethi %hi(prom_halt), %g5 + st %g6, [%g5 + %lo(prom_halt)] + add %g7, 0x78, %g6 + sethi %hi(prom_sync), %g5 + st %g6, [%g5 + %lo(prom_sync)] + add %g7, 0x7c, %g6 + sethi %hi(prom_eval), %g5 + st %g6, [%g5 + %lo(prom_eval)] + add %g7, 0x80, %g6 + sethi %hi(prom_v0bootline), %g6 + st %g6, [%g5 + %lo(prom_v0bootline)] + + +/* That was easy, now lets try to print some message on the screen. + We don't have to worry about bad address translations when the prom + addresses our pointers because our pointers are at 0x0-kern_size + as the prom expects. +*/ + + set boot_msg, %o0 + ld [prom_printf], %o1 + ld [%o1], %o1 + call %o1 ! print boot message #1 + nop + +_newline: set newline, %o0 + ld [prom_printf], %o1 + ld [%o1], %o1 + call %o1 + nop + + set pstring1, %o0 + ld [prom_printf], %o2 + ld [%o2], %o2 + ld [prom_magic], %o1 + ld [%o1], %o1 + call %o2 + + set pstring2, %o0 + ld [prom_printf], %o2 + ld [%o2], %o2 + ld [_prom_iface_vers], %o1 + ld [%o1], %o1 + call %o2 + + b halt_me + nop + +no_sun4_here: + ld [%g7 + 0x68], %o1 + set sun4_notsup, %o0 + call %o1 + nop + +rest_of_boot: + mov PAGESHIFT_SUN4C, %g5 + + set AC_CONTEXT, %g1 ! kernel context, safe now + ! the only valid context + ! until we call paging_init() + stba %g0, [%g1] ASI_CONTROL + + +/* I make the kernel image sit in memory relative to 0x0 with the text + * starting at 0x4000. Now it looks like the way memory is set in Linux + * on an ix86. + */ + +/* Uh, oh, interrupt time. This crap is real confusing. What I want to do is + clear all interrupts, map the interrupt enable register which in effect + enables non-maskable interrupts (or NMI's). Actuall we take no interrupts + until we frob with the %tbr (trap base register) which the prom has set + to all its routines which allows some sanity during bootup. +*/ + +#if 0 /* paranoid, need to fix this routine now */ + sethi %hi(IE_reg_addr), %l0 + or %l0, %lo(IE_reg_addr), %l0 + sethi %hi(INT_ENABLE_REG_PHYSADR), %l2 + or %l2, %lo(INT_ENABLE_REG_PHYSADR), %l2 + srl %l2, %g5, %l1 + + sta %l1, [%l0] ASI_PTE + mov INTS_ALL_ENAB, %l1 + stb %l1, [%l0] +#endif /* paranoid, see above */ + +/* Aieee, now set PC and nPC, enable traps, give ourselves a stack and it's + show-time! +*/ + + set 1f, %g1 + jmp %g1 + nop + + .align 4 +1: sethi %hi(_cputyp), %o0 + st %g4, [%o0 + %lo(_cputyp)] + + sethi %hi(_pgshift), %o0 + st %g5, [%o0 + %lo(_pgshift)] + + mov 1, %o0 + sll %o0, %g5, %g5 + sethi %hi(_nbpg), %o0 + st %g5, [%o0 + %lo(_nbpg)] + + sub %g5, 1, %g5 + sethi %hi(_pgofset), %o0 + st %g5, [%o0 + %lo(_pgofset)] + + + rd %psr, %g3 + andn %g3, PSR_ET, %g3 + wr %g3, 0, %psr ! make sure traps are off + ! before we play around + WRITE_PAUSE ! no guarentees until 3 insns + + + wr %g0, 0, %wim ! magical invalid window reg + WRITE_PAUSE ! see above + + +/* I keep the timer interrupt on so that BogoMIPS works and the prom + * keeps updating it's "jiffies" counter. 100HZ clock on sparcstations. + */ + wr %g0, (PSR_S | PSR_PS | PSR_PIL), %psr + WRITE_PAUSE + + wr %g0, 2, %wim ! window 1 invalid + WRITE_PAUSE + mov 1, %g1 + sethi %hi(_task + PCB_WIM), %g2 + st %g1, [%g2 + %lo(_task + PCB_WIM)] + +/* I want a kernel stack NOW! */ + + set USRSTACK - C_STACK, %fp + set estack0 - C_STACK - 80, %sp + rd %psr, %l0 + wr %l0, PSR_ET, %psr + WRITE_PAUSE + + +/* + Maybe the prom zero's out our BSS section, maybe it doesn't. I certainly + don't know, do you? +*/ + + set _edata, %o0 + set _end, %o1 + sub %o1, %o0, %g2 + sethi %hi(_kernel_bss_len), %g3 + st %g2, [%g3 + %lo(_kernel_bss_len)] + sethi %hi(_trapbase), %g3 + or %g3, %lo(_trapbase), %g3 + sethi %hi(_etext), %g4 + or %g4, %lo(_etext), %g4 + sub %g4, %g3, %g2 + sethi %hi(_kernel_text_len), %g3 + st %g2, [%g3 + %lo(_kernel_text_len)] + sethi %hi(_etext), %g4 + or %g4, %lo(_etext), %g4 + sethi %hi(_edata), %g3 + or %g3, %lo(_edata), %g3 + sub %g3, %g4, %g2 + sethi %hi(_kernel_data_len), %g3 + st %g2, [%g3 + %lo(_kernel_data_len)] + clr %g1 + +1: + st %g0, [%o0] + add %o0, 0x4, %o0 + cmp %o0, %o1 + bl 1b + nop + +/* Compute NWINDOWS and stash it away. Now uses %wim trick explained + * in the V8 manual. Ok, this method seems to work, sparc is cool... + */ + + sethi %hi(0xffffffff), %g1 + rd %wim, %g2 ! save current value + or %g1, %lo(0xffffffff), %g1 + wr %g1, 0x0, %wim + rd %wim, %g1 ! get remaining mask + wr %g2, 0x0, %wim ! restore old value + WRITE_PAUSE + + or %g0, 0x0, %g3 + +1: srl %g1, 0x1, %g1 ! shift until highest + cmp %g1, 0x0 ! bit set + bne 1b + add %g3, 0x1, %g3 + sethi %hi(_nwindows), %g4 + st %g3, [%g4 + %lo(_nwindows)] ! store final value + + +/* Here we go */ + +/* start_kernel() wants the command line args at empty_zero_page, copy + * the boot command line from the prom data struct here... + */ + +/* I still haven't gotten this right yet... hack hack hack */ + +#if 0 + sethi %hi(prom_v0bootline), %g6 + ld [%g6 + %lo(prom_v0bootline)], %g6 + ld [%g6], %g6 + ld [%g6], %g6 + sethi %hi(_empty_zero_page + 2048), %g2 + ld [%g2 + %lo(_empty_zero_page + 2048)], %g2 + ld [%g6], %g3 ! argv[0] + or %g0, 0x8, %g1 ! argv counter +1: ld [%g3], %g4 + st %g4, [%g2] + add %g3, 0x4, %g3 + cmp %g4, 0 + bne,a 1b + add %g2, 0x4, %g2 + + or %g0, %lo(' '), %g4 + st %g4, [%g2] + sub %g1, 0x1, %g1 + add %g3, 0x4, %g3 + cmp %g1, 0 + bne 1b + add %g2, 0x4, %g2 +#endif + + sethi %hi(_prom_vector_p), %g5 + ld [%g5 + %lo(_prom_vector_p)], %o0 + call _start_kernel + nop + + call halt_me + nop + +/* There, happy now adrian? */ + +no_sun4d_here: + ld [%g7 + 0x68], %o1 + set sun4d_notsup, %o0 + call %o1 + nop + b halt_me + nop + +halt_me: + ld [%g7 + 0x74], %o0 + call %o0 ! get us out of here... + nop ! apparently solaris is better + + .data + .align 4 + +/* + Fill up the prom vector, note in particular the kind first element, + no joke. I don't need all of them in here as the entire prom vector + gets initialized in c-code so all routines can use it. +*/ + + .globl _prom_vector_p + +_prom_vector_p: .skip 4 +prom_magic: .skip 4 ! magic mushroom, beware... +prom_rom_vers: .skip 4 ! interface version (v0 or v2) +prom_pluginvers: .skip 4 ! XXX help help help ??? +prom_revision: .skip 4 ! PROM revision (ie. 1.4) +prom_halt: .skip 4 ! void halt(void) solaris friend +prom_eval: .skip 4 ! void eval(int len, char* string) +prom_v0bootline: .skip 4 ! boot command line +prom_v0mem_desc: .skip 4 ! V0 memory descriptor list ptr. +prom_nodefuncs: .skip 4 ! Magical Node functions +prom_printf: .skip 4 ! minimal printf() + +/* The prom_abort pointer MUST be mapped in all contexts, because if you + don't then if a user process is running when you press the abort key + sequence, all sorts of bad things can happen +*/ + +prom_abort: .skip 4 ! "L1-A" magic cookie + ! must be mapped in ALL contexts + +/* prom_sync is a place where the kernel should place a pointer to a kernel + function that when called will sync all pending information to the drives + and then promptly return. If the kernel gets aborted with 'L1-A' one can + give the 'sync' command to the boot prompt and this magic cookie gets + executed. Nice feature eh? +*/ + +prom_sync: .skip 4 ! hook in prom for "sync" func + + .align 4 + +/* Boot time priviledged register values, plus magic %o2 value */ + + .globl _boot_wim + .globl _boot_psr + .globl _boot_tbr + .globl _boot_o_two +_boot_wim: .skip 4 +_boot_psr: .skip 4 +_boot_tbr: .skip 4 +_boot_smp_ptr: .skip 4 + diff -u --recursive --new-file v1.1.76/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c --- v1.1.76/linux/arch/sparc/kernel/process.c Thu Jan 1 02:00:00 1970 +++ linux/arch/sparc/kernel/process.c Thu Jan 5 09:38:32 1995 @@ -0,0 +1,105 @@ +/* + * linux/arch/i386/kernel/process.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +void ret_from_sys_call(void) { __asm__("nop"); } + +/* + * The idle loop on a i386.. + */ +asmlinkage int sys_idle(void) +{ + int i; + + if (current->pid != 0) + return -EPERM; + + /* Map out the low memory: it's no longer needed */ + /* Sparc version RSN */ + + /* endless idle loop with no priority at all */ + current->counter = -100; + for (;;) { + if (!need_resched) + __asm__("nop"); + schedule(); + } +} + +/* + * Do necessary setup to start up a newly executed thread. + */ +void start_thread(struct pt_regs * regs, unsigned long sp, unsigned long fp) +{ + regs->sp = sp; + regs->fp = fp; + regs->psr = psr; +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ + return; /* i'm getting to it */ +} + +void flush_thread(void) +{ + return; +} + +unsigned long copy_thread(int nr, unsigned long clone_flags, struct task_struct * p, struct pt_regs * regs) +{ + struct pt_regs * childregs; + + childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1; + p->tss.sp = (unsigned long) childregs; + *childregs = *regs; + p->tss.back_link = 0; + p->tss.psr = regs->psr; /* for condition codes */ + return clone_flags; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + return; /* solaris does this enough */ +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(struct pt_regs regs) +{ + int error; + char * filename; + + error = do_execve(filename, (char **) regs.reg_window[0], + (char **) regs.reg_window[1], ®s); + putname(filename); + return error; +} diff -u --recursive --new-file v1.1.76/linux/arch/sparc/kernel/traps.c linux/arch/sparc/kernel/traps.c --- v1.1.76/linux/arch/sparc/kernel/traps.c Thu Jan 1 02:00:00 1970 +++ linux/arch/sparc/kernel/traps.c Thu Jan 5 09:38:32 1995 @@ -0,0 +1,39 @@ +/* + * arch/sparc/kernel/traps.c + * + * Copyright 1994 David S. Miller (davem@caip.rutgers.edu) + */ + +/* + * I hate traps on the sparc, grrr... + */ + + +void do_hw_interrupt(unsigned long type, unsigned long vector) +{ + if (vector == 14) { + jiffies++; + return; + } + + /* Just print garbage for everything else for now. */ + + printk("Unimplemented Sparc TRAP, vector = %lx type = %lx\n", vector, type); + + return; +} + +extern unsigned int *trapbase; + +void trap_init(void) +{ + + /* load up the trap table */ + + __asm__("wr %0, 0x0, %%tbr\n\t" + "nop; nop; nop\n\t" : : + "r" (trapbase)); + + return; +} + diff -u --recursive --new-file v1.1.76/linux/arch/sparc/mm/vac-flush.c linux/arch/sparc/mm/vac-flush.c --- v1.1.76/linux/arch/sparc/mm/vac-flush.c Thu Jan 1 02:00:00 1970 +++ linux/arch/sparc/mm/vac-flush.c Thu Jan 5 09:38:36 1995 @@ -0,0 +1,93 @@ +/* vac.c: Routines for flushing various amount of the Sparc VAC + (virtual address cache). + + Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) +*/ + +#include + +/* Flush all VAC entries for the current context */ + +extern int do_hw_vac_flushes, vac_size, vac_linesize; +extern int vac_entries_per_context, vac_entries_per_segment; +extern int vac_entries_per_page; + +void +flush_vac_context() +{ + register int entries_left, offset; + register char* address; + + entries_left = vac_entries_per_context; + address = (char *) 0; + + if(do_hw_vac_flushes) + { + while(entries_left-- >=0) + { + hw_flush_vac_context_entry(address); + address += 4096; + } + } + else + { + offset = vac_linesize; + while(entries_left-- >=0) + { + sw_flush_vac_context_entry(address); + address += offset; + } + } +} + +void +flush_vac_segment(register unsigned int segment) +{ + register int entries_left, offset; + register char* address; + + entries_left = vac_entries_per_segment; + __asm__ __volatile__("sll %0, 18, %1\n\t" + "sra %1, 0x2, %1\n\t" + : "=r" (segment) : "0" (address)); + + if(do_hw_vac_flushes) + { + while(entries_left-- >=0) + { + hw_flush_vac_segment_entry(address); + address += 4096; + } + } + else + { + offset = vac_linesize; + while(entries_left-- >=0) + { + sw_flush_vac_segment_entry(address); + address += offset; + } + } +} + +void +flush_vac_page(register unsigned int addr) +{ + register int entries_left, offset; + + if(do_hw_vac_flushes) + { + hw_flush_vac_page_entry(addr); + } + else + { + entries_left = vac_entries_per_page; + offset = vac_linesize; + while(entries_left-- >=0) + { + sw_flush_vac_page_entry(addr); + addr += offset; + } + } +} + diff -u --recursive --new-file v1.1.76/linux/arch/sparc/string.S linux/arch/sparc/string.S --- v1.1.76/linux/arch/sparc/string.S Wed Dec 7 07:21:24 1994 +++ linux/arch/sparc/string.S Thu Jan 1 02:00:00 1970 @@ -1,115 +0,0 @@ -/* string.h: Efficient string functions in sparc-assembly for - the linux kernel. - - Copyright 1994 (c) David S. Miller (davem@caip.rutgers.edu) -*/ - - -/* If we are smart we will use only the output and global registers - as that will allow us to avoid a window save which would be nice. -*/ - -/* Believe it or not the following strlen is not optimized enough! - In the future I may play games with doing word reads and reducing - the per-word comparisons to *one*, yes I have seen it done. -*/ - .align 4 - .globl _strlen -_strlen: - mov %o0, %g3 ! leaf-procedure optimization, here - ldsb [%g3], %g2 ! I only use the register sent to me - cmp %g2, 0 ! and the globals. Now, this routine - be 1f ! is callable from boot code. - nop - add %o0, 1, %o0 -0: ldsb [%o0], %g2 - cmp %g2, 0 - bne,a 0b ! annulling branch, yuck - add %o0, 1, %o0 - -1: retl - sub %o0, %g3, %o0 ! since %g3 holds the original pointer - ! and %o0 is at the end byte, we can - ! subtract and the result is strlen. - -/* String concatenate function. I am too lazy to honor the third count - argument at this time. Once again, this could be optimized so much - more to use word accesses instead of slooow byte loads. -*/ - .align 4 - .globl _strcat -_strcat: - mov %o0, %g4 - ldsb [%g4], %g3 - cmp %g3, 0 - be,a 2f - ldub [%o1], %g3 - add %o0, 1, %o0 - -0: ldsb [%o0], %g3 - cmp %g3, 0 - bne,a 0b - add %o0, 1, %o0 - -1: ldub [%o1], %g3 - -2: add %o1, 1, %o1 - stb %g3, [%o0] - cmp %g3, 0 - bne 1b - add %o0, 1, %o0 - retl - mov %g4, %o0 - -/* Aieee, this code is starting to give me a headache. I shouldn't - have tried to do this in one sitting :-( -*/ - - .align 4 - .globl _strcmp -_strcmp: b 2f - ldsb [%o1], %g4 - -0: sll %o2, 24, %g3 - cmp %g3, 0 - bne 1f - add %o0, 1, %o0 - b 3f - or %g0, %g0, %o0 - -1: ldsb [%o1], %g4 - -2: ldsb [%o0], %g3 - add %o1, 1, %o1 - cmp %g3, %g4 - be 0b - mov %g3, %o2 - ldub [%o2], %g3 - ldub [%o1-1], %o0 ! oh man, no joke - sub %g2, %o0, %o0 - -3: retl - nop - -/* Ok, strcpy() should be easy enough. Maybe I catch some sleep after - this one.... -*/ - .align 4 - .globl _strcpy -_strcpy: ldub [%o1], %g3 - mov %o0, %g4 - cmp %g3, 0 - be 1f - stb %g3, [%g4] - -0: add %o1, 1, %o1 - ldub [%o1], %g3 - add %o0, 1, %o0 - cmp %g3, 0 - bne 0b - stb %g3, [%o0] - -1: retl - mov %g4, %o0 - - diff -u --recursive --new-file v1.1.76/linux/arch/sparc/vac-flush.c linux/arch/sparc/vac-flush.c --- v1.1.76/linux/arch/sparc/vac-flush.c Mon Dec 19 17:39:00 1994 +++ linux/arch/sparc/vac-flush.c Thu Jan 1 02:00:00 1970 @@ -1,93 +0,0 @@ -/* vac.c: Routines for flushing various amount of the Sparc VAC - (virtual address cache). - - Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) -*/ - -#include - -/* Flush all VAC entries for the current context */ - -extern int do_hw_vac_flushes, vac_size, vac_linesize; -extern int vac_entries_per_context, vac_entries_per_segment; -extern int vac_entries_per_page; - -void -flush_vac_context() -{ - register int entries_left, offset; - register char* address; - - entries_left = vac_entries_per_context; - address = (char *) 0; - - if(do_hw_vac_flushes) - { - while(entries_left-- >=0) - { - hw_flush_vac_context_entry(address); - address += 4096; - } - } - else - { - offset = vac_linesize; - while(entries_left-- >=0) - { - sw_flush_vac_context_entry(address); - address += offset; - } - } -} - -void -flush_vac_segment(register unsigned int segment) -{ - register int entries_left, offset; - register char* address; - - entries_left = vac_entries_per_segment; - __asm__ __volatile__("sll %0, 18, %1\n\t" - "sra %1, 0x2, %1\n\t" - : "=r" (segment) : "0" (address)); - - if(do_hw_vac_flushes) - { - while(entries_left-- >=0) - { - hw_flush_vac_segment_entry(address); - address += 4096; - } - } - else - { - offset = vac_linesize; - while(entries_left-- >=0) - { - sw_flush_vac_segment_entry(address); - address += offset; - } - } -} - -void -flush_vac_page(register unsigned int addr) -{ - register int entries_left, offset; - - if(do_hw_vac_flushes) - { - hw_flush_vac_page_entry(addr); - } - else - { - entries_left = vac_entries_per_page; - offset = vac_linesize; - while(entries_left-- >=0) - { - sw_flush_vac_page_entry(addr); - addr += offset; - } - } -} - diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/Makefile linux/drivers/FPU-emu/Makefile --- v1.1.76/linux/drivers/FPU-emu/Makefile Mon Aug 1 08:19:12 1994 +++ linux/drivers/FPU-emu/Makefile Thu Jan 1 02:00:00 1970 @@ -1,50 +0,0 @@ -# -# Makefile for wm-FPU-emu -# - -#DEBUG = -DDEBUGGING -DEBUG = -PARANOID = -DPARANOID -CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin - -.c.o: - $(CC) $(CFLAGS) $(MATH_EMULATION) -c $< - -.S.o: - $(CC) -D__ASSEMBLER__ $(PARANOID) -c $< - -.s.o: - $(CC) -c $< - -OBJS = fpu_entry.o div_small.o errors.o \ - fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \ - load_store.o get_address.o \ - poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \ - reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o \ - reg_div.o reg_mul.o reg_norm.o \ - reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \ - reg_round.o \ - wm_shrx.o wm_sqrt.o \ - div_Xsig.o polynom_Xsig.o round_Xsig.o \ - shr_Xsig.o mul_Xsig.o - -math.a: $(OBJS) - rm -f math.a - $(AR) rcs math.a $(OBJS) - sync - -dep: - $(CPP) -M *.c > .depend - $(CPP) -D__ASSEMBLER__ -M *.S >> .depend - -proto: - cproto -e -DMAKING_PROTO *.c >fpu_proto.h - -dummy: - -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/README linux/drivers/FPU-emu/README --- v1.1.76/linux/drivers/FPU-emu/README Fri Aug 19 08:54:01 1994 +++ linux/drivers/FPU-emu/README Thu Jan 1 02:00:00 1970 @@ -1,436 +0,0 @@ - +---------------------------------------------------------------------------+ - | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | This program is free software; you can redistribute it and/or modify | - | it under the terms of the GNU General Public License version 2 as | - | published by the Free Software Foundation. | - | | - | This program is distributed in the hope that it will be useful, | - | but WITHOUT ANY WARRANTY; without even the implied warranty of | - | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | - | GNU General Public License for more details. | - | | - | You should have received a copy of the GNU General Public License | - | along with this program; if not, write to the Free Software | - | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | - | | - +---------------------------------------------------------------------------+ - - - -wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387 -which was my 80387 emulator for early versions of 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). Unfortunately, numerous -facets of the functioning of the FPU are not well covered in the -Reference Manual. The information in the manual has been supplemented -with measurements on real 80486's. Unfortunately, it is simply not -possible to be sure that all of the peculiarities of the 80486 have -been discovered, so there is always likely to be obscure differences -in the detailed behaviour of the emulator and a real 80486. - -wm-FPU-emu does not implement all of the behaviour of the 80486 FPU, -but is very close. See "Limitations" later in this file for a list of -some differences. - -Please report bugs, etc to me at: - billm@vaxc.cc.monash.edu.au - or at: - billm@jacobi.maths.monash.edu.au - - ---Bill Metzenthen - August 1994 - - ------------------------ 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. -(5) The argument reducing code for the trig function effectively uses - a value of pi which is accurate to more than 128 bits. As a consequence, - the reduced argument is accurate to more than 64 bits for arguments up - to a few pi, and accurate to more than 64 bits for most arguments, - even for arguments approaching 2^63. This is far superior to an - 80486, which uses a value of pi which is accurate to 66 bits. - -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 perhaps changing static -variables. 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 -As from version 1.12 of the emulator, no static variables are used -(apart from those in the kernel's per-process tables). The emulator is -therefore now fully re-entrant, rather than having just the restricted -form of re-entrancy which is required by the Linux kernel. - ------------------------ Limitations of wm-FPU-emu ----------------------- - -There are a number of differences between the current wm-FPU-emu -(version 1.20) and the 80486 FPU (apart from bugs). Some of the more -important differences are listed below: - -The Roundup flag does not have much meaning for the transcendental -functions and its 80486 value with these functions is likely to differ -from its emulator value. - -In a few rare cases the Underflow flag obtained with the emulator will -be different from that obtained with an 80486. This occurs when the -following conditions apply simultaneously: -(a) the operands have a higher precision than the current setting of the - precision control (PC) flags. -(b) the underflow exception is masked. -(c) the magnitude of the exact result (before rounding) is less than 2^-16382. -(d) the magnitude of the final result (after rounding) is exactly 2^-16382. -(e) the magnitude of the exact result would be exactly 2^-16382 if the - operands were rounded to the current precision before the arithmetic - operation was performed. -If all of these apply, the emulator will set the Underflow flag but a real -80486 will not. - -NOTE: Certain formats of Extended Real are UNSUPPORTED. They are -unsupported by the 80486. They are the Pseudo-NaNs, Pseudoinfinities, -and Unnormals. None of these will be generated by an 80486 or by the -emulator. Do not use them. The emulator treats them differently in -detail from the way an 80486 does. - -The emulator treats PseudoDenormals differently from an 80486. These -numbers are in fact properly normalised numbers with the exponent -offset by 1, and the emulator treats them as such. Unlike the 80486, -the emulator does not generate a Denormal Operand exception for these -numbers. The arithmetical results produced when using such a number as -an operand are the same for the emulator and a real 80486 (apart from -any slight precision difference for the transcendental functions). -Neither the emulator nor an 80486 produces one of these numbers as the -result of any arithmetic operation. An 80486 can keep one of these -numbers in an FPU register with its identity as a PseudoDenormal, but -the emulator will not; they are always converted to a valid number. - -Self modifying code can cause the emulator to fail. An example of such -code is: - movl %esp,[%ebx] - fld1 -The FPU instruction may be (usually will be) loaded into the pre-fetch -queue of the cpu before the mov instruction is executed. If the -destination of the 'movl' overlaps the FPU instruction then the bytes -in the prefetch queue and memory will be inconsistent when the FPU -instruction is executed. The emulator will be invoked but will not be -able to find the instruction which caused the device-not-present -exception. For this case, the emulator cannot emulate the behaviour of -an 80486DX. - -Handling of the address size override prefix byte (0x67) has not been -extensively tested yet. A major problem exists because using it in -vm86 mode can cause a general protection fault. Address offsets -greater than 0xffff appear to be illegal in vm86 mode but are quite -acceptable (and work) in real mode. A small test program developed to -check the addressing, and which runs successfully in real mode, -crashes dosemu under Linux and also brings Windows down with a general -protection fault message when run under the MS-DOS prompt of Windows -3.1. (The program simply reads data from a valid address). - -The emulator supports 16-bit protected mode, with one difference from -an 80486DX. A 80486DX will allow some floating point instructions to -write a few bytes below the lowest address of the stack. The emulator -will not allow this in 16-bit protected mode: no instructions are -allowed to write outside the bounds set by the protection. - ------------------------ 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 ----------------------- - - -The accuracy of the emulator is in almost all cases equal to or better -than that of an Intel 80486 FPU. - -The results of the basic arithmetic functions (+,-,*,/), and fsqrt -match those of an 80486 FPU. They are the best possible; the error for -these never exceeds 1/2 an lsb. The fprem and fprem1 instructions -return exact results; they have no error. - - -The following table compares the emulator accuracy for the sqrt(), -trig and log functions against the Turbo C "emulator". For this table, -each function was tested at about 400 points. Ideal worst-case results -would be 64 bits. The reduced Turbo C accuracy of cos() and tan() for -arguments greater than pi/4 can be thought of as being related 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. - - -Function Tested x range Worst result Turbo C - (relative bits) - -sqrt(x) 1 .. 2 64.1 63.2 -atan(x) 1e-10 .. 200 64.2 62.8 -cos(x) 0 .. pi/2-(1e-10) 64.4 (x <= pi/4) 62.4 - 64.1 (x = pi/2-(1e-10)) 31.9 -sin(x) 1e-10 .. pi/2 64.0 62.8 -tan(x) 1e-10 .. pi/2-(1e-10) 64.0 (x <= pi/4) 62.1 - 64.1 (x = pi/2-(1e-10)) 31.9 -exp(x) 0 .. 1 63.1 ** 62.9 -log(x) 1+1e-6 .. 2 63.8 ** 62.1 - -** The accuracy for exp() and log() is low because the FPU (emulator) -does not compute them directly; two operations are required. - - -The emulator passes the "paranoia" tests (compiled with gcc 2.3.3 or -later) 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). - -The code for reducing the argument for the trig functions (fsin, fcos, -fptan and fsincos) has been improved and now effectively uses a value -for pi which is accurate to more than 128 bits precision. As a -consequence, the accuracy of these functions for large arguments has -been dramatically improved (and is now very much better than an 80486 -FPU). There is also now no degradation of accuracy for fcos and fptan -for operands close to pi/2. Measured results are (note that the -definition of accuracy has changed slightly from that used for the -above table): - -Function Tested x range Worst result - (absolute bits) - -cos(x) 0 .. 9.22e+18 62.0 -sin(x) 1e-16 .. 9.22e+18 62.1 -tan(x) 1e-16 .. 9.22e+18 61.8 - -It is possible with some effort to find very large arguments which -give much degraded precision. For example, the integer number - 8227740058411162616.0 -is within about 10e-7 of a multiple of pi. To find the tan (for -example) of this number to 64 bits precision it would be necessary to -have a value of pi which had about 150 bits precision. The FPU -emulator computes the result to about 42.6 bits precision (the correct -result is about -9.739715e-8). On the other hand, an 80486 FPU returns -0.01059, which in relative terms is hopelessly inaccurate. - -For arguments close to critical angles (which occur at multiples of -pi/2) the emulator is more accurate than an 80486 FPU. For very large -arguments, the emulator is far more accurate. - - -Prior to version 1.20 of the emulator, the accuracy of the results for -the transcendental functions (in their principal range) was not as -good as the results from an 80486 FPU. From version 1.20, the accuracy -has been considerably improved and these functions now give measured -worst-case results which are better than the worst-case results given -by an 80486 FPU. - -The following table gives the measured results for the emulator. The -number of randomly selected arguments in each case is about half a -million. The group of three columns gives the frequency of the given -accuracy in number of times per million, thus the second of these -columns shows that an accuracy of between 63.80 and 63.89 bits was -found at a rate of 133 times per one million measurements for fsin. -The results show that the fsin, fcos and fptan instructions return -results which are in error (i.e. less accurate than the best possible -result (which is 64 bits)) for about one per cent of all arguments -between -pi/2 and +pi/2. The other instructions have a lower -frequency of results which are in error. The last two columns give -the worst accuracy which was found (in bits) and the approximate value -of the argument which produced it. - - frequency (per M) - ------------------- --------------- -instr arg range # tests 63.7 63.8 63.9 worst at arg - bits bits bits bits ------ ------------ ------- ---- ---- ----- ----- -------- -fsin (0,pi/2) 547756 0 133 10673 63.89 0.451317 -fcos (0,pi/2) 547563 0 126 10532 63.85 0.700801 -fptan (0,pi/2) 536274 11 267 10059 63.74 0.784876 -fpatan 4 quadrants 517087 0 8 1855 63.88 0.435121 (4q) -fyl2x (0,20) 541861 0 0 1323 63.94 1.40923 (x) -fyl2xp1 (-.293,.414) 520256 0 0 5678 63.93 0.408542 (x) -f2xm1 (-1,1) 538847 4 481 6488 63.79 0.167709 - - -Tests performed on an 80486 FPU showed results of lower accuracy. The -following table gives the results which were obtained with an AMD -486DX2/66 (other tests indicate that an Intel 486DX produces -identical results). The tests were basically the same as those used -to measure the emulator (the values, being random, were in general not -the same). The total number of tests for each instruction are given -at the end of the table, in case each about 100k tests were performed. -Another line of figures at the end of the table shows that most of the -instructions return results which are in error for more than 10 -percent of the arguments tested. - -The numbers in the body of the table give the approx number of times a -result of the given accuracy in bits (given in the left-most column) -was obtained per one million arguments. For three of the instructions, -two columns of results are given: * The second column for f2xm1 gives -the number cases where the results of the first column were for a -positive argument, this shows that this instruction gives better -results for positive arguments than it does for negative. * In the -cases of fcos and fptan, the first column gives the results when all -cases where arguments greater than 1.5 were removed from the results -given in the second column. Unlike the emulator, an 80486 FPU returns -results of relatively poor accuracy for these instructions when the -argument approaches pi/2. The table does not show those cases when the -accuracy of the results were less than 62 bits, which occurs quite -often for fsin and fptan when the argument approaches pi/2. This poor -accuracy is discussed above in relation to the Turbo C "emulator", and -the accuracy of the value of pi. - - -bits f2xm1 f2xm1 fpatan fcos fcos fyl2x fyl2xp1 fsin fptan fptan -62.0 0 0 0 0 437 0 0 0 0 925 -62.1 0 0 10 0 894 0 0 0 0 1023 -62.2 14 0 0 0 1033 0 0 0 0 945 -62.3 57 0 0 0 1202 0 0 0 0 1023 -62.4 385 0 0 10 1292 0 23 0 0 1178 -62.5 1140 0 0 119 1649 0 39 0 0 1149 -62.6 2037 0 0 189 1620 0 16 0 0 1169 -62.7 5086 14 0 646 2315 10 101 35 39 1402 -62.8 8818 86 0 984 3050 59 287 131 224 2036 -62.9 11340 1355 0 2126 4153 79 605 357 321 1948 -63.0 15557 4750 0 3319 5376 246 1281 862 808 2688 -63.1 20016 8288 0 4620 6628 511 2569 1723 1510 3302 -63.2 24945 11127 10 6588 8098 1120 4470 2968 2990 4724 -63.3 25686 12382 69 8774 10682 1906 6775 4482 5474 7236 -63.4 29219 14722 79 11109 12311 3094 9414 7259 8912 10587 -63.5 30458 14936 393 13802 15014 5874 12666 9609 13762 15262 -63.6 32439 16448 1277 17945 19028 10226 15537 14657 19158 20346 -63.7 35031 16805 4067 23003 23947 18910 20116 21333 25001 26209 -63.8 33251 15820 7673 24781 25675 24617 25354 24440 29433 30329 -63.9 33293 16833 18529 28318 29233 31267 31470 27748 29676 30601 - -Per cent with error: - 30.9 3.2 18.5 9.8 13.1 11.6 17.4 -Total arguments tested: - 70194 70099 101784 100641 100641 101799 128853 114893 102675 102675 - - -------------------------- Contributors ------------------------------- - -A number of people have contributed to the development of the -emulator, often by just reporting bugs, sometimes with suggested -fixes, 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 may have forgotten, please forgive 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 -Bruce Evans, bde@kralizec.zeta.org.au -Timo Korvola, Timo.Korvola@hut.fi -Rick Lyons, rick@razorback.brisnet.org.au -Rick, jrs@world.std.com - -...and numerous others who responded to my request for help with -a real 80486. - diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/control_w.h linux/drivers/FPU-emu/control_w.h --- v1.1.76/linux/drivers/FPU-emu/control_w.h Wed Feb 16 13:07:55 1994 +++ linux/drivers/FPU-emu/control_w.h Thu Jan 1 02:00:00 1970 @@ -1,45 +0,0 @@ -/*---------------------------------------------------------------------------+ - | control_w.h | - | | - | Copyright (C) 1992,1993 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - +---------------------------------------------------------------------------*/ - -#ifndef _CONTROLW_H_ -#define _CONTROLW_H_ - -#ifdef __ASSEMBLER__ -#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) -#define PR_RESERVED_BITS _Const_(0x100) -/* FULL_PRECISION simulates all exceptions masked */ -#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f) - -#endif _CONTROLW_H_ diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/div_Xsig.S linux/drivers/FPU-emu/div_Xsig.S --- v1.1.76/linux/drivers/FPU-emu/div_Xsig.S Mon Aug 1 08:19:13 1994 +++ linux/drivers/FPU-emu/div_Xsig.S Thu Jan 1 02:00:00 1970 @@ -1,369 +0,0 @@ - .file "div_Xsig.S" -/*---------------------------------------------------------------------------+ - | div_Xsig.S | - | | - | Division subroutine for 96 bit quantities | - | | - | Copyright (C) 1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | Divide the 96 bit quantity pointed to by a, by that pointed to by b, and | - | put the 96 bit result at the location d. | - | | - | The result may not be accurate to 96 bits. It is intended for use where | - | a result better than 64 bits is required. The result should usually be | - | good to at least 94 bits. | - | The returned result is actually divided by one half. This is done to | - | prevent overflow. | - | | - | .aaaaaaaaaaaaaa / .bbbbbbbbbbbbb -> .dddddddddddd | - | | - | void div_Xsig(Xsig *a, Xsig *b, Xsig *dest) | - | | - +---------------------------------------------------------------------------*/ - -#include "exception.h" -#include "fpu_asm.h" - - -#define XsigLL(x) (x) -#define XsigL(x) 4(x) -#define XsigH(x) 8(x) - - -#ifndef NON_REENTRANT_FPU -/* - Local storage on the stack: - Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 - */ -#define FPU_accum_3 -4(%ebp) -#define FPU_accum_2 -8(%ebp) -#define FPU_accum_1 -12(%ebp) -#define FPU_accum_0 -16(%ebp) -#define FPU_result_3 -20(%ebp) -#define FPU_result_2 -24(%ebp) -#define FPU_result_1 -28(%ebp) - -#else -.data -/* - Local storage in a static area: - Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 - */ - .align 2,0 -FPU_accum_3: - .long 0 -FPU_accum_2: - .long 0 -FPU_accum_1: - .long 0 -FPU_accum_0: - .long 0 -FPU_result_3: - .long 0 -FPU_result_2: - .long 0 -FPU_result_1: - .long 0 -#endif NON_REENTRANT_FPU - - -.text - .align 2,144 - -.globl _div_Xsig - -_div_Xsig: - pushl %ebp - movl %esp,%ebp -#ifndef NON_REENTRANT_FPU - subl $28,%esp -#endif NON_REENTRANT_FPU - - pushl %esi - pushl %edi - pushl %ebx - - movl PARAM1,%esi /* pointer to num */ - movl PARAM2,%ebx /* pointer to denom */ - -#ifdef PARANOID - testl $0x80000000, XsigH(%ebx) /* Divisor */ - je L_bugged -#endif PARANOID - - -/*---------------------------------------------------------------------------+ - | Divide: Return arg1/arg2 to 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 | - | | - +---------------------------------------------------------------------------*/ - - /* Save extended dividend in local register */ - - /* Divide by 2 to prevent overflow */ - clc - movl XsigH(%esi),%eax - rcrl %eax - movl %eax,FPU_accum_3 - movl XsigL(%esi),%eax - rcrl %eax - movl %eax,FPU_accum_2 - movl XsigLL(%esi),%eax - rcrl %eax - movl %eax,FPU_accum_1 - movl $0,%eax - rcrl %eax - movl %eax,FPU_accum_0 - - movl FPU_accum_2,%eax /* Get the current num */ - movl FPU_accum_3,%edx - -/*----------------------------------------------------------------------*/ -/* Initialization done. - Do the first 32 bits. */ - - /* We will divide by a number which is too large */ - movl XsigH(%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,FPU_result_3 /* Put the result in the answer */ - - mull XsigH(%ebx) /* mul by the ms dw of the denom */ - - subl %eax,FPU_accum_2 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_3 - - movl FPU_result_3,%eax /* Get the result back */ - mull XsigL(%ebx) /* now mul the ls dw of the denom */ - - subl %eax,FPU_accum_1 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_2 - sbbl $0,FPU_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 FPU_result_3 /* Correct the answer */ - - movl XsigL(%ebx),%eax - movl XsigH(%ebx),%edx - subl %eax,FPU_accum_1 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_2 - -#ifdef PARANOID - sbbl $0,FPU_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, FPU_accum_0 not used from now on */ -LDo_2nd_32_bits: - movl FPU_accum_2,%edx /* get the reduced num */ - movl FPU_accum_1,%eax - - /* need to check for possible subsequent overflow */ - cmpl XsigH(%ebx),%edx - jb LDo_2nd_div - ja LPrevent_2nd_overflow - - cmpl XsigL(%ebx),%eax - jb LDo_2nd_div - -LPrevent_2nd_overflow: -/* The numerator is greater or equal, would cause overflow */ - /* prevent overflow */ - subl XsigL(%ebx),%eax - sbbl XsigH(%ebx),%edx - movl %edx,FPU_accum_2 - movl %eax,FPU_accum_1 - - incl FPU_result_3 /* 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,FPU_result_2 /* Put the result in the answer */ - - mull XsigH(%ebx) /* mul by the ms dw of the denom */ - - subl %eax,FPU_accum_1 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_2 - -#ifdef PARANOID - jc L_bugged_2 -#endif PARANOID - - movl FPU_result_2,%eax /* Get the result back */ - mull XsigL(%ebx) /* now mul the ls dw of the denom */ - - subl %eax,FPU_accum_0 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */ - sbbl $0,FPU_accum_2 - -#ifdef PARANOID - jc L_bugged_2 -#endif PARANOID - - jz LDo_3rd_32_bits - -#ifdef PARANOID - cmpl $1,FPU_accum_2 - jne L_bugged_2 -#endif PARANOID - - /* need to subtract another once of the denom */ - movl XsigL(%ebx),%eax - movl XsigH(%ebx),%edx - subl %eax,FPU_accum_0 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_1 - sbbl $0,FPU_accum_2 - -#ifdef PARANOID - jc L_bugged_2 - jne L_bugged_2 -#endif PARANOID - - addl $1,FPU_result_2 /* Correct the answer */ - adcl $0,FPU_result_3 - -#ifdef PARANOID - jc L_bugged_2 /* Must check for non-zero result here */ -#endif PARANOID - -/*----------------------------------------------------------------------*/ -/* The division is essentially finished here, we just need to perform - tidying operations. - Deal with the 3rd 32 bits */ -LDo_3rd_32_bits: - /* We use an approximation for the third 32 bits. - To take account of the 3rd 32 bits of the divisor - (call them del), we subtract del * (a/b) */ - - movl FPU_result_3,%eax /* a/b */ - mull XsigLL(%ebx) /* del */ - - subl %edx,FPU_accum_1 - - /* A borrow indicates that the result is negative */ - jnb LTest_over - - movl XsigH(%ebx),%edx - addl %edx,FPU_accum_1 - - subl $1,FPU_result_2 /* Adjust the answer */ - sbbl $0,FPU_result_3 - - /* The above addition might not have been enough, check again. */ - movl FPU_accum_1,%edx /* get the reduced num */ - cmpl XsigH(%ebx),%edx /* denom */ - jb LDo_3rd_div - - movl XsigH(%ebx),%edx - addl %edx,FPU_accum_1 - - subl $1,FPU_result_2 /* Adjust the answer */ - sbbl $0,FPU_result_3 - jmp LDo_3rd_div - -LTest_over: - movl FPU_accum_1,%edx /* get the reduced num */ - - /* need to check for possible subsequent overflow */ - cmpl XsigH(%ebx),%edx /* denom */ - jb LDo_3rd_div - - /* prevent overflow */ - subl XsigH(%ebx),%edx - movl %edx,FPU_accum_1 - - addl $1,FPU_result_2 /* Reflect the subtraction in the answer */ - adcl $0,FPU_result_3 - -LDo_3rd_div: - movl FPU_accum_0,%eax - movl FPU_accum_1,%edx - divl XsigH(%ebx) - - movl %eax,FPU_result_1 /* Rough estimate of third word */ - - movl PARAM3,%esi /* pointer to answer */ - - movl FPU_result_1,%eax - movl %eax,XsigLL(%esi) - movl FPU_result_2,%eax - movl %eax,XsigL(%esi) - movl FPU_result_3,%eax - movl %eax,XsigH(%esi) - -L_exit: - popl %ebx - popl %edi - popl %esi - - leave - ret - - -#ifdef PARANOID -/* The logic is wrong if we got here */ -L_bugged: - pushl EX_INTERNAL|0x240 - call EXCEPTION - pop %ebx - jmp L_exit - -L_bugged_1: - pushl EX_INTERNAL|0x241 - call EXCEPTION - pop %ebx - jmp L_exit - -L_bugged_2: - pushl EX_INTERNAL|0x242 - call EXCEPTION - pop %ebx - jmp L_exit -#endif PARANOID diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/div_small.S linux/drivers/FPU-emu/div_small.S --- v1.1.76/linux/drivers/FPU-emu/div_small.S Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/div_small.S Thu Jan 1 02:00:00 1970 @@ -1,50 +0,0 @@ - .file "div_small.S" -/*---------------------------------------------------------------------------+ - | div_small.S | - | | - | Divide a 64 bit integer by a 32 bit integer & return remainder. | - | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | 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 -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/errors.c linux/drivers/FPU-emu/errors.c --- v1.1.76/linux/drivers/FPU-emu/errors.c Mon Aug 1 08:19:13 1994 +++ linux/drivers/FPU-emu/errors.c Thu Jan 1 02:00:00 1970 @@ -1,671 +0,0 @@ -/*---------------------------------------------------------------------------+ - | errors.c | - | | - | The error handling functions for wm-FPU-emu | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | 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 - -#include - -#include "fpu_system.h" -#include "exception.h" -#include "fpu_emu.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; - unsigned long address = FPU_ORIG_EIP; - - RE_ENTRANT_CHECK_OFF; - /* No need to verify_area(), we have previously fetched these bytes. */ - printk("Unimplemented FPU Opcode at eip=%p : ", (void *) address); - if ( FPU_CS == USER_CS ) - { - while ( 1 ) - { - byte1 = get_fs_byte((unsigned char *) address); - if ( (byte1 & 0xf8) == 0xd8 ) break; - printk("[%02x]", byte1); - address++; - } - printk("%02x ", byte1); - FPU_modrm = get_fs_byte(1 + (unsigned char *) address); - - if (FPU_modrm >= 0300) - printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); - else - printk("/%d\n", (FPU_modrm >> 3) & 7); - } - else - { - printk("cs selector = %04x\n", FPU_CS); - } - - RE_ENTRANT_CHECK_ON; - - EXCEPTION(EX_Invalid); - -} - - -/* - Called for opcodes which are illegal and which are known to result in a - SIGILL with a real 80486. - */ -void FPU_illegal(void) -{ - math_abort(FPU_info,SIGILL); -} - - - -void emu_printall() -{ - int i; - static char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR", - "DeNorm", "Inf", "NaN", "Empty" }; - unsigned char byte1, FPU_modrm; - unsigned long address = FPU_ORIG_EIP; - - RE_ENTRANT_CHECK_OFF; - /* No need to verify_area(), we have previously fetched these bytes. */ - printk("At %p:", (void *) address); - if ( FPU_CS == USER_CS ) - { -#define MAX_PRINTED_BYTES 20 - for ( i = 0; i < MAX_PRINTED_BYTES; i++ ) - { - byte1 = get_fs_byte((unsigned char *) address); - if ( (byte1 & 0xf8) == 0xd8 ) - { - printk(" %02x", byte1); - break; - } - printk(" [%02x]", byte1); - address++; - } - if ( i == MAX_PRINTED_BYTES ) - printk(" [more..]\n"); - else - { - FPU_modrm = get_fs_byte(1 + (unsigned char *) address); - - if (FPU_modrm >= 0300) - printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); - else - printk(" /%d, mod=%d rm=%d\n", - (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7); - } - } - else - { - printk("%04x\n", FPU_CS); - } - - partial_status = status_word(); - -#ifdef DEBUGGING -if ( partial_status & SW_Backward ) printk("SW: backward compatibility\n"); -if ( partial_status & SW_C3 ) printk("SW: condition bit 3\n"); -if ( partial_status & SW_C2 ) printk("SW: condition bit 2\n"); -if ( partial_status & SW_C1 ) printk("SW: condition bit 1\n"); -if ( partial_status & SW_C0 ) printk("SW: condition bit 0\n"); -if ( partial_status & SW_Summary ) printk("SW: exception summary\n"); -if ( partial_status & SW_Stack_Fault ) printk("SW: stack fault\n"); -if ( partial_status & SW_Precision ) printk("SW: loss of precision\n"); -if ( partial_status & SW_Underflow ) printk("SW: underflow\n"); -if ( partial_status & SW_Overflow ) printk("SW: overflow\n"); -if ( partial_status & SW_Zero_Div ) printk("SW: divide by zero\n"); -if ( partial_status & SW_Denorm_Op ) printk("SW: denormalized operand\n"); -if ( partial_status & SW_Invalid ) printk("SW: invalid operation\n"); -#endif DEBUGGING - - printk(" SW: b=%d st=%ld es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n", - partial_status & 0x8000 ? 1 : 0, /* busy */ - (partial_status & 0x3800) >> 11, /* stack top pointer */ - partial_status & 0x80 ? 1 : 0, /* Error summary status */ - partial_status & 0x40 ? 1 : 0, /* Stack flag */ - partial_status & SW_C3?1:0, partial_status & SW_C2?1:0, /* cc */ - partial_status & SW_C1?1:0, partial_status & SW_C0?1:0, /* cc */ - partial_status & SW_Precision?1:0, partial_status & SW_Underflow?1:0, - partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0, - partial_status & SW_Denorm_Op?1:0, partial_status & SW_Invalid?1:0); - -printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld 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: -#if 0 - printk("st(%d) %c .0000 0000 0000 0000 ", - i, r->sign ? '-' : '+'); - break; -#endif - case TW_Valid: - case TW_NaN: -/* case TW_Denormal: */ - case TW_Infinity: - printk("st(%d) %c .%04lx %04lx %04lx %04lx e%+-6ld ", i, - r->sign ? '-' : '+', - (long)(r->sigh >> 16), - (long)(r->sigh & 0xFFFF), - (long)(r->sigl >> 16), - (long)(r->sigl & 0xFFFF), - r->exp - EXP_BIAS + 1); - break; - default: - printk("Whoops! Error in errors.c "); - break; - } - printk("%s\n", tag_desc[(int) (unsigned) r->tag]); - } - -#ifdef OBSOLETE - printk("[data] %c .%04lx %04lx %04lx %04lx e%+-6ld ", - 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); - printk("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]); -#endif OBSOLETE - RE_ENTRANT_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 fpu_etc.c - 0x1nn in a *.c file: - 0x101 in reg_add_sub.c - 0x102 in reg_mul.c - 0x104 in poly_atan.c - 0x105 in reg_mul.c - 0x107 in fpu_trig.c - 0x108 in reg_compare.c - 0x109 in reg_compare.c - 0x110 in reg_add_sub.c - 0x111 in fpe_entry.c - 0x112 in fpu_trig.c - 0x113 in errors.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 - 0x125 in fpu_trig.c - 0x126 in fpu_entry.c - 0x127 in poly_2xm1.c - 0x128 in fpu_entry.c - 0x129 in fpu_entry.c - 0x130 in get_address.c - 0x131 in get_address.c - 0x132 in get_address.c - 0x133 in get_address.c - 0x140 in load_store.c - 0x141 in load_store.c - 0x150 in poly_sin.c - 0x151 in poly_sin.c - 0x160 in reg_ld_str.c - 0x161 in reg_ld_str.c - 0x162 in reg_ld_str.c - 0x163 in reg_ld_str.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 - 0x220 in reg_norm.S - 0x221 in reg_norm.S - 0x230 in reg_round.S - 0x231 in reg_round.S - 0x232 in reg_round.S - 0x233 in reg_round.S - 0x234 in reg_round.S - 0x235 in reg_round.S - 0x236 in reg_round.S - 0x240 in div_Xsig.S - 0x241 in div_Xsig.S - 0x242 in div_Xsig.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! */ - partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward); - } - else - { - /* Extract only the bits which we use to set the status word */ - n &= (SW_Exc_Mask); - /* Set the corresponding exception bit */ - partial_status |= n; - /* Set summary bits iff exception isn't masked */ - if ( partial_status & ~control_word & CW_Exceptions ) - partial_status |= (SW_Summary | SW_Backward); - 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. */ - partial_status &= ~SW_C1; - } - } - - RE_ENTRANT_CHECK_OFF; - if ( (~control_word & n & CW_Exceptions) || (n == EX_INTERNAL) ) - { -#ifdef PRINT_MESSAGES - /* My message from the sponsor */ - printk(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 - printk("FP Exception: %s!\n", exception_names[i].name); -#endif PRINT_MESSAGES - } - else - printk("FPU emulator: Unknown Exception: 0x%04x!\n", n); - - if ( n == EX_INTERNAL ) - { - printk("FPU 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; */ - } - RE_ENTRANT_CHECK_ON; - -#ifdef __DEBUG__ - math_abort(FPU_info,SIGFPE); -#endif __DEBUG__ - -} - - -/* Real operation attempted on two operands, one a NaN. */ -/* Returns nz if the exception is unmasked */ -asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest) -{ - FPU_REG const *x; - int signalling; - - /* The default result for the case of two "equal" NaNs (signs may - differ) is chosen to reproduce 80486 behaviour */ - x = a; - if (a->tag == TW_NaN) - { - if (b->tag == TW_NaN) - { - signalling = !(a->sigh & b->sigh & 0x40000000); - /* find the "larger" */ - if ( significand(a) < significand(b) ) - 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 0; - } - - 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 !(control_word & CW_Invalid); -} - - -/* Invalid arith operation on Valid registers */ -/* Returns nz if the exception is unmasked */ -asmlinkage int arith_invalid(FPU_REG *dest) -{ - - EXCEPTION(EX_Invalid); - - if ( control_word & CW_Invalid ) - { - /* The masked response */ - reg_move(&CONST_QNaN, dest); - } - - return !(control_word & CW_Invalid); - -} - - -/* Divide a finite number by zero */ -asmlinkage int 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 !(control_word & CW_ZeroDiv); - -} - - -/* This may be called often, so keep it lean */ -int set_precision_flag(int flags) -{ - if ( control_word & CW_Precision ) - { - partial_status &= ~(SW_C1 & flags); - partial_status |= flags; /* The masked response */ - return 0; - } - else - { - exception(flags); - return 1; - } -} - - -/* This may be called often, so keep it lean */ -asmlinkage void set_precision_flag_up(void) -{ - if ( control_word & CW_Precision ) - partial_status |= (SW_Precision | SW_C1); /* The masked response */ - else - exception(EX_Precision | SW_C1); - -} - - -/* This may be called often, so keep it lean */ -asmlinkage void set_precision_flag_down(void) -{ - if ( control_word & CW_Precision ) - { /* The masked response */ - partial_status &= ~SW_C1; - partial_status |= SW_Precision; - } - else - exception(EX_Precision); -} - - -asmlinkage int denormal_operand(void) -{ - if ( control_word & CW_Denormal ) - { /* The masked response */ - partial_status |= SW_Denorm_Op; - return 0; - } - else - { - exception(EX_Denormal); - return 1; - } -} - - -asmlinkage int 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)); - } - - EXCEPTION(EX_Overflow); - if ( control_word & CW_Overflow ) - { - /* The overflow exception is masked. */ - /* By definition, precision is lost. - The roundup bit (C1) is also set because we have - "rounded" upwards to Infinity. */ - EXCEPTION(EX_Precision | SW_C1); - return !(control_word & CW_Precision); - } - - return !(control_word & CW_Overflow); - -} - - -asmlinkage int arith_underflow(FPU_REG *dest) -{ - - if ( control_word & CW_Underflow ) - { - /* The masked response */ - if ( dest->exp <= EXP_UNDER - 63 ) - { - reg_move(&CONST_Z, dest); - partial_status &= ~SW_C1; /* Round down. */ - } - } - else - { - /* Add the magic number to the exponent. */ - dest->exp += (3 * (1 << 13)); - } - - EXCEPTION(EX_Underflow); - if ( control_word & CW_Underflow ) - { - /* The underflow exception is masked. */ - EXCEPTION(EX_Precision); - return !(control_word & CW_Precision); - } - - return !(control_word & CW_Underflow); - -} - - -void stack_overflow(void) -{ - - if ( control_word & CW_Invalid ) - { - /* The masked response */ - top--; - reg_move(&CONST_QNaN, &st(0)); - } - - EXCEPTION(EX_StackOver); - - return; - -} - - -void stack_underflow(void) -{ - - if ( control_word & CW_Invalid ) - { - /* The masked response */ - reg_move(&CONST_QNaN, &st(0)); - } - - 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 -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/exception.h linux/drivers/FPU-emu/exception.h --- v1.1.76/linux/drivers/FPU-emu/exception.h Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/exception.h Thu Jan 1 02:00:00 1970 @@ -1,53 +0,0 @@ -/*---------------------------------------------------------------------------+ - | exception.h | - | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - +---------------------------------------------------------------------------*/ - -#ifndef _EXCEPTION_H_ -#define _EXCEPTION_H_ - - -#ifdef __ASSEMBLER__ -#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 */ - - -#define PRECISION_LOST_UP Const_((EX_Precision | SW_C1)) -#define PRECISION_LOST_DOWN Const_(EX_Precision) - - -#ifndef __ASSEMBLER__ - -#ifdef DEBUG -#define EXCEPTION(x) { printk("exception in %s at line %d\n", \ - __FILE__, __LINE__); exception(x); } -#else -#define EXCEPTION(x) exception(x) -#endif - -#endif __ASSEMBLER__ - -#endif _EXCEPTION_H_ diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/fpu_arith.c linux/drivers/FPU-emu/fpu_arith.c --- v1.1.76/linux/drivers/FPU-emu/fpu_arith.c Thu Jun 2 10:28:23 1994 +++ linux/drivers/FPU-emu/fpu_arith.c Thu Jan 1 02:00:00 1970 @@ -1,179 +0,0 @@ -/*---------------------------------------------------------------------------+ - | 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 billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -#include "fpu_system.h" -#include "fpu_emu.h" -#include "control_w.h" -#include "status_w.h" - - -void fadd__() -{ - /* fadd st,st(i) */ - clear_C1(); - reg_add(&st(0), &st(FPU_rm), &st(0), control_word); -} - - -void fmul__() -{ - /* fmul st,st(i) */ - clear_C1(); - reg_mul(&st(0), &st(FPU_rm), &st(0), control_word); -} - - - -void fsub__() -{ - /* fsub st,st(i) */ - clear_C1(); - reg_sub(&st(0), &st(FPU_rm), &st(0), control_word); -} - - -void fsubr_() -{ - /* fsubr st,st(i) */ - clear_C1(); - reg_sub(&st(FPU_rm), &st(0), &st(0), control_word); -} - - -void fdiv__() -{ - /* fdiv st,st(i) */ - clear_C1(); - reg_div(&st(0), &st(FPU_rm), &st(0), control_word); -} - - -void fdivr_() -{ - /* fdivr st,st(i) */ - clear_C1(); - reg_div(&st(FPU_rm), &st(0), &st(0), control_word); -} - - - -void fadd_i() -{ - /* fadd st(i),st */ - clear_C1(); - reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); -} - - -void fmul_i() -{ - /* fmul st(i),st */ - clear_C1(); - reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); -} - - -void fsubri() -{ - /* fsubr st(i),st */ - /* This is the sense of the 80486 manual - reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */ - clear_C1(); - reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); -} - - -void fsub_i() -{ - /* fsub st(i),st */ - /* This is the sense of the 80486 manual - reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */ - clear_C1(); - reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); -} - - -void fdivri() -{ - /* fdivr st(i),st */ - clear_C1(); - reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); -} - - -void fdiv_i() -{ - /* fdiv st(i),st */ - clear_C1(); - reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); -} - - - -void faddp_() -{ - /* faddp st(i),st */ - clear_C1(); - if ( !reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) - pop(); -} - - -void fmulp_() -{ - /* fmulp st(i),st */ - clear_C1(); - if ( !reg_mul(&st(0), &st(FPU_rm), &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), &st(0), &st(FPU_rm), control_word); */ - clear_C1(); - if ( !reg_sub(&st(0), &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(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */ - clear_C1(); - if ( !reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) - pop(); -} - - -void fdivrp() -{ - /* fdivrp st(i),st */ - clear_C1(); - if ( !reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) - pop(); -} - - -void fdivp_() -{ - /* fdivp st(i),st */ - clear_C1(); - if ( !reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) - pop(); -} - diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/fpu_asm.h linux/drivers/FPU-emu/fpu_asm.h --- v1.1.76/linux/drivers/FPU-emu/fpu_asm.h Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/fpu_asm.h Thu Jan 1 02:00:00 1970 @@ -1,30 +0,0 @@ -/*---------------------------------------------------------------------------+ - | fpu_asm.h | - | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - +---------------------------------------------------------------------------*/ - -#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 -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/fpu_aux.c linux/drivers/FPU-emu/fpu_aux.c --- v1.1.76/linux/drivers/FPU-emu/fpu_aux.c Thu Jun 2 10:28:23 1994 +++ linux/drivers/FPU-emu/fpu_aux.c Thu Jan 1 02:00:00 1970 @@ -1,184 +0,0 @@ -/*---------------------------------------------------------------------------+ - | fpu_aux.c | - | | - | Code to implement some of the FPU auxiliary instructions. | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -#include "fpu_system.h" -#include "exception.h" -#include "fpu_emu.h" -#include "status_w.h" -#include "control_w.h" - - -static void fnop(void) -{ -} - -void fclex(void) -{ - partial_status &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision| - SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op| - SW_Invalid); - no_ip_update = 1; -} - -/* Needs to be externally visible */ -void finit() -{ - int r; - control_word = 0x037f; - partial_status = 0; - top = 0; /* We don't keep top in the status word internally. */ - for (r = 0; r < 8; r++) - { - regs[r].tag = TW_Empty; - } - /* The behaviour is different to that detailed in - Section 15.1.6 of the Intel manual */ - operand_address.offset = 0; - operand_address.selector = 0; - instruction_address.offset = 0; - instruction_address.selector = 0; - instruction_address.opcode = 0; - no_ip_update = 1; -} - -/* - * These are nops on the i387.. - */ -#define feni fnop -#define fdisi fnop -#define fsetpm fnop - -static FUNC const finit_table[] = { - feni, fdisi, fclex, finit, - fsetpm, FPU_illegal, FPU_illegal, FPU_illegal -}; - -void finit_() -{ - (finit_table[FPU_rm])(); -} - - -static void fstsw_ax(void) -{ - *(short *) &FPU_EAX = status_word(); - no_ip_update = 1; -} - -static FUNC const fstsw_table[] = { - fstsw_ax, FPU_illegal, FPU_illegal, FPU_illegal, - FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal -}; - -void fstsw_() -{ - (fstsw_table[FPU_rm])(); -} - - -static FUNC const fp_nop_table[] = { - fnop, FPU_illegal, FPU_illegal, FPU_illegal, - FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal -}; - -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 & CW_Invalid ) - { - /* The masked response */ - stack_underflow(); - } - else - EXCEPTION(EX_StackUnder); - } - -} - - -void fxch_i() -{ - /* fxch st(i) */ - FPU_REG t; - register FPU_REG *sti_ptr = &st(FPU_rm), *st0_ptr = &st(0); - - if ( st0_ptr->tag == TW_Empty ) - { - if ( sti_ptr->tag == TW_Empty ) - { - stack_underflow(); - stack_underflow_i(FPU_rm); - return; - } - if ( control_word & CW_Invalid ) - reg_move(sti_ptr, st0_ptr); /* Masked response */ - stack_underflow_i(FPU_rm); - return; - } - if ( sti_ptr->tag == TW_Empty ) - { - if ( control_word & CW_Invalid ) - reg_move(st0_ptr, sti_ptr); /* Masked response */ - stack_underflow(); - return; - } - clear_C1(); - reg_move(st0_ptr, &t); - reg_move(sti_ptr, 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(&st(0), &st(FPU_rm)); -} - - -void fstp_i() -{ - /* fstp st(i) */ - reg_move(&st(0), &st(FPU_rm)); - pop(); -} - diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/fpu_emu.h linux/drivers/FPU-emu/fpu_emu.h --- v1.1.76/linux/drivers/FPU-emu/fpu_emu.h Mon Aug 1 08:19:13 1994 +++ linux/drivers/FPU-emu/fpu_emu.h Thu Jan 1 02:00:00 1970 @@ -1,171 +0,0 @@ -/*---------------------------------------------------------------------------+ - | fpu_emu.h | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - +---------------------------------------------------------------------------*/ - - -#ifndef _FPU_EMU_H_ -#define _FPU_EMU_H_ - -/* - * Define DENORM_OPERAND to make the emulator detect denormals - * and use the denormal flag of the status word. Note: this only - * affects the flag and corresponding interrupt, the emulator - * will always generate denormals and operate upon them as required. - */ -#define DENORM_OPERAND - -/* - * Define PECULIAR_486 to get a closer approximation to 80486 behaviour, - * rather than behaviour which appears to be cleaner. - * 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 __ASSEMBLER__ -#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 EXP_Infinity EXP_OVER -#define EXP_NaN EXP_OVER - -#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 */ - - -#ifndef __ASSEMBLER__ - -#include -#include - -/* -#define RE_ENTRANT_CHECKING - */ - -#ifdef RE_ENTRANT_CHECKING -extern char emulating; -# define RE_ENTRANT_CHECK_OFF emulating = 0 -# define RE_ENTRANT_CHECK_ON emulating = 1 -#else -# define RE_ENTRANT_CHECK_OFF -# define RE_ENTRANT_CHECK_ON -#endif RE_ENTRANT_CHECKING - -#define FWAIT_OPCODE 0x9b -#define OP_SIZE_PREFIX 0x66 -#define ADDR_SIZE_PREFIX 0x67 -#define PREFIX_CS 0x2e -#define PREFIX_DS 0x3e -#define PREFIX_ES 0x26 -#define PREFIX_SS 0x36 -#define PREFIX_FS 0x64 -#define PREFIX_GS 0x65 -#define PREFIX_REPE 0xf3 -#define PREFIX_REPNE 0xf2 -#define PREFIX_LOCK 0xf0 -#define PREFIX_CS_ 1 -#define PREFIX_DS_ 2 -#define PREFIX_ES_ 3 -#define PREFIX_FS_ 4 -#define PREFIX_GS_ 5 -#define PREFIX_SS_ 6 -#define PREFIX_DEFAULT 7 - -struct address { - unsigned int offset; - unsigned int selector:16; - unsigned int opcode:11; - unsigned int empty:5; -}; -typedef void (*FUNC)(void); -typedef struct fpu_reg FPU_REG; -typedef void (*FUNC_ST0)(FPU_REG *st0_ptr); -typedef struct { unsigned char address_size, operand_size, segment; } - overrides; -/* This structure is 32 bits: */ -typedef struct { overrides override; - unsigned char default_mode; } fpu_addr_modes; -/* PROTECTED has a restricted meaning in the emulator; it is used - to signal that the emulator needs to do special things to ensure - that protection is respected in a segmented model. */ -#define PROTECTED 4 -#define SIXTEEN 1 /* We rely upon this being 1 (true) */ -#define VM86 SIXTEEN -#define PM16 (SIXTEEN | PROTECTED) -#define SEG32 PROTECTED -extern unsigned char const data_sizes_16[32]; - -#define st(x) ( regs[((top+x) &7 )] ) - -#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty) -#define NOT_EMPTY(i) (st(i).tag != TW_Empty) -#define NOT_EMPTY_ST0 (st0_tag ^ TW_Empty) - -#define pop() { regs[(top++ & 7 )].tag = TW_Empty; } -#define poppop() { regs[((top + 1) & 7 )].tag \ - = regs[(top & 7 )].tag = TW_Empty; \ - top += 2; } - -/* push() does not affect the tags */ -#define push() { top--; } - - -#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); } - -#define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] ) - - -/*----- Prototypes for functions written in assembler -----*/ -/* extern void reg_move(FPU_REG *a, FPU_REG *b); */ - -asmlinkage void normalize(FPU_REG *x); -asmlinkage void normalize_nuo(FPU_REG *x); -asmlinkage int reg_div(FPU_REG const *arg1, FPU_REG const *arg2, - FPU_REG *answ, unsigned int control_w); -asmlinkage int reg_u_sub(FPU_REG const *arg1, FPU_REG const *arg2, - FPU_REG *answ, unsigned int control_w); -asmlinkage int reg_u_mul(FPU_REG const *arg1, FPU_REG const *arg2, - FPU_REG *answ, unsigned int control_w); -asmlinkage int reg_u_div(FPU_REG const *arg1, FPU_REG const *arg2, - FPU_REG *answ, unsigned int control_w); -asmlinkage int reg_u_add(FPU_REG const *arg1, FPU_REG const *arg2, - FPU_REG *answ, unsigned int control_w); -asmlinkage int wm_sqrt(FPU_REG *n, unsigned int control_w); -asmlinkage unsigned shrx(void *l, unsigned x); -asmlinkage unsigned shrxs(void *v, unsigned x); -asmlinkage unsigned long div_small(unsigned long long *x, unsigned long y); -asmlinkage void round_reg(FPU_REG *arg, unsigned int extent, - unsigned int control_w); - -#ifndef MAKING_PROTO -#include "fpu_proto.h" -#endif - -#endif __ASSEMBLER__ - -#endif _FPU_EMU_H_ diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/fpu_entry.c linux/drivers/FPU-emu/fpu_entry.c --- v1.1.76/linux/drivers/FPU-emu/fpu_entry.c Sun Nov 27 20:19:52 1994 +++ linux/drivers/FPU-emu/fpu_entry.c Thu Jan 1 02:00:00 1970 @@ -1,690 +0,0 @@ -/*---------------------------------------------------------------------------+ - | fpu_entry.c | - | | - | The entry function for wm-FPU-emu | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | See the files "README" and "COPYING" for further copyright and warranty | - | information. | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | 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 - -#include - -#include "fpu_system.h" -#include "fpu_emu.h" -#include "exception.h" -#include "control_w.h" -#include "status_w.h" - -#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */ - -#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 const st_instr_table[64] = { - fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_, - fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_, - fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_, - fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_, - fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, - fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, - fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, - fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, -}; - -#else /* Support only documented FPU op-codes */ - -static FUNC const st_instr_table[64] = { - fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__, - fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__, - fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__, - fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__, - fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, - fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, - fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, - fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, -}; - -#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 const type_table[64] = { - _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_, - _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_, - _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, - _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, - _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 const type_table[64] = { - _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_, - _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_, - _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_, - _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_, - _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 - - -#ifdef RE_ENTRANT_CHECKING -char emulating=0; -#endif RE_ENTRANT_CHECKING - -static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip, - overrides *override); - -asmlinkage void math_emulate(long arg) -{ - unsigned char FPU_modrm, byte1; - unsigned short code; - fpu_addr_modes addr_modes; - int unmasked; - FPU_REG loaded_data; - void *data_address; - struct address data_sel_off; - struct address entry_sel_off; - unsigned long code_base = 0; - unsigned long code_limit = 0; /* Initialized to stop compiler warnings */ - char st0_tag; - FPU_REG *st0_ptr; - struct desc_struct code_descriptor; - -#ifdef RE_ENTRANT_CHECKING - if ( emulating ) - { - printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n"); - } - RE_ENTRANT_CHECK_ON; -#endif RE_ENTRANT_CHECKING - - if (!current->used_math) - { - int i; - for ( i = 0; i < 8; i++ ) - { - /* Make sure that the registers are compatible - with the assumptions of the emulator. */ - regs[i].exp = 0; - regs[i].sigh = 0x80000000; - } - finit(); - current->used_math = 1; - } - - SETUP_DATA_AREA(arg); - - FPU_ORIG_EIP = FPU_EIP; - - if ( (FPU_EFLAGS & 0x00020000) != 0 ) - { - /* Virtual 8086 mode */ - addr_modes.default_mode = VM86; - FPU_EIP += code_base = FPU_CS << 4; - code_limit = code_base + 0xffff; /* Assumes code_base <= 0xffff0000 */ - } - else if ( FPU_CS == USER_CS && FPU_DS == USER_DS ) - { - addr_modes.default_mode = 0; - } - else if ( FPU_CS == KERNEL_CS ) - { - printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP); - panic("Math emulation needed in kernel"); - } - else - { - - if ( (FPU_CS & 4) != 4 ) /* Must be in the LDT */ - { - /* Can only handle segmented addressing via the LDT - for now, and it must be 16 bit */ - printk("FPU emulator: Unsupported addressing mode\n"); - math_abort(FPU_info, SIGILL); - } - - if ( SEG_D_SIZE(code_descriptor = LDT_DESCRIPTOR(FPU_CS)) ) - { - /* The above test may be wrong, the book is not clear */ - /* Segmented 32 bit protected mode */ - addr_modes.default_mode = SEG32; - } - else - { - /* 16 bit protected mode */ - addr_modes.default_mode = PM16; - } - FPU_EIP += code_base = SEG_BASE_ADDR(code_descriptor); - code_limit = code_base - + (SEG_LIMIT(code_descriptor)+1) * SEG_GRANULARITY(code_descriptor) - - 1; - if ( code_limit < code_base ) code_limit = 0xffffffff; - } - - FPU_lookahead = 1; - if (current->flags & PF_PTRACED) - FPU_lookahead = 0; - - if ( !valid_prefix(&byte1, (unsigned char **)&FPU_EIP, - &addr_modes.override) ) - { - RE_ENTRANT_CHECK_OFF; - printk("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n" - "FPU emulator: self-modifying code! (emulation impossible)\n", - byte1); - RE_ENTRANT_CHECK_ON; - EXCEPTION(EX_INTERNAL|0x126); - math_abort(FPU_info,SIGILL); - } - -do_another_FPU_instruction: - - no_ip_update = 0; - - FPU_EIP++; /* We have fetched the prefix and first code bytes. */ - - if ( addr_modes.default_mode ) - { - /* This checks for the minimum instruction bytes. - We also need to check any extra (address mode) code access. */ - if ( FPU_EIP > code_limit ) - math_abort(FPU_info,SIGSEGV); - } - - if ( (byte1 & 0xf8) != 0xd8 ) - { - if ( byte1 == FWAIT_OPCODE ) - { - if (partial_status & SW_Summary) - goto do_the_FPU_interrupt; - else - goto FPU_fwait_done; - } -#ifdef PARANOID - EXCEPTION(EX_INTERNAL|0x128); - math_abort(FPU_info,SIGILL); -#endif PARANOID - } - - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(1); - FPU_modrm = get_fs_byte((unsigned char *) FPU_EIP); - RE_ENTRANT_CHECK_ON; - FPU_EIP++; - - if (partial_status & 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, - but a real 80486 uses the following instructions: - fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex. - */ - code = (FPU_modrm << 8) | byte1; - if ( ! ( (((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */ - (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv, - fnstsw */ - ((code & 0xc000) != 0xc000))) ) ) - { - /* - * 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: - instruction_address.selector = status_word(); - operand_address.selector = tag_word(); - partial_status = 0; - top = 0; - { - int r; - for (r = 0; r < 8; r++) - { - regs[r].tag = TW_Empty; - } - } - - FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */ - - RE_ENTRANT_CHECK_OFF; - current->tss.trap_no = 16; - current->tss.error_code = 0; - send_sig(SIGFPE, current, 1); - return; - } - } - - entry_sel_off.offset = FPU_ORIG_EIP; - entry_sel_off.selector = FPU_CS; - entry_sel_off.opcode = (byte1 << 8) | FPU_modrm; - - FPU_rm = FPU_modrm & 7; - - if ( FPU_modrm < 0300 ) - { - /* All of these instructions use the mod/rm byte to get a data address */ - - if ( (addr_modes.default_mode & SIXTEEN) - ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX) ) - data_address = get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off, - addr_modes); - else - data_address = get_address(FPU_modrm, &FPU_EIP, &data_sel_off, - addr_modes); - - if ( addr_modes.default_mode ) - { - if ( FPU_EIP-1 > code_limit ) - math_abort(FPU_info,SIGSEGV); - } - - if ( !(byte1 & 1) ) - { - unsigned short status1 = partial_status; - - st0_ptr = &st(0); - st0_tag = st0_ptr->tag; - - /* Stack underflow has priority */ - if ( NOT_EMPTY_ST0 ) - { - if ( addr_modes.default_mode & PROTECTED ) - { - /* This table works for 16 and 32 bit protected mode */ - if ( access_limit < data_sizes_16[(byte1 >> 1) & 3] ) - math_abort(FPU_info,SIGSEGV); - } - - unmasked = 0; /* Do this here to stop compiler warnings. */ - switch ( (byte1 >> 1) & 3 ) - { - case 0: - unmasked = reg_load_single((float *)data_address, - &loaded_data); - break; - case 1: - reg_load_int32((long *)data_address, &loaded_data); - break; - case 2: - unmasked = reg_load_double((double *)data_address, - &loaded_data); - break; - case 3: - reg_load_int16((short *)data_address, &loaded_data); - break; - } - - /* No more access to user memory, it is safe - to use static data now */ - - /* NaN operands have the next priority. */ - /* We have to delay looking at st(0) until after - loading the data, because that data might contain an SNaN */ - if ( (st0_tag == TW_NaN) || - (loaded_data.tag == TW_NaN) ) - { - /* Restore the status word; we might have loaded a - denormal. */ - partial_status = status1; - if ( (FPU_modrm & 0x30) == 0x10 ) - { - /* fcom or fcomp */ - EXCEPTION(EX_Invalid); - setcc(SW_C3 | SW_C2 | SW_C0); - if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) ) - pop(); /* fcomp, masked, so we pop. */ - } - else - { -#ifdef PECULIAR_486 - /* This is not really needed, but gives behaviour - identical to an 80486 */ - if ( (FPU_modrm & 0x28) == 0x20 ) - /* fdiv or fsub */ - real_2op_NaN(&loaded_data, st0_ptr, - st0_ptr); - else -#endif PECULIAR_486 - /* fadd, fdivr, fmul, or fsubr */ - real_2op_NaN(st0_ptr, &loaded_data, - st0_ptr); - } - goto reg_mem_instr_done; - } - - if ( unmasked && !((FPU_modrm & 0x30) == 0x10) ) - { - /* Is not a comparison instruction. */ - if ( (FPU_modrm & 0x38) == 0x38 ) - { - /* fdivr */ - if ( (st0_tag == TW_Zero) && - (loaded_data.tag == TW_Valid) ) - { - if ( divide_by_zero(loaded_data.sign, - st0_ptr) ) - { - /* We use the fact here that the unmasked - exception in the loaded data was for a - denormal operand */ - /* Restore the state of the denormal op bit */ - partial_status &= ~SW_Denorm_Op; - partial_status |= status1 & SW_Denorm_Op; - } - } - } - goto reg_mem_instr_done; - } - - switch ( (FPU_modrm >> 3) & 7 ) - { - case 0: /* fadd */ - clear_C1(); - reg_add(st0_ptr, &loaded_data, st0_ptr, - control_word); - break; - case 1: /* fmul */ - clear_C1(); - reg_mul(st0_ptr, &loaded_data, st0_ptr, - control_word); - break; - case 2: /* fcom */ - compare_st_data(&loaded_data); - break; - case 3: /* fcomp */ - if ( !compare_st_data(&loaded_data) && !unmasked ) - pop(); - break; - case 4: /* fsub */ - clear_C1(); - reg_sub(st0_ptr, &loaded_data, st0_ptr, - control_word); - break; - case 5: /* fsubr */ - clear_C1(); - reg_sub(&loaded_data, st0_ptr, st0_ptr, - control_word); - break; - case 6: /* fdiv */ - clear_C1(); - reg_div(st0_ptr, &loaded_data, st0_ptr, - control_word); - break; - case 7: /* fdivr */ - clear_C1(); - if ( st0_tag == TW_Zero ) - partial_status = status1; /* Undo any denorm tag, - zero-divide has priority. */ - reg_div(&loaded_data, st0_ptr, 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) && (control_word & CW_Invalid) ) - pop(); /* fcomp */ - } - else - stack_underflow(); - } - reg_mem_instr_done: - operand_address = data_sel_off; - } - else - { - if ( !(no_ip_update = - load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1, - addr_modes, data_address)) ) - { - operand_address = data_sel_off; - } - } - - } - else - { - /* None of these instructions access user memory */ - unsigned char instr_index = (FPU_modrm & 0x38) | (byte1 & 7); - -#ifdef PECULIAR_486 - /* This is supposed to be undefined, but a real 80486 seems - to do this: */ - operand_address.offset = 0; - operand_address.selector = FPU_DS; -#endif PECULIAR_486 - - st0_ptr = &st(0); - st0_tag = st0_ptr->tag; - switch ( type_table[(int) instr_index] ) - { - case _NONE_: /* also _REGIc: _REGIn */ - break; - case _REG0_: - if ( !NOT_EMPTY_ST0 ) - { - stack_underflow(); - goto FPU_instruction_done; - } - break; - case _REGIi: - if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) - { - stack_underflow_i(FPU_rm); - goto FPU_instruction_done; - } - break; - case _REGIp: - if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) - { - stack_underflow_pop(FPU_rm); - goto FPU_instruction_done; - } - break; - case _REGI_: - if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) - { - stack_underflow(); - goto FPU_instruction_done; - } - break; - case _PUSH_: /* Only used by the fld st(i) instruction */ - break; - case _null_: - FPU_illegal(); - goto FPU_instruction_done; - default: - EXCEPTION(EX_INTERNAL|0x111); - goto FPU_instruction_done; - } - (*st_instr_table[(int) instr_index])(); - -FPU_instruction_done: - ; - } - - if ( ! no_ip_update ) - instruction_address = entry_sel_off; - -FPU_fwait_done: - -#ifdef DEBUG - RE_ENTRANT_CHECK_OFF; - emu_printall(); - RE_ENTRANT_CHECK_ON; -#endif DEBUG - - if (FPU_lookahead && !need_resched) - { - FPU_ORIG_EIP = FPU_EIP - code_base; - if ( valid_prefix(&byte1, (unsigned char **)&FPU_EIP, - &addr_modes.override) ) - goto do_another_FPU_instruction; - } - - if ( addr_modes.default_mode ) - FPU_EIP -= code_base; - - RE_ENTRANT_CHECK_OFF; -} - - -/* Support for prefix bytes is not yet complete. To properly handle - all prefix bytes, further changes are needed in the emulator code - which accesses user address space. Access to separate segments is - important for msdos emulation. */ -static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip, - overrides *override) -{ - unsigned char byte; - unsigned char *ip = *fpu_eip; - - *override = (overrides) { 0, 0, PREFIX_DEFAULT }; /* defaults */ - - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(1); - byte = get_fs_byte(ip); - RE_ENTRANT_CHECK_ON; - - while ( 1 ) - { - switch ( byte ) - { - case ADDR_SIZE_PREFIX: - override->address_size = ADDR_SIZE_PREFIX; - goto do_next_byte; - - case OP_SIZE_PREFIX: - override->operand_size = OP_SIZE_PREFIX; - goto do_next_byte; - - case PREFIX_CS: - override->segment = PREFIX_CS_; - goto do_next_byte; - case PREFIX_ES: - override->segment = PREFIX_ES_; - goto do_next_byte; - case PREFIX_SS: - override->segment = PREFIX_SS_; - goto do_next_byte; - case PREFIX_FS: - override->segment = PREFIX_FS_; - goto do_next_byte; - case PREFIX_GS: - override->segment = PREFIX_GS_; - goto do_next_byte; - case PREFIX_DS: - override->segment = PREFIX_DS_; - goto do_next_byte; - -/* lock is not a valid prefix for FPU instructions, - let the cpu handle it to generate a SIGILL. */ -/* case PREFIX_LOCK: */ - - /* rep.. prefixes have no meaning for FPU instructions */ - case PREFIX_REPE: - case PREFIX_REPNE: - - do_next_byte: - ip++; - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(1); - byte = get_fs_byte(ip); - RE_ENTRANT_CHECK_ON; - break; - case FWAIT_OPCODE: - *Byte = byte; - return 1; - default: - if ( (byte & 0xf8) == 0xd8 ) - { - *Byte = byte; - *fpu_eip = ip; - return 1; - } - else - { - /* Not a valid sequence of prefix bytes followed by - an FPU instruction. */ - *Byte = byte; /* Needed for error message. */ - return 0; - } - } - } -} - - -void math_abort(struct info * info, unsigned int signal) -{ - FPU_EIP = FPU_ORIG_EIP; - current->tss.trap_no = 16; - current->tss.error_code = 0; - send_sig(signal,current,1); - RE_ENTRANT_CHECK_OFF; - __asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4)); -#ifdef PARANOID - printk("ERROR: wm-FPU-emu math_abort failed!\n"); -#endif PARANOID -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/fpu_etc.c linux/drivers/FPU-emu/fpu_etc.c --- v1.1.76/linux/drivers/FPU-emu/fpu_etc.c Fri Aug 19 08:54:01 1994 +++ linux/drivers/FPU-emu/fpu_etc.c Thu Jan 1 02:00:00 1970 @@ -1,129 +0,0 @@ -/*---------------------------------------------------------------------------+ - | fpu_etc.c | - | | - | Implement a few FPU instructions. | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -#include "fpu_system.h" -#include "exception.h" -#include "fpu_emu.h" -#include "status_w.h" -#include "reg_constant.h" - - -static void fchs(FPU_REG *st0_ptr) -{ - if ( st0_ptr->tag ^ TW_Empty ) - { - st0_ptr->sign ^= SIGN_POS^SIGN_NEG; - clear_C1(); - } - else - stack_underflow(); -} - -static void fabs(FPU_REG *st0_ptr) -{ - if ( st0_ptr->tag ^ TW_Empty ) - { - st0_ptr->sign = SIGN_POS; - clear_C1(); - } - else - stack_underflow(); -} - - -static void ftst_(FPU_REG *st0_ptr) -{ - switch (st0_ptr->tag) - { - case TW_Zero: - setcc(SW_C3); - break; - case TW_Valid: - if (st0_ptr->sign == SIGN_POS) - setcc(0); - else - setcc(SW_C0); - -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - { -#ifdef PECULIAR_486 - /* This is weird! */ - if (st0_ptr->sign == SIGN_POS) - setcc(SW_C3); -#endif PECULIAR_486 - return; - } -#endif DENORM_OPERAND - - break; - case TW_NaN: - setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ - EXCEPTION(EX_Invalid); - break; - case TW_Infinity: - if (st0_ptr->sign == SIGN_POS) - setcc(0); - else - setcc(SW_C0); - break; - case TW_Empty: - setcc(SW_C0|SW_C2|SW_C3); - EXCEPTION(EX_StackUnder); - break; - default: - setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ - EXCEPTION(EX_INTERNAL|0x14); - break; - } -} - -static void fxam(FPU_REG *st0_ptr) -{ - int c=0; - switch (st0_ptr->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 ( st0_ptr->exp <= EXP_UNDER ) - c = SW_C2|SW_C3; /* Denormal */ - else - c = SW_C2; - break; - case TW_NaN: - c = SW_C0; - break; - case TW_Infinity: - c = SW_C2|SW_C0; - break; - } - if (st0_ptr->sign == SIGN_NEG) - c |= SW_C1; - setcc(c); -} - - -static FUNC_ST0 const fp_etc_table[] = { - fchs, fabs, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal, - ftst_, fxam, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal -}; - -void fp_etc() -{ - (fp_etc_table[FPU_rm])(&st(0)); -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/fpu_proto.h linux/drivers/FPU-emu/fpu_proto.h --- v1.1.76/linux/drivers/FPU-emu/fpu_proto.h Mon Aug 1 08:19:13 1994 +++ linux/drivers/FPU-emu/fpu_proto.h Thu Jan 1 02:00:00 1970 @@ -1,137 +0,0 @@ -/* errors.c */ -extern void Un_impl(void); -extern void FPU_illegal(void); -extern void emu_printall(void); -extern void stack_overflow(void); -extern void stack_underflow(void); -extern void stack_underflow_i(int i); -extern void stack_underflow_pop(int i); -extern int set_precision_flag(int flags); -asmlinkage void exception(int n); -asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest); -asmlinkage int arith_invalid(FPU_REG *dest); -asmlinkage int divide_by_zero(int sign, FPU_REG *dest); -asmlinkage void set_precision_flag_up(void); -asmlinkage void set_precision_flag_down(void); -asmlinkage int denormal_operand(void); -asmlinkage int arith_overflow(FPU_REG *dest); -asmlinkage int arith_underflow(FPU_REG *dest); - -/* 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 */ -asmlinkage void math_emulate(long arg); -extern void math_abort(struct info *info, unsigned int signal); - -/* fpu_etc.c */ -extern void fp_etc(void); - -/* fpu_trig.c */ -extern void convert_l2reg(long const *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, unsigned long *fpu_eip, - struct address *addr, - fpu_addr_modes); -extern void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, - struct address *addr, - fpu_addr_modes); - -/* load_store.c */ -extern int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, - void *address); - -/* poly_2xm1.c */ -extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result); - -/* poly_atan.c */ -extern void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result); - -/* poly_l2.c */ -extern void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result); -extern int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result); - -/* poly_sin.c */ -extern void poly_sine(FPU_REG const *arg, FPU_REG *result); -extern void poly_cos(FPU_REG const *arg, FPU_REG *result); - -/* poly_tan.c */ -extern void poly_tan(FPU_REG const *arg, FPU_REG *result); - -/* reg_add_sub.c */ -extern int reg_add(FPU_REG const *a, FPU_REG const *b, - FPU_REG *dest, int control_w); -extern int reg_sub(FPU_REG const *a, FPU_REG const *b, - FPU_REG *dest, int control_w); - -/* reg_compare.c */ -extern int compare(FPU_REG const *b); -extern int compare_st_data(FPU_REG const *b); -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 int reg_load_extended(long double *addr, FPU_REG *loaded_data); -extern int reg_load_double(double *dfloat, FPU_REG *loaded_data); -extern int reg_load_single(float *single, FPU_REG *loaded_data); -extern void reg_load_int64(long long *_s, FPU_REG *loaded_data); -extern void reg_load_int32(long *_s, FPU_REG *loaded_data); -extern void reg_load_int16(short *_s, FPU_REG *loaded_data); -extern void reg_load_bcd(char *s, FPU_REG *loaded_data); -extern int reg_store_extended(long double *d, FPU_REG *st0_ptr); -extern int reg_store_double(double *dfloat, FPU_REG *st0_ptr); -extern int reg_store_single(float *single, FPU_REG *st0_ptr); -extern int reg_store_int64(long long *d, FPU_REG *st0_ptr); -extern int reg_store_int32(long *d, FPU_REG *st0_ptr); -extern int reg_store_int16(short *d, FPU_REG *st0_ptr); -extern int reg_store_bcd(char *d, FPU_REG *st0_ptr); -extern int round_to_int(FPU_REG *r); -extern char *fldenv(fpu_addr_modes addr_modes, char *address); -extern void frstor(fpu_addr_modes addr_modes, char *address); -extern unsigned short tag_word(void); -extern char *fstenv(fpu_addr_modes addr_modes, char *address); -extern void fsave(fpu_addr_modes addr_modes, char *address); - -/* reg_mul.c */ -extern int reg_mul(FPU_REG const *a, FPU_REG const *b, - FPU_REG *dest, unsigned int control_w); diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/fpu_system.h linux/drivers/FPU-emu/fpu_system.h --- v1.1.76/linux/drivers/FPU-emu/fpu_system.h Thu Jun 2 10:28:24 1994 +++ linux/drivers/FPU-emu/fpu_system.h Thu Jan 1 02:00:00 1970 @@ -1,82 +0,0 @@ -/*---------------------------------------------------------------------------+ - | fpu_system.h | - | | - | Copyright (C) 1992,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - +---------------------------------------------------------------------------*/ - -#ifndef _FPU_SYSTEM_H -#define _FPU_SYSTEM_H - -/* system dependent definitions */ - -#include -#include - -/* This sets the pointer FPU_info to point to the argument part - of the stack frame of math_emulate() */ -#define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg - -#define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3]) -#define SEG_D_SIZE(x) ((x).b & (3 << 21)) -#define SEG_G_BIT(x) ((x).b & (1 << 23)) -#define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1) -#define SEG_286_MODE(x) ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23))) -#define SEG_BASE_ADDR(s) (((s).b & 0xff000000) \ - | (((s).b & 0xff) << 16) | ((s).a >> 16)) -#define SEG_LIMIT(s) (((s).b & 0xff0000) | ((s).a & 0xffff)) -#define SEG_EXECUTE_ONLY(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 11)) -#define SEG_WRITE_PERM(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 9)) -#define SEG_EXPAND_DOWN(s) (((s).b & ((1 << 11) | (1 << 10))) \ - == (1 << 10)) - -#define I387 (current->tss.i387) -#define FPU_info (I387.soft.info) - -#define FPU_CS (*(unsigned short *) &(FPU_info->___cs)) -#define FPU_SS (*(unsigned short *) &(FPU_info->___ss)) -#define FPU_DS (*(unsigned short *) &(FPU_info->___ds)) -#define FPU_EAX (FPU_info->___eax) -#define FPU_EFLAGS (FPU_info->___eflags) -#define FPU_EIP (FPU_info->___eip) -#define FPU_ORIG_EIP (FPU_info->___orig_eip) - -#define FPU_lookahead (I387.soft.lookahead) - -/* nz if ip_offset and cs_selector are not to be set for the current - instruction. */ -#define no_ip_update (((char *)&(I387.soft.twd))[0]) -#define FPU_rm (((unsigned char *)&(I387.soft.twd))[1]) - -/* Number of bytes of data which can be legally accessed by the current - instruction. This only needs to hold a number <= 108, so a byte will do. */ -#define access_limit (((unsigned char *)&(I387.soft.twd))[2]) - -#define partial_status (I387.soft.swd) -#define control_word (I387.soft.cwd) -#define regs (I387.soft.regs) -#define top (I387.soft.top) - -#define instruction_address (*(struct address *)&I387.soft.fip) -#define operand_address (*(struct address *)&I387.soft.foo) - -#define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \ - math_abort(FPU_info,SIGSEGV) - -#undef FPU_IGNORE_CODE_SEGV -#ifdef FPU_IGNORE_CODE_SEGV -/* verify_area() is very expensive, and causes the emulator to run - about 20% slower if applied to the code. Anyway, errors due to bad - code addresses should be much rarer than errors due to bad data - addresses. */ -#define FPU_code_verify_area(z) -#else -/* A simpler test than verify_area() can probably be done for - FPU_code_verify_area() because the only possible error is to step - past the upper boundary of a legal code area. */ -#define FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z) -#endif - -#endif diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/fpu_trig.c linux/drivers/FPU-emu/fpu_trig.c --- v1.1.76/linux/drivers/FPU-emu/fpu_trig.c Mon Aug 1 08:19:13 1994 +++ linux/drivers/FPU-emu/fpu_trig.c Thu Jan 1 02:00:00 1970 @@ -1,1718 +0,0 @@ -/*---------------------------------------------------------------------------+ - | fpu_trig.c | - | | - | Implementation of the FPU "transcendental" functions. | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -#include "fpu_system.h" -#include "exception.h" -#include "fpu_emu.h" -#include "status_w.h" -#include "control_w.h" -#include "reg_constant.h" - - -static void rem_kernel(unsigned long long st0, unsigned long long *y, - unsigned long long st1, - unsigned long long q, int n); - -#define BETTER_THAN_486 - -#define FCOS 4 -/* Not needed now with new code -#define FPTAN 1 - */ - -/* Used only by fptan, fsin, fcos, and fsincos. */ -/* This routine produces very accurate results, similar to - using a value of pi with more than 128 bits precision. */ -/* Limited measurements show no results worse than 64 bit precision - except for the results for arguments close to 2^63, where the - precision of the result sometimes degrades to about 63.9 bits */ -static int trig_arg(FPU_REG *X, int even) -{ - FPU_REG tmp; - unsigned long long q; - int old_cw = control_word, saved_status = partial_status; - - if ( X->exp >= EXP_BIAS + 63 ) - { - partial_status |= SW_C2; /* Reduction incomplete. */ - return -1; - } - - control_word &= ~CW_RC; - control_word |= RC_CHOP; - - reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f); - round_to_int(&tmp); /* Fortunately, this can't overflow - to 2^64 */ - q = significand(&tmp); - if ( q ) - { - rem_kernel(significand(X), - &significand(&tmp), - significand(&CONST_PI2), - q, X->exp - CONST_PI2.exp); - tmp.exp = CONST_PI2.exp; - normalize(&tmp); - reg_move(&tmp, X); - } - -#ifdef FPTAN - if ( even == FPTAN ) - { - if ( ((X->exp >= EXP_BIAS) || - ((X->exp == EXP_BIAS-1) - && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) ) - even = FCOS; - else - even = 0; - } -#endif FPTAN - - if ( (even && !(q & 1)) || (!even && (q & 1)) ) - { - reg_sub(&CONST_PI2, X, X, FULL_PRECISION); -#ifdef BETTER_THAN_486 - /* So far, the results are exact but based upon a 64 bit - precision approximation to pi/2. The technique used - now is equivalent to using an approximation to pi/2 which - is accurate to about 128 bits. */ - if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) ) - { - /* This code gives the effect of having p/2 to better than - 128 bits precision. */ - significand(&tmp) = q + 1; - tmp.exp = EXP_BIAS + 63; - tmp.tag = TW_Valid; - normalize(&tmp); - reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); - reg_add(X, &tmp, X, FULL_PRECISION); - if ( X->sign == SIGN_NEG ) - { - /* CONST_PI2extra is negative, so the result of the addition - can be negative. This means that the argument is actually - in a different quadrant. The correction is always < pi/2, - so it can't overflow into yet another quadrant. */ - X->sign = SIGN_POS; - q++; - } - } -#endif BETTER_THAN_486 - } -#ifdef BETTER_THAN_486 - else - { - /* So far, the results are exact but based upon a 64 bit - precision approximation to pi/2. The technique used - now is equivalent to using an approximation to pi/2 which - is accurate to about 128 bits. */ - if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) ) - { - /* This code gives the effect of having p/2 to better than - 128 bits precision. */ - significand(&tmp) = q; - tmp.exp = EXP_BIAS + 63; - tmp.tag = TW_Valid; - normalize(&tmp); - reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); - reg_sub(X, &tmp, X, FULL_PRECISION); - if ( (X->exp == CONST_PI2.exp) && - ((X->sigh > CONST_PI2.sigh) - || ((X->sigh == CONST_PI2.sigh) - && (X->sigl > CONST_PI2.sigl))) ) - { - /* CONST_PI2extra is negative, so the result of the - subtraction can be larger than pi/2. This means - that the argument is actually in a different quadrant. - The correction is always < pi/2, so it can't overflow - into yet another quadrant. */ - reg_sub(&CONST_PI, X, X, FULL_PRECISION); - q++; - } - } - } -#endif BETTER_THAN_486 - - control_word = old_cw; - partial_status = saved_status & ~SW_C2; /* Reduction complete. */ - - return (q & 3) | even; -} - - -/* Convert a long to register */ -void convert_l2reg(long const *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(FPU_REG *st0_ptr) -{ - switch ( st0_ptr->tag ) - { - case TW_NaN: - if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ - { - EXCEPTION(EX_Invalid); - if ( control_word & CW_Invalid ) - st0_ptr->sigh |= 0x40000000; /* Convert to a QNaN */ - } - break; /* return with a NaN in st(0) */ - case TW_Empty: - stack_underflow(); /* Puts a QNaN in st(0) */ - break; -#ifdef PARANOID - default: - EXCEPTION(EX_INTERNAL|0x0112); -#endif PARANOID - } -} - - -static void single_arg_2_error(FPU_REG *st0_ptr) -{ - FPU_REG *st_new_ptr; - - switch ( st0_ptr->tag ) - { - case TW_NaN: - if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ - { - EXCEPTION(EX_Invalid); - if ( control_word & CW_Invalid ) - { - /* The masked response */ - /* Convert to a QNaN */ - st0_ptr->sigh |= 0x40000000; - st_new_ptr = &st(-1); - push(); - reg_move(&st(1), st_new_ptr); - } - } - else - { - /* A QNaN */ - st_new_ptr = &st(-1); - push(); - reg_move(&st(1), st_new_ptr); - } - break; /* return with a NaN in st(0) */ -#ifdef PARANOID - default: - EXCEPTION(EX_INTERNAL|0x0112); -#endif PARANOID - } -} - - -/*---------------------------------------------------------------------------*/ - -static void f2xm1(FPU_REG *st0_ptr) -{ - clear_C1(); - switch ( st0_ptr->tag ) - { - case TW_Valid: - { - if ( st0_ptr->exp >= 0 ) - { - /* For an 80486 FPU, the result is undefined. */ - } -#ifdef DENORM_OPERAND - else if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - else - { - /* poly_2xm1(x) requires 0 < x < 1. */ - poly_2xm1(st0_ptr, st0_ptr); - } - if ( st0_ptr->exp <= EXP_UNDER ) - { - /* A denormal result has been produced. - Precision must have been lost, this is always - an underflow. */ - arith_underflow(st0_ptr); - } - set_precision_flag_up(); /* 80486 appears to always do this */ - return; - } - case TW_Zero: - return; - case TW_Infinity: - if ( st0_ptr->sign == SIGN_NEG ) - { - /* -infinity gives -1 (p16-10) */ - reg_move(&CONST_1, st0_ptr); - st0_ptr->sign = SIGN_NEG; - } - return; - default: - single_arg_error(st0_ptr); - } -} - - -static void fptan(FPU_REG *st0_ptr) -{ - char st0_tag = st0_ptr->tag; - FPU_REG *st_new_ptr; - int q; - char arg_sign = st0_ptr->sign; - - /* Stack underflow has higher priority */ - if ( st0_tag == TW_Empty ) - { - stack_underflow(); /* Puts a QNaN in st(0) */ - if ( control_word & CW_Invalid ) - { - st_new_ptr = &st(-1); - push(); - stack_underflow(); /* Puts a QNaN in the new st(0) */ - } - return; - } - - if ( STACK_OVERFLOW ) - { stack_overflow(); return; } - - switch ( st0_tag ) - { - case TW_Valid: - if ( st0_ptr->exp > EXP_BIAS - 40 ) - { - st0_ptr->sign = SIGN_POS; - if ( (q = trig_arg(st0_ptr, 0)) != -1 ) - { - poly_tan(st0_ptr, st0_ptr); - st0_ptr->sign = (q & 1) ^ arg_sign; - } - else - { - /* Operand is out of range */ - st0_ptr->sign = arg_sign; /* restore st(0) */ - return; - } - set_precision_flag_up(); /* We do not really know if up or down */ - } - else - { - /* For a small arg, the result == the argument */ - /* Underflow may happen */ - - if ( st0_ptr->exp <= EXP_UNDER ) - { -#ifdef DENORM_OPERAND - if ( denormal_operand() ) - return; -#endif DENORM_OPERAND - /* A denormal result has been produced. - Precision must have been lost, this is always - an underflow. */ - if ( arith_underflow(st0_ptr) ) - return; - } - set_precision_flag_down(); /* Must be down. */ - } - push(); - reg_move(&CONST_1, st_new_ptr); - return; - break; - case TW_Infinity: - /* The 80486 treats infinity as an invalid operand */ - arith_invalid(st0_ptr); - if ( control_word & CW_Invalid ) - { - st_new_ptr = &st(-1); - push(); - arith_invalid(st_new_ptr); - } - return; - case TW_Zero: - push(); - reg_move(&CONST_1, st_new_ptr); - setcc(0); - break; - default: - single_arg_2_error(st0_ptr); - break; - } -} - - -static void fxtract(FPU_REG *st0_ptr) -{ - char st0_tag = st0_ptr->tag; - FPU_REG *st_new_ptr; - register FPU_REG *st1_ptr = st0_ptr; /* anticipate */ - - if ( STACK_OVERFLOW ) - { stack_overflow(); return; } - clear_C1(); - if ( !(st0_tag ^ TW_Valid) ) - { - long e; - -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - push(); - reg_move(st1_ptr, st_new_ptr); - st_new_ptr->exp = EXP_BIAS; - e = st1_ptr->exp - EXP_BIAS; - convert_l2reg(&e, st1_ptr); - return; - } - else if ( st0_tag == TW_Zero ) - { - char sign = st0_ptr->sign; - if ( divide_by_zero(SIGN_NEG, st0_ptr) ) - return; - push(); - reg_move(&CONST_Z, st_new_ptr); - st_new_ptr->sign = sign; - return; - } - else if ( st0_tag == TW_Infinity ) - { - char sign = st0_ptr->sign; - st0_ptr->sign = SIGN_POS; - push(); - reg_move(&CONST_INF, st_new_ptr); - st_new_ptr->sign = sign; - return; - } - else if ( st0_tag == TW_NaN ) - { - if ( real_2op_NaN(st0_ptr, st0_ptr, st0_ptr) ) - return; - push(); - reg_move(st1_ptr, st_new_ptr); - return; - } - else if ( 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(FPU_REG *st0_ptr) -{ - clear_C1(); - top--; /* st0_ptr will be fixed in math_emulate() before the next instr */ -} - -static void fincstp(FPU_REG *st0_ptr) -{ - clear_C1(); - top++; /* st0_ptr will be fixed in math_emulate() before the next instr */ -} - - -static void fsqrt_(FPU_REG *st0_ptr) -{ - char st0_tag = st0_ptr->tag; - - clear_C1(); - if ( !(st0_tag ^ TW_Valid) ) - { - int expon; - - if (st0_ptr->sign == SIGN_NEG) - { - arith_invalid(st0_ptr); /* sqrt(negative) is invalid */ - return; - } - -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - expon = st0_ptr->exp - EXP_BIAS; - st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 .. 4.0) */ - - wm_sqrt(st0_ptr, control_word); /* Do the computation */ - - st0_ptr->exp += expon >> 1; - st0_ptr->sign = SIGN_POS; - } - else if ( st0_tag == TW_Zero ) - return; - else if ( st0_tag == TW_Infinity ) - { - if ( st0_ptr->sign == SIGN_NEG ) - arith_invalid(st0_ptr); /* sqrt(-Infinity) is invalid */ - return; - } - else - { single_arg_error(st0_ptr); return; } - -} - - -static void frndint_(FPU_REG *st0_ptr) -{ - char st0_tag = st0_ptr->tag; - int flags; - - if ( !(st0_tag ^ TW_Valid) ) - { - if (st0_ptr->exp > EXP_BIAS+63) - return; - -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - /* Fortunately, this can't overflow to 2^64 */ - if ( (flags = round_to_int(st0_ptr)) ) - set_precision_flag(flags); - - st0_ptr->exp = EXP_BIAS + 63; - normalize(st0_ptr); - return; - } - else if ( (st0_tag == TW_Zero) || (st0_tag == TW_Infinity) ) - return; - else - single_arg_error(st0_ptr); -} - - -static void fsin(FPU_REG *st0_ptr) -{ - char st0_tag = st0_ptr->tag; - char arg_sign = st0_ptr->sign; - - if ( st0_tag == TW_Valid ) - { - FPU_REG rv; - int q; - - if ( st0_ptr->exp > EXP_BIAS - 40 ) - { - st0_ptr->sign = SIGN_POS; - if ( (q = trig_arg(st0_ptr, 0)) != -1 ) - { - - poly_sine(st0_ptr, &rv); - - if (q & 2) - rv.sign ^= SIGN_POS ^ SIGN_NEG; - rv.sign ^= arg_sign; - reg_move(&rv, st0_ptr); - - /* We do not really know if up or down */ - set_precision_flag_up(); - return; - } - else - { - /* Operand is out of range */ - st0_ptr->sign = arg_sign; /* restore st(0) */ - return; - } - } - else - { - /* For a small arg, the result == the argument */ - /* Underflow may happen */ - - if ( st0_ptr->exp <= EXP_UNDER ) - { -#ifdef DENORM_OPERAND - if ( denormal_operand() ) - return; -#endif DENORM_OPERAND - /* A denormal result has been produced. - Precision must have been lost, this is always - an underflow. */ - arith_underflow(st0_ptr); - return; - } - - set_precision_flag_up(); /* Must be up. */ - } - } - else if ( st0_tag == TW_Zero ) - { - setcc(0); - return; - } - else if ( st0_tag == TW_Infinity ) - { - /* The 80486 treats infinity as an invalid operand */ - arith_invalid(st0_ptr); - return; - } - else - single_arg_error(st0_ptr); -} - - -static int f_cos(FPU_REG *arg) -{ - char arg_sign = arg->sign; - - if ( arg->tag == TW_Valid ) - { - FPU_REG rv; - int q; - - if ( arg->exp > EXP_BIAS - 40 ) - { - arg->sign = SIGN_POS; - if ( (arg->exp < EXP_BIAS) - || ((arg->exp == EXP_BIAS) - && (significand(arg) <= 0xc90fdaa22168c234LL)) ) - { - poly_cos(arg, &rv); - reg_move(&rv, arg); - - /* We do not really know if up or down */ - set_precision_flag_down(); - - return 0; - } - else if ( (q = trig_arg(arg, FCOS)) != -1 ) - { - poly_sine(arg, &rv); - - if ((q+1) & 2) - rv.sign ^= SIGN_POS ^ SIGN_NEG; - reg_move(&rv, arg); - - /* We do not really know if up or down */ - set_precision_flag_down(); - - return 0; - } - else - { - /* Operand is out of range */ - arg->sign = arg_sign; /* restore st(0) */ - return 1; - } - } - else - { -#ifdef DENORM_OPERAND - if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) ) - return 1; -#endif DENORM_OPERAND - - setcc(0); - reg_move(&CONST_1, arg); -#ifdef PECULIAR_486 - set_precision_flag_down(); /* 80486 appears to do this. */ -#else - set_precision_flag_up(); /* Must be up. */ -#endif PECULIAR_486 - return 0; - } - } - else if ( arg->tag == TW_Zero ) - { - reg_move(&CONST_1, arg); - setcc(0); - return 0; - } - else if ( arg->tag == TW_Infinity ) - { - /* The 80486 treats infinity as an invalid operand */ - arith_invalid(arg); - return 1; - } - else - { - single_arg_error(arg); /* requires arg == &st(0) */ - return 1; - } -} - - -static void fcos(FPU_REG *st0_ptr) -{ - f_cos(st0_ptr); -} - - -static void fsincos(FPU_REG *st0_ptr) -{ - char st0_tag = st0_ptr->tag; - FPU_REG *st_new_ptr; - FPU_REG arg; - - /* Stack underflow has higher priority */ - if ( st0_tag == TW_Empty ) - { - stack_underflow(); /* Puts a QNaN in st(0) */ - if ( control_word & CW_Invalid ) - { - st_new_ptr = &st(-1); - push(); - stack_underflow(); /* Puts a QNaN in the new st(0) */ - } - return; - } - - if ( STACK_OVERFLOW ) - { stack_overflow(); return; } - - if ( st0_tag == TW_NaN ) - { - single_arg_2_error(st0_ptr); - return; - } - else if ( st0_tag == TW_Infinity ) - { - /* The 80486 treats infinity as an invalid operand */ - if ( !arith_invalid(st0_ptr) ) - { - /* unmasked response */ - push(); - arith_invalid(st_new_ptr); - } - return; - } - - reg_move(st0_ptr,&arg); - if ( !f_cos(&arg) ) - { - fsin(st0_ptr); - push(); - reg_move(&arg,st_new_ptr); - } - -} - - -/*---------------------------------------------------------------------------*/ -/* The following all require two arguments: st(0) and st(1) */ - -/* A lean, mean kernel for the fprem instructions. This relies upon - the division and rounding to an integer in do_fprem giving an - exact result. Because of this, rem_kernel() needs to deal only with - the least significant 64 bits, the more significant bits of the - result must be zero. - */ -static void rem_kernel(unsigned long long st0, unsigned long long *y, - unsigned long long st1, - unsigned long long q, int n) -{ - unsigned long long x; - - x = st0 << n; - - /* Do the required multiplication and subtraction in the one operation */ - asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1; - movl %3,%%eax; mull %4; subl %%eax,%1; - movl %2,%%eax; mull %5; subl %%eax,%1;" - :"=m" (x), "=m" (((unsigned *)&x)[1]) - :"m" (st1),"m" (((unsigned *)&st1)[1]), - "m" (q),"m" (((unsigned *)&q)[1]) - :"%ax","%dx"); - - *y = x; -} - - -/* Remainder of st(0) / st(1) */ -/* This routine produces exact results, i.e. there is never any - rounding or truncation, etc of the result. */ -static void do_fprem(FPU_REG *st0_ptr, int round) -{ - FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; - char st0_tag = st0_ptr->tag; - char sign = st0_ptr->sign; - - if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) - { - FPU_REG tmp; - int old_cw = control_word; - int expdif = st0_ptr->exp - st1_ptr->exp; - long long q; - unsigned short saved_status; - int cc = 0; - -#ifdef DENORM_OPERAND - if ( ((st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - /* We want the status following the denorm tests, but don't want - the status changed by the arithmetic operations. */ - saved_status = partial_status; - control_word &= ~CW_RC; - control_word |= RC_CHOP; - - if (expdif < 64) - { - /* This should be the most common case */ - - if ( expdif > -2 ) - { - reg_div(st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); - - if ( tmp.exp >= EXP_BIAS ) - { - round_to_int(&tmp); /* Fortunately, this can't overflow - to 2^64 */ - q = significand(&tmp); - - rem_kernel(significand(st0_ptr), - &significand(&tmp), - significand(st1_ptr), - q, expdif); - - tmp.exp = st1_ptr->exp; - } - else - { - reg_move(st0_ptr, &tmp); - q = 0; - } - tmp.sign = sign; - - if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) ) - { - /* We may need to subtract st(1) once more, - to get a result <= 1/2 of st(1). */ - unsigned long long x; - expdif = st1_ptr->exp - tmp.exp; - if ( expdif <= 1 ) - { - if ( expdif == 0 ) - x = significand(st1_ptr) - significand(&tmp); - else /* expdif is 1 */ - x = (significand(st1_ptr) << 1) - significand(&tmp); - if ( (x < significand(&tmp)) || - /* or equi-distant (from 0 & st(1)) and q is odd */ - ((x == significand(&tmp)) && (q & 1) ) ) - { - tmp.sign ^= (SIGN_POS^SIGN_NEG); - significand(&tmp) = x; - q++; - } - } - } - - if (q & 4) cc |= SW_C0; - if (q & 2) cc |= SW_C3; - if (q & 1) cc |= SW_C1; - } - else - { - control_word = old_cw; - setcc(0); - return; - } - } - else - { - /* There is a large exponent difference ( >= 64 ) */ - /* To make much sense, the code in this section should - be done at high precision. */ - int exp_1; - - /* prevent overflow here */ - /* N is 'a number between 32 and 63' (p26-113) */ - reg_move(st0_ptr, &tmp); - tmp.exp = EXP_BIAS + 56; - exp_1 = st1_ptr->exp; st1_ptr->exp = EXP_BIAS; - expdif -= 56; - - reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); - st1_ptr->exp = exp_1; - - round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */ - - rem_kernel(significand(st0_ptr), - &significand(&tmp), - significand(st1_ptr), - significand(&tmp), - tmp.exp - EXP_BIAS - ); - tmp.exp = exp_1 + expdif; - tmp.sign = sign; - - /* It is possible for the operation to be complete here. - What does the IEEE standard say? The Intel 80486 manual - implies that the operation will never be completed at this - point, and the behaviour of a real 80486 confirms this. - */ - if ( !(tmp.sigh | tmp.sigl) ) - { - /* The result is zero */ - control_word = old_cw; - partial_status = saved_status; - reg_move(&CONST_Z, st0_ptr); - st0_ptr->sign = sign; -#ifdef PECULIAR_486 - setcc(SW_C2); -#else - setcc(0); -#endif PECULIAR_486 - return; - } - cc = SW_C2; - } - - control_word = old_cw; - partial_status = saved_status; - normalize_nuo(&tmp); - reg_move(&tmp, st0_ptr); - setcc(cc); - - /* The only condition to be looked for is underflow, - and it can occur here only if underflow is unmasked. */ - if ( (st0_ptr->exp <= EXP_UNDER) && (st0_ptr->tag != TW_Zero) - && !(control_word & CW_Underflow) ) - arith_underflow(st0_ptr); - - return; - } - else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) - { - stack_underflow(); - return; - } - else if ( 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(st0_ptr); return; } /* fprem(?,0) always invalid */ - else if ( st1_tag == TW_Infinity ) - { setcc(0); return; } - } - else if ( st0_tag == TW_Valid ) - { - if ( st1_tag == TW_Zero ) - { - arith_invalid(st0_ptr); /* fprem(Valid,Zero) is invalid */ - return; - } - else if ( st1_tag != TW_NaN ) - { -#ifdef DENORM_OPERAND - if ( (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 ( st0_tag == TW_Infinity ) - { - if ( st1_tag != TW_NaN ) - { - arith_invalid(st0_ptr); /* fprem(Infinity,?) is invalid */ - return; - } - } - - /* One of the registers must contain a NaN is we got here. */ - -#ifdef PARANOID - if ( (st0_tag != TW_NaN) && (st1_tag != TW_NaN) ) - EXCEPTION(EX_INTERNAL | 0x118); -#endif PARANOID - - real_2op_NaN(st1_ptr, st0_ptr, st0_ptr); - -} - - -/* ST(1) <- ST(1) * log ST; pop ST */ -static void fyl2x(FPU_REG *st0_ptr) -{ - char st0_tag = st0_ptr->tag; - FPU_REG *st1_ptr = &st(1), exponent; - char st1_tag = st1_ptr->tag; - int e; - - clear_C1(); - if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) - { - if ( st0_ptr->sign == SIGN_POS ) - { -#ifdef DENORM_OPERAND - if ( ((st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - if ( (st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0) ) - { - /* Special case. The result can be precise. */ - e = st0_ptr->exp - EXP_BIAS; - if ( e > 0 ) - { - exponent.sigh = e; - exponent.sign = SIGN_POS; - } - else - { - exponent.sigh = -e; - exponent.sign = SIGN_NEG; - } - exponent.sigl = 0; - exponent.exp = EXP_BIAS + 31; - exponent.tag = TW_Valid; - normalize_nuo(&exponent); - reg_mul(&exponent, st1_ptr, st1_ptr, FULL_PRECISION); - } - else - { - /* The usual case */ - poly_l2(st0_ptr, st1_ptr, st1_ptr); - if ( st1_ptr->exp <= EXP_UNDER ) - { - /* A denormal result has been produced. - Precision must have been lost, this is always - an underflow. */ - arith_underflow(st1_ptr); - } - else - set_precision_flag_up(); /* 80486 appears to always do this */ - } - pop(); - return; - } - else - { - /* negative */ - if ( !arith_invalid(st1_ptr) ) - pop(); - return; - } - } - else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) - { - stack_underflow_pop(1); - return; - } - else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) - { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); - return; - } - else if ( (st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) ) - { - /* one of the args is zero, the other valid, or both zero */ - if ( st0_tag == TW_Zero ) - { - if ( st1_tag == TW_Zero ) - { - /* Both args zero is invalid */ - if ( !arith_invalid(st1_ptr) ) - pop(); - } -#ifdef PECULIAR_486 - /* This case is not specifically covered in the manual, - but divide-by-zero would seem to be the best response. - However, a real 80486 does it this way... */ - else if ( st0_ptr->tag == TW_Infinity ) - { - reg_move(&CONST_INF, st1_ptr); - pop(); - } -#endif PECULIAR_486 - else - { - if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) ) - pop(); - } - return; - } - else - { - /* st(1) contains zero, st(0) valid <> 0 */ - /* Zero is the valid answer */ - char sign = st1_ptr->sign; - - if ( st0_ptr->sign == SIGN_NEG ) - { - /* log(negative) */ - if ( !arith_invalid(st1_ptr) ) - pop(); - return; - } - -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS; - pop(); st0_ptr = &st(0); - reg_move(&CONST_Z, st0_ptr); - st0_ptr->sign = sign; - return; - } - } - /* One or both arg must be an infinity */ - else if ( st0_tag == TW_Infinity ) - { - if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) ) - { - /* log(-infinity) or 0*log(infinity) */ - if ( !arith_invalid(st1_ptr) ) - pop(); - return; - } - else - { - char sign = st1_ptr->sign; - -#ifdef DENORM_OPERAND - if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - pop(); st0_ptr = &st(0); - reg_move(&CONST_INF, st0_ptr); - st0_ptr->sign = sign; - return; - } - } - /* st(1) must be infinity here */ - else if ( (st0_tag == TW_Valid) && (st0_ptr->sign == SIGN_POS) ) - { - if ( st0_ptr->exp >= EXP_BIAS ) - { - if ( (st0_ptr->exp == EXP_BIAS) && - (st0_ptr->sigh == 0x80000000) && - (st0_ptr->sigl == 0) ) - { - /* st(0) holds 1.0 */ - /* infinity*log(1) */ - if ( !arith_invalid(st1_ptr) ) - pop(); - return; - } - /* st(0) is positive and > 1.0 */ - pop(); - } - else - { - /* st(0) is positive and < 1.0 */ - -#ifdef DENORM_OPERAND - if ( (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 ( st0_ptr->tag == TW_Zero ) - { - /* This should be invalid, but a real 80486 is happy with it. */ -#ifndef PECULIAR_486 - if ( !divide_by_zero(st1_ptr->sign, st1_ptr) ) -#endif PECULIAR_486 - { - st1_ptr->sign ^= SIGN_NEG^SIGN_POS; - pop(); - } - } - else - { - /* log(negative) */ - if ( !arith_invalid(st1_ptr) ) - pop(); - } - return; - } -} - - -static void fpatan(FPU_REG *st0_ptr) -{ - char st0_tag = st0_ptr->tag; - FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; - - clear_C1(); - if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) - { -#ifdef DENORM_OPERAND - if ( ((st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - poly_atan(st0_ptr, st1_ptr, st1_ptr); - - if ( st1_ptr->exp <= EXP_UNDER ) - { - /* A denormal result has been produced. - Precision must have been lost. - This is by definition an underflow. */ - arith_underflow(st1_ptr); - pop(); - return; - } - } - else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) - { - stack_underflow_pop(1); - return; - } - else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) - { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); - return; - } - else if ( (st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) ) - { - char sign = st1_ptr->sign; - if ( st0_tag == TW_Infinity ) - { - if ( st1_tag == TW_Infinity ) - { - if ( 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_tag != TW_Zero ) - { - if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; - } -#endif DENORM_OPERAND - - if ( st0_ptr->sign == SIGN_POS ) - { - reg_move(&CONST_Z, st1_ptr); - st1_ptr->sign = sign; /* An 80486 preserves the sign */ - pop(); - return; - } - else - reg_move(&CONST_PI, st1_ptr); - } - } - else - { - /* st(1) is infinity, st(0) not infinity */ -#ifdef DENORM_OPERAND - if ( st0_tag != TW_Zero ) - { - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; - } -#endif DENORM_OPERAND - - 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 ( st0_tag != TW_Zero ) - { - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; - } -#endif DENORM_OPERAND - - if ( st0_ptr->sign == SIGN_POS ) - { /* An 80486 preserves the sign */ pop(); return; } - else - reg_move(&CONST_PI, st1_ptr); - st1_ptr->sign = sign; - } - else if ( 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 | 0x125); -#endif PARANOID - - pop(); - set_precision_flag_up(); /* We do not really know if up or down */ -} - - -static void fprem(FPU_REG *st0_ptr) -{ - do_fprem(st0_ptr, RC_CHOP); -} - - -static void fprem1(FPU_REG *st0_ptr) -{ - do_fprem(st0_ptr, RC_RND); -} - - -static void fyl2xp1(FPU_REG *st0_ptr) -{ - char st0_tag = st0_ptr->tag, sign; - FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; - - clear_C1(); - if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) - { -#ifdef DENORM_OPERAND - if ( ((st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() ) - return; -#endif DENORM_OPERAND - - if ( poly_l2p1(st0_ptr, st1_ptr, st1_ptr) ) - { -#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ - st1_ptr->sign ^= SIGN_POS^SIGN_NEG; -#else - if ( arith_invalid(st1_ptr) ) /* poly_l2p1() returned invalid */ - return; -#endif PECULIAR_486 - } - if ( st1_ptr->exp <= EXP_UNDER ) - { - /* A denormal result has been produced. - Precision must have been lost, this is always - an underflow. */ - sign = st1_ptr->sign; - arith_underflow(st1_ptr); - st1_ptr->sign = sign; - } - else - set_precision_flag_up(); /* 80486 appears to always do this */ - pop(); - return; - } - else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) - { - stack_underflow_pop(1); - return; - } - else if ( 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 - - st0_ptr->sign ^= st1_ptr->sign; - reg_move(st0_ptr, st1_ptr); - } - else if ( st1_tag == TW_Infinity ) - { - /* Infinity*log(1) */ - if ( !arith_invalid(st1_ptr) ) - pop(); - return; - } - else if ( st1_tag == TW_NaN ) - { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); - return; - } -#ifdef PARANOID - else - { - EXCEPTION(EX_INTERNAL | 0x116); - return; - } -#endif PARANOID - pop(); return; - } - else if ( st0_tag == TW_Valid ) - { - if ( st1_tag == TW_Zero ) - { - if ( st0_ptr->sign == SIGN_NEG ) - { - if ( st0_ptr->exp >= EXP_BIAS ) - { - /* st(0) holds <= -1.0 */ -#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ - st1_ptr->sign ^= SIGN_POS^SIGN_NEG; -#else - if ( arith_invalid(st1_ptr) ) return; -#endif PECULIAR_486 - pop(); return; - } -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - st1_ptr->sign ^= SIGN_POS^SIGN_NEG; - pop(); return; - } -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - pop(); return; - } - if ( st1_tag == TW_Infinity ) - { - if ( st0_ptr->sign == SIGN_NEG ) - { - if ( (st0_ptr->exp >= EXP_BIAS) && - !((st0_ptr->sigh == 0x80000000) && - (st0_ptr->sigl == 0)) ) - { - /* st(0) holds < -1.0 */ -#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ - st1_ptr->sign ^= SIGN_POS^SIGN_NEG; -#else - if ( arith_invalid(st1_ptr) ) return; -#endif PECULIAR_486 - pop(); return; - } -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - st1_ptr->sign ^= SIGN_POS^SIGN_NEG; - pop(); return; - } -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - pop(); return; - } - if ( st1_tag == TW_NaN ) - { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); - return; - } - } - else if ( st0_tag == TW_NaN ) - { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); - return; - } - else if ( st0_tag == TW_Infinity ) - { - if ( st1_tag == TW_NaN ) - { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); - return; - } - else if ( st0_ptr->sign == SIGN_NEG ) - { - int exponent = st1_ptr->exp; -#ifndef PECULIAR_486 - /* This should have higher priority than denormals, but... */ - if ( arith_invalid(st1_ptr) ) /* log(-infinity) */ - return; -#endif PECULIAR_486 -#ifdef DENORM_OPERAND - if ( st1_tag != TW_Zero ) - { - if ( (exponent <= EXP_UNDER) && (denormal_operand()) ) - return; - } -#endif DENORM_OPERAND -#ifdef PECULIAR_486 - /* Denormal operands actually get higher priority */ - if ( arith_invalid(st1_ptr) ) /* log(-infinity) */ - return; -#endif PECULIAR_486 - pop(); - return; - } - else if ( st1_tag == TW_Zero ) - { - /* log(infinity) */ - if ( !arith_invalid(st1_ptr) ) - 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(FPU_REG *st0_ptr) -{ - char st0_tag = st0_ptr->tag; - FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; - int old_cw = control_word; - char sign = st0_ptr->sign; - - clear_C1(); - if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) - { - long scale; - FPU_REG tmp; - -#ifdef DENORM_OPERAND - if ( ((st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - 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 = st0_ptr->sign; - reg_move(&CONST_INF, st0_ptr); - st0_ptr->sign = sign; - } - else - { - EXCEPTION(EX_Underflow); - sign = st0_ptr->sign; - reg_move(&CONST_Z, st0_ptr); - 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 += st0_ptr->exp; - st0_ptr->exp = scale; - - /* Use round_reg() to properly detect under/overflow etc */ - round_reg(st0_ptr, 0, control_word); - - return; - } - else if ( st0_tag == TW_Valid ) - { - if ( st1_tag == TW_Zero ) - { - -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - return; - } - if ( st1_tag == TW_Infinity ) - { -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - - if ( st1_ptr->sign == SIGN_POS ) - { reg_move(&CONST_INF, st0_ptr); } - else - reg_move(&CONST_Z, st0_ptr); - st0_ptr->sign = sign; - return; - } - if ( st1_tag == TW_NaN ) - { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } - } - else if ( 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(st0_ptr); /* Zero scaled by +Infinity */ - return; - } - } - else if ( st1_tag == TW_NaN ) - { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } - } - else if ( st0_tag == TW_Infinity ) - { - if ( st1_tag == TW_Valid ) - { - -#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(st0_ptr); /* Infinity scaled by -Infinity */ - return; - } - else if ( st1_tag == TW_NaN ) - { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } - } - else if ( st0_tag == TW_NaN ) - { - if ( st1_tag != TW_Empty ) - { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } - } - -#ifdef PARANOID - if ( !((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_ST0 const trig_table_a[] = { - f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp -}; - -void trig_a(void) -{ - (trig_table_a[FPU_rm])(&st(0)); -} - - -static FUNC_ST0 const trig_table_b[] = - { - fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos - }; - -void trig_b(void) -{ - (trig_table_b[FPU_rm])(&st(0)); -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/get_address.c linux/drivers/FPU-emu/get_address.c --- v1.1.76/linux/drivers/FPU-emu/get_address.c Fri Aug 19 08:54:01 1994 +++ linux/drivers/FPU-emu/get_address.c Thu Jan 1 02:00:00 1970 @@ -1,423 +0,0 @@ -/*---------------------------------------------------------------------------+ - | get_address.c | - | | - | Get the effective address from an FPU instruction. | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | 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 -#include - -#include - -#include "fpu_system.h" -#include "exception.h" -#include "fpu_emu.h" - - -#define FPU_WRITE_BIT 0x10 - -static int reg_offset[] = { - offsetof(struct info,___eax), - offsetof(struct info,___ecx), - offsetof(struct info,___edx), - offsetof(struct info,___ebx), - offsetof(struct info,___esp), - offsetof(struct info,___ebp), - offsetof(struct info,___esi), - offsetof(struct info,___edi) -}; - -#define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info)) - -static int reg_offset_vm86[] = { - offsetof(struct info,___cs), - offsetof(struct info,___vm86_ds), - offsetof(struct info,___vm86_es), - offsetof(struct info,___vm86_fs), - offsetof(struct info,___vm86_gs), - offsetof(struct info,___ss), - offsetof(struct info,___vm86_ds) - }; - -#define VM86_REG_(x) (*(unsigned short *) \ - (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info)) - -static int reg_offset_pm[] = { - offsetof(struct info,___cs), - offsetof(struct info,___ds), - offsetof(struct info,___es), - offsetof(struct info,___fs), - offsetof(struct info,___gs), - offsetof(struct info,___ss), - offsetof(struct info,___ds) - }; - -#define PM_REG_(x) (*(unsigned short *) \ - (reg_offset_pm[((unsigned)x)]+(char *) FPU_info)) - - -/* Decode the SIB byte. This function assumes mod != 0 */ -static int sib(int mod, unsigned long *fpu_eip) -{ - unsigned char ss,index,base; - long offset; - - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(1); - base = get_fs_byte((char *) (*fpu_eip)); /* The SIB byte */ - RE_ENTRANT_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 */ - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(1); - offset += (signed char) get_fs_byte((char *) (*fpu_eip)); - RE_ENTRANT_CHECK_ON; - (*fpu_eip)++; - } - else if (mod == 2 || base == 5) /* The second condition also has mod==0 */ - { - /* 32 bit displacement */ - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(4); - offset += (signed) get_fs_long((unsigned long *) (*fpu_eip)); - RE_ENTRANT_CHECK_ON; - (*fpu_eip) += 4; - } - - return offset; -} - - -static unsigned long vm86_segment(unsigned char segment, - unsigned short *selector) -{ - segment--; -#ifdef PARANOID - if ( segment > PREFIX_SS_ ) - { - EXCEPTION(EX_INTERNAL|0x130); - math_abort(FPU_info,SIGSEGV); - } -#endif PARANOID - *selector = VM86_REG_(segment); - return (unsigned long)VM86_REG_(segment) << 4; -} - - -/* This should work for 16 and 32 bit protected mode. */ -static long pm_address(unsigned char FPU_modrm, unsigned char segment, - unsigned short *selector, long offset) -{ - struct desc_struct descriptor; - unsigned long base_address, limit, address, seg_top; - - segment--; -#ifdef PARANOID - if ( segment > PREFIX_SS_ ) - { - EXCEPTION(EX_INTERNAL|0x132); - math_abort(FPU_info,SIGSEGV); - } -#endif PARANOID - - *selector = PM_REG_(segment); - - descriptor = LDT_DESCRIPTOR(PM_REG_(segment)); - base_address = SEG_BASE_ADDR(descriptor); - address = base_address + offset; - limit = base_address - + (SEG_LIMIT(descriptor)+1) * SEG_GRANULARITY(descriptor) - 1; - if ( limit < base_address ) limit = 0xffffffff; - - if ( SEG_EXPAND_DOWN(descriptor) ) - { - if ( SEG_G_BIT(descriptor) ) - seg_top = 0xffffffff; - else - { - seg_top = base_address + (1 << 20); - if ( seg_top < base_address ) seg_top = 0xffffffff; - } - access_limit = - (address <= limit) || (address >= seg_top) ? 0 : - ((seg_top-address) >= 255 ? 255 : seg_top-address); - } - else - { - access_limit = - (address > limit) || (address < base_address) ? 0 : - ((limit-address) >= 254 ? 255 : limit-address+1); - } - if ( SEG_EXECUTE_ONLY(descriptor) || - (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT)) ) - { - access_limit = 0; - } - return address; -} - - -/* - 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 long *fpu_eip, - struct address *addr, -/* unsigned short *selector, unsigned long *offset, */ - fpu_addr_modes addr_modes) -{ - unsigned char mod; - unsigned rm = FPU_modrm & 7; - long *cpu_reg_ptr; - int address = 0; /* Initialized just to stop compiler warnings. */ - - /* Memory accessed via the cs selector is write protected - in `non-segmented' 32 bit protected mode. */ - if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) - && (addr_modes.override.segment == PREFIX_CS_) ) - { - math_abort(FPU_info,SIGSEGV); - } - - addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ - - mod = (FPU_modrm >> 6) & 3; - - if (rm == 4 && mod != 3) - { - address = sib(mod, fpu_eip); - } - else - { - cpu_reg_ptr = & REG_(rm); - switch (mod) - { - case 0: - if (rm == 5) - { - /* Special case: disp32 */ - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(4); - address = get_fs_long((unsigned long *) (*fpu_eip)); - (*fpu_eip) += 4; - RE_ENTRANT_CHECK_ON; - addr->offset = address; - return (void *) address; - } - else - { - address = *cpu_reg_ptr; /* Just return the contents - of the cpu register */ - addr->offset = address; - return (void *) address; - } - case 1: - /* 8 bit signed displacement */ - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(1); - address = (signed char) get_fs_byte((char *) (*fpu_eip)); - RE_ENTRANT_CHECK_ON; - (*fpu_eip)++; - break; - case 2: - /* 32 bit displacement */ - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(4); - address = (signed) get_fs_long((unsigned long *) (*fpu_eip)); - (*fpu_eip) += 4; - RE_ENTRANT_CHECK_ON; - break; - case 3: - /* Not legal for the FPU */ - EXCEPTION(EX_Invalid); - } - address += *cpu_reg_ptr; - } - - addr->offset = address; - - switch ( addr_modes.default_mode ) - { - case 0: - break; - case VM86: - address += vm86_segment(addr_modes.override.segment, - (unsigned short *)&(addr->selector)); - break; - case PM16: - case SEG32: - address = pm_address(FPU_modrm, addr_modes.override.segment, - (unsigned short *)&(addr->selector), address); - break; - default: - EXCEPTION(EX_INTERNAL|0x133); - } - - return (void *)address; -} - - -void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, - struct address *addr, -/* unsigned short *selector, unsigned long *offset, */ - fpu_addr_modes addr_modes) -{ - unsigned char mod; - unsigned rm = FPU_modrm & 7; - int address = 0; /* Default used for mod == 0 */ - - /* Memory accessed via the cs selector is write protected - in `non-segmented' 32 bit protected mode. */ - if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) - && (addr_modes.override.segment == PREFIX_CS_) ) - { - math_abort(FPU_info,SIGSEGV); - } - - addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ - - mod = (FPU_modrm >> 6) & 3; - - switch (mod) - { - case 0: - if (rm == 6) - { - /* Special case: disp16 */ - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(2); - address = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip)); - (*fpu_eip) += 2; - RE_ENTRANT_CHECK_ON; - goto add_segment; - } - break; - case 1: - /* 8 bit signed displacement */ - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(1); - address = (signed char) get_fs_byte((signed char *) (*fpu_eip)); - RE_ENTRANT_CHECK_ON; - (*fpu_eip)++; - break; - case 2: - /* 16 bit displacement */ - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(2); - address = (unsigned) get_fs_word((unsigned short *) (*fpu_eip)); - (*fpu_eip) += 2; - RE_ENTRANT_CHECK_ON; - break; - case 3: - /* Not legal for the FPU */ - EXCEPTION(EX_Invalid); - break; - } - switch ( rm ) - { - case 0: - address += FPU_info->___ebx + FPU_info->___esi; - break; - case 1: - address += FPU_info->___ebx + FPU_info->___edi; - break; - case 2: - address += FPU_info->___ebp + FPU_info->___esi; - if ( addr_modes.override.segment == PREFIX_DEFAULT ) - addr_modes.override.segment = PREFIX_SS_; - break; - case 3: - address += FPU_info->___ebp + FPU_info->___edi; - if ( addr_modes.override.segment == PREFIX_DEFAULT ) - addr_modes.override.segment = PREFIX_SS_; - break; - case 4: - address += FPU_info->___esi; - break; - case 5: - address += FPU_info->___edi; - break; - case 6: - address += FPU_info->___ebp; - if ( addr_modes.override.segment == PREFIX_DEFAULT ) - addr_modes.override.segment = PREFIX_SS_; - break; - case 7: - address += FPU_info->___ebx; - break; - } - - add_segment: - address &= 0xffff; - - addr->offset = address; - - switch ( addr_modes.default_mode ) - { - case 0: - break; - case VM86: - address += vm86_segment(addr_modes.override.segment, - (unsigned short *)&(addr->selector)); - break; - case PM16: - case SEG32: - address = pm_address(FPU_modrm, addr_modes.override.segment, - (unsigned short *)&(addr->selector), address); - break; - default: - EXCEPTION(EX_INTERNAL|0x131); - } - - return (void *)address ; -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/load_store.c linux/drivers/FPU-emu/load_store.c --- v1.1.76/linux/drivers/FPU-emu/load_store.c Thu Jun 2 10:28:26 1994 +++ linux/drivers/FPU-emu/load_store.c Thu Jan 1 02:00:00 1970 @@ -1,260 +0,0 @@ -/*---------------------------------------------------------------------------+ - | 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,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | 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 - -#include "fpu_system.h" -#include "exception.h" -#include "fpu_emu.h" -#include "status_w.h" -#include "control_w.h" - - -#define _NONE_ 0 /* 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() { st0_ptr->tag = TW_Empty; top++; } - - -static unsigned char const 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_ - }; - -unsigned char const data_sizes_16[32] = { - 4, 4, 8, 2, 0, 0, 0, 0, - 4, 4, 8, 2, 4, 4, 8, 2, - 14, 0, 94, 10, 2, 10, 0, 8, - 14, 0, 94, 10, 2, 10, 2, 8 -}; - -unsigned char const data_sizes_32[32] = { - 4, 4, 8, 2, 0, 0, 0, 0, - 4, 4, 8, 2, 4, 4, 8, 2, - 28, 0,108, 10, 2, 10, 0, 8, - 28, 0,108, 10, 2, 10, 2, 8 -}; - -int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, - void *data_address) -{ - FPU_REG loaded_data; - FPU_REG *st0_ptr; - - st0_ptr = NULL; /* Initialized just to stop compiler warnings. */ - - if ( addr_modes.default_mode & PROTECTED ) - { - if ( addr_modes.default_mode == SEG32 ) - { - if ( access_limit < data_sizes_32[type] ) - math_abort(FPU_info,SIGSEGV); - } - else if ( addr_modes.default_mode == PM16 ) - { - if ( access_limit < data_sizes_16[type] ) - math_abort(FPU_info,SIGSEGV); - } -#ifdef PARANOID - else - EXCEPTION(EX_INTERNAL|0x140); -#endif PARANOID - } - - switch ( type_table[type] ) - { - case _NONE_: - break; - case _REG0_: - st0_ptr = &st(0); /* Some of these instructions pop after - storing */ - break; - case _PUSH_: - { - st0_ptr = &st(-1); - if ( st0_ptr->tag != TW_Empty ) - { stack_overflow(); return 0; } - top--; - } - break; - case _null_: - FPU_illegal(); - return 0; -#ifdef PARANOID - default: - EXCEPTION(EX_INTERNAL|0x141); - return 0; -#endif PARANOID - } - - switch ( type ) - { - case 000: /* fld m32real */ - clear_C1(); - reg_load_single((float *)data_address, &loaded_data); - if ( (loaded_data.tag == TW_NaN) && - real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) - { - top++; - break; - } - reg_move(&loaded_data, st0_ptr); - break; - case 001: /* fild m32int */ - clear_C1(); - reg_load_int32((long *)data_address, st0_ptr); - break; - case 002: /* fld m64real */ - clear_C1(); - reg_load_double((double *)data_address, &loaded_data); - if ( (loaded_data.tag == TW_NaN) && - real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) - { - top++; - break; - } - reg_move(&loaded_data, st0_ptr); - break; - case 003: /* fild m16int */ - clear_C1(); - reg_load_int16((short *)data_address, st0_ptr); - break; - case 010: /* fst m32real */ - clear_C1(); - reg_store_single((float *)data_address, st0_ptr); - break; - case 011: /* fist m32int */ - clear_C1(); - reg_store_int32((long *)data_address, st0_ptr); - break; - case 012: /* fst m64real */ - clear_C1(); - reg_store_double((double *)data_address, st0_ptr); - break; - case 013: /* fist m16int */ - clear_C1(); - reg_store_int16((short *)data_address, st0_ptr); - break; - case 014: /* fstp m32real */ - clear_C1(); - if ( reg_store_single((float *)data_address, st0_ptr) ) - pop_0(); /* pop only if the number was actually stored - (see the 80486 manual p16-28) */ - break; - case 015: /* fistp m32int */ - clear_C1(); - if ( reg_store_int32((long *)data_address, st0_ptr) ) - pop_0(); /* pop only if the number was actually stored - (see the 80486 manual p16-28) */ - break; - case 016: /* fstp m64real */ - clear_C1(); - if ( reg_store_double((double *)data_address, st0_ptr) ) - pop_0(); /* pop only if the number was actually stored - (see the 80486 manual p16-28) */ - break; - case 017: /* fistp m16int */ - clear_C1(); - if ( reg_store_int16((short *)data_address, st0_ptr) ) - pop_0(); /* pop only if the number was actually stored - (see the 80486 manual p16-28) */ - break; - case 020: /* fldenv m14/28byte */ - fldenv(addr_modes, (char *)data_address); - /* Ensure that the values just loaded are not changed by - fix-up operations. */ - return 1; - case 022: /* frstor m94/108byte */ - frstor(addr_modes, (char *)data_address); - /* Ensure that the values just loaded are not changed by - fix-up operations. */ - return 1; - case 023: /* fbld m80dec */ - clear_C1(); - reg_load_bcd((char *)data_address, st0_ptr); - break; - case 024: /* fldcw */ - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, data_address, 2); - control_word = get_fs_word((unsigned short *) data_address); - RE_ENTRANT_CHECK_ON; - if ( partial_status & ~control_word & CW_Exceptions ) - partial_status |= (SW_Summary | SW_Backward); - else - partial_status &= ~(SW_Summary | SW_Backward); -#ifdef PECULIAR_486 - control_word |= 0x40; /* An 80486 appears to always set this bit */ -#endif PECULIAR_486 - return 1; - case 025: /* fld m80real */ - clear_C1(); - reg_load_extended((long double *)data_address, st0_ptr); - break; - case 027: /* fild m64int */ - clear_C1(); - reg_load_int64((long long *)data_address, st0_ptr); - break; - case 030: /* fstenv m14/28byte */ - fstenv(addr_modes, (char *)data_address); - return 1; - case 032: /* fsave */ - fsave(addr_modes, (char *)data_address); - return 1; - case 033: /* fbstp m80dec */ - clear_C1(); - if ( reg_store_bcd((char *)data_address, st0_ptr) ) - pop_0(); /* pop only if the number was actually stored - (see the 80486 manual p16-28) */ - break; - case 034: /* fstcw m16int */ - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,data_address,2); - put_fs_word(control_word, (short *) data_address); - RE_ENTRANT_CHECK_ON; - return 1; - case 035: /* fstp m80real */ - clear_C1(); - if ( reg_store_extended((long double *)data_address, st0_ptr) ) - pop_0(); /* pop only if the number was actually stored - (see the 80486 manual p16-28) */ - break; - case 036: /* fstsw m2byte */ - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,data_address,2); - put_fs_word(status_word(),(short *) data_address); - RE_ENTRANT_CHECK_ON; - return 1; - case 037: /* fistp m64int */ - clear_C1(); - if ( reg_store_int64((long long *)data_address, st0_ptr) ) - pop_0(); /* pop only if the number was actually stored - (see the 80486 manual p16-28) */ - break; - } - return 0; -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/mul_Xsig.S linux/drivers/FPU-emu/mul_Xsig.S --- v1.1.76/linux/drivers/FPU-emu/mul_Xsig.S Mon Aug 1 08:19:13 1994 +++ linux/drivers/FPU-emu/mul_Xsig.S Thu Jan 1 02:00:00 1970 @@ -1,182 +0,0 @@ -/*---------------------------------------------------------------------------+ - | mul_Xsig.S | - | | - | Multiply a 12 byte fixed point number by another fixed point number. | - | | - | Copyright (C) 1992,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | Call from C as: | - | void mul32_Xsig(Xsig *x, unsigned b) | - | | - | void mul64_Xsig(Xsig *x, unsigned long long *b) | - | | - | void mul_Xsig_Xsig(Xsig *x, unsigned *b) | - | | - | The result is neither rounded nor normalized, and the ls bit or so may | - | be wrong. | - | | - +---------------------------------------------------------------------------*/ - .file "mul_Xsig.S" - - -#include "fpu_asm.h" - -.text - .align 2,144 -.globl _mul32_Xsig -_mul32_Xsig: - pushl %ebp - movl %esp,%ebp - subl $16,%esp - pushl %esi - - movl PARAM1,%esi - movl PARAM2,%ecx - - xor %eax,%eax - movl %eax,-4(%ebp) - movl %eax,-8(%ebp) - - movl (%esi),%eax /* lsl of Xsig */ - mull %ecx /* msl of b */ - movl %edx,-12(%ebp) - - movl 4(%esi),%eax /* midl of Xsig */ - mull %ecx /* msl of b */ - addl %eax,-12(%ebp) - adcl %edx,-8(%ebp) - adcl $0,-4(%ebp) - - movl 8(%esi),%eax /* msl of Xsig */ - mull %ecx /* msl of b */ - addl %eax,-8(%ebp) - adcl %edx,-4(%ebp) - - movl -12(%ebp),%eax - movl %eax,(%esi) - movl -8(%ebp),%eax - movl %eax,4(%esi) - movl -4(%ebp),%eax - movl %eax,8(%esi) - - popl %esi - leave - ret - - - .align 2,144 -.globl _mul64_Xsig -_mul64_Xsig: - pushl %ebp - movl %esp,%ebp - subl $16,%esp - pushl %esi - - movl PARAM1,%esi - movl PARAM2,%ecx - - xor %eax,%eax - movl %eax,-4(%ebp) - movl %eax,-8(%ebp) - - movl (%esi),%eax /* lsl of Xsig */ - mull 4(%ecx) /* msl of b */ - movl %edx,-12(%ebp) - - movl 4(%esi),%eax /* midl of Xsig */ - mull (%ecx) /* lsl of b */ - addl %edx,-12(%ebp) - adcl $0,-8(%ebp) - adcl $0,-4(%ebp) - - movl 4(%esi),%eax /* midl of Xsig */ - mull 4(%ecx) /* msl of b */ - addl %eax,-12(%ebp) - adcl %edx,-8(%ebp) - adcl $0,-4(%ebp) - - movl 8(%esi),%eax /* msl of Xsig */ - mull (%ecx) /* lsl of b */ - addl %eax,-12(%ebp) - adcl %edx,-8(%ebp) - adcl $0,-4(%ebp) - - movl 8(%esi),%eax /* msl of Xsig */ - mull 4(%ecx) /* msl of b */ - addl %eax,-8(%ebp) - adcl %edx,-4(%ebp) - - movl -12(%ebp),%eax - movl %eax,(%esi) - movl -8(%ebp),%eax - movl %eax,4(%esi) - movl -4(%ebp),%eax - movl %eax,8(%esi) - - popl %esi - leave - ret - - - - .align 2,144 -.globl _mul_Xsig_Xsig -_mul_Xsig_Xsig: - pushl %ebp - movl %esp,%ebp - subl $16,%esp - pushl %esi - - movl PARAM1,%esi - movl PARAM2,%ecx - - xor %eax,%eax - movl %eax,-4(%ebp) - movl %eax,-8(%ebp) - - movl (%esi),%eax /* lsl of Xsig */ - mull 8(%ecx) /* msl of b */ - movl %edx,-12(%ebp) - - movl 4(%esi),%eax /* midl of Xsig */ - mull 4(%ecx) /* midl of b */ - addl %edx,-12(%ebp) - adcl $0,-8(%ebp) - adcl $0,-4(%ebp) - - movl 8(%esi),%eax /* msl of Xsig */ - mull (%ecx) /* lsl of b */ - addl %edx,-12(%ebp) - adcl $0,-8(%ebp) - adcl $0,-4(%ebp) - - movl 4(%esi),%eax /* midl of Xsig */ - mull 8(%ecx) /* msl of b */ - addl %eax,-12(%ebp) - adcl %edx,-8(%ebp) - adcl $0,-4(%ebp) - - movl 8(%esi),%eax /* msl of Xsig */ - mull 4(%ecx) /* midl of b */ - addl %eax,-12(%ebp) - adcl %edx,-8(%ebp) - adcl $0,-4(%ebp) - - movl 8(%esi),%eax /* msl of Xsig */ - mull 8(%ecx) /* msl of b */ - addl %eax,-8(%ebp) - adcl %edx,-4(%ebp) - - movl -12(%ebp),%edx - movl %edx,(%esi) - movl -8(%ebp),%edx - movl %edx,4(%esi) - movl -4(%ebp),%edx - movl %edx,8(%esi) - - popl %esi - leave - ret - diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/poly.h linux/drivers/FPU-emu/poly.h --- v1.1.76/linux/drivers/FPU-emu/poly.h Mon Aug 1 08:19:14 1994 +++ linux/drivers/FPU-emu/poly.h Thu Jan 1 02:00:00 1970 @@ -1,116 +0,0 @@ -/*---------------------------------------------------------------------------+ - | poly.h | - | | - | Header file for the FPU-emu poly*.c source files. | - | | - | Copyright (C) 1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | Declarations and definitions for functions operating on Xsig (12-byte | - | extended-significand) quantities. | - | | - +---------------------------------------------------------------------------*/ - -#ifndef _POLY_H -#define _POLY_H - -/* This 12-byte structure is used to improve the accuracy of computation - of transcendental functions. - Intended to be used to get results better than 8-byte computation - allows. 9-byte would probably be sufficient. - */ -typedef struct { - unsigned long lsw; - unsigned long midw; - unsigned long msw; -} Xsig; - -asmlinkage void mul64(unsigned long long const *a, unsigned long long const *b, - unsigned long long *result); -asmlinkage void polynomial_Xsig(Xsig *, const unsigned long long *x, - const unsigned long long terms[], const int n); - -asmlinkage void mul32_Xsig(Xsig *, const unsigned long mult); -asmlinkage void mul64_Xsig(Xsig *, const unsigned long long *mult); -asmlinkage void mul_Xsig_Xsig(Xsig *dest, const Xsig *mult); - -asmlinkage void shr_Xsig(Xsig *, const int n); -asmlinkage int round_Xsig(Xsig *); -asmlinkage int norm_Xsig(Xsig *); -asmlinkage void div_Xsig(Xsig *x1, const Xsig *x2, const Xsig *dest); - -/* Macro to extract the most significant 32 bits from a long long */ -#define LL_MSW(x) (((unsigned long *)&x)[1]) - -/* Macro to initialize an Xsig struct */ -#define MK_XSIG(a,b,c) { c, b, a } - -/* Macro to access the 8 ms bytes of an Xsig as a long long */ -#define XSIG_LL(x) (*(unsigned long long *)&x.midw) - - -/* - Need to run gcc with optimizations on to get these to - actually be in-line. - */ - -/* Multiply two fixed-point 32 bit numbers. */ -extern inline void mul_32_32(const unsigned long arg1, - const unsigned long arg2, - unsigned long *out) -{ - asm volatile ("movl %1,%%eax; mull %2; movl %%edx,%0" \ - :"=g" (*out) \ - :"g" (arg1), "g" (arg2) \ - :"ax","dx"); -} - - -/* Add the 12 byte Xsig x2 to Xsig dest, with no checks for overflow. */ -extern inline void add_Xsig_Xsig(Xsig *dest, const Xsig *x2) -{ - asm volatile ("movl %1,%%edi; movl %2,%%esi; - movl (%%esi),%%eax; addl %%eax,(%%edi); - movl 4(%%esi),%%eax; adcl %%eax,4(%%edi); - movl 8(%%esi),%%eax; adcl %%eax,8(%%edi);" - :"=g" (*dest):"g" (dest), "g" (x2) - :"ax","si","di"); -} - - -/* Add the 12 byte Xsig x2 to Xsig dest, adjust exp if overflow occurs. */ -/* Note: the constraints in the asm statement didn't always work properly - with gcc 2.5.8. Changing from using edi to using ecx got around the - problem, but keep fingers crossed! */ -extern inline int add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp) -{ - asm volatile ("movl %2,%%ecx; movl %3,%%esi; - movl (%%esi),%%eax; addl %%eax,(%%ecx); - movl 4(%%esi),%%eax; adcl %%eax,4(%%ecx); - movl 8(%%esi),%%eax; adcl %%eax,8(%%ecx); - jnc 0f; - rcrl 8(%%ecx); rcrl 4(%%ecx); rcrl (%%ecx) - movl %4,%%ecx; incl (%%ecx) - movl $1,%%eax; jmp 1f; - 0: xorl %%eax,%%eax; - 1:" - :"=g" (*exp), "=g" (*dest) - :"g" (dest), "g" (x2), "g" (exp) - :"cx","si","ax"); -} - - -/* Negate (subtract from 1.0) the 12 byte Xsig */ -/* This is faster in a loop on my 386 than using the "neg" instruction. */ -extern inline void negate_Xsig(Xsig *x) -{ - asm volatile("movl %1,%%esi; " - "xorl %%ecx,%%ecx; " - "movl %%ecx,%%eax; subl (%%esi),%%eax; movl %%eax,(%%esi); " - "movl %%ecx,%%eax; sbbl 4(%%esi),%%eax; movl %%eax,4(%%esi); " - "movl %%ecx,%%eax; sbbl 8(%%esi),%%eax; movl %%eax,8(%%esi); " - :"=g" (*x):"g" (x):"si","ax","cx"); -} - -#endif _POLY_H diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/poly_2xm1.c linux/drivers/FPU-emu/poly_2xm1.c --- v1.1.76/linux/drivers/FPU-emu/poly_2xm1.c Mon Aug 1 08:19:14 1994 +++ linux/drivers/FPU-emu/poly_2xm1.c Thu Jan 1 02:00:00 1970 @@ -1,152 +0,0 @@ -/*---------------------------------------------------------------------------+ - | poly_2xm1.c | - | | - | Function to compute 2^x-1 by a polynomial approximation. | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -#include "exception.h" -#include "reg_constant.h" -#include "fpu_emu.h" -#include "control_w.h" -#include "poly.h" - - -#define HIPOWER 11 -static const unsigned long long lterms[HIPOWER] = -{ - 0x0000000000000000LL, /* This term done separately as 12 bytes */ - 0xf5fdeffc162c7543LL, - 0x1c6b08d704a0bfa6LL, - 0x0276556df749cc21LL, - 0x002bb0ffcf14f6b8LL, - 0x0002861225ef751cLL, - 0x00001ffcbfcd5422LL, - 0x00000162c005d5f1LL, - 0x0000000da96ccb1bLL, - 0x0000000078d1b897LL, - 0x000000000422b029LL -}; - -static const Xsig hiterm = MK_XSIG(0xb17217f7, 0xd1cf79ab, 0xc8a39194); - -/* Four slices: 0.0 : 0.25 : 0.50 : 0.75 : 1.0, - These numbers are 2^(1/4), 2^(1/2), and 2^(3/4) - */ -static const Xsig shiftterm0 = MK_XSIG(0, 0, 0); -static const Xsig shiftterm1 = MK_XSIG(0x9837f051, 0x8db8a96f, 0x46ad2318); -static const Xsig shiftterm2 = MK_XSIG(0xb504f333, 0xf9de6484, 0x597d89b3); -static const Xsig shiftterm3 = MK_XSIG(0xd744fcca, 0xd69d6af4, 0x39a68bb9); - -static const Xsig *shiftterm[] = { &shiftterm0, &shiftterm1, - &shiftterm2, &shiftterm3 }; - - -/*--- poly_2xm1() -----------------------------------------------------------+ - | Requires an argument which is TW_Valid and < 1. | - +---------------------------------------------------------------------------*/ -int poly_2xm1(FPU_REG const *arg, FPU_REG *result) -{ - long int exponent, shift; - unsigned long long Xll; - Xsig accumulator, Denom, argSignif; - - - exponent = arg->exp - EXP_BIAS; - -#ifdef PARANOID - if ( (exponent >= 0) /* Don't want a |number| >= 1.0 */ - || (arg->tag != TW_Valid) ) - { - /* Number negative, too large, or not Valid. */ - EXCEPTION(EX_INTERNAL|0x127); - return 1; - } -#endif PARANOID - - argSignif.lsw = 0; - XSIG_LL(argSignif) = Xll = significand(arg); - - if ( exponent == -1 ) - { - shift = (argSignif.msw & 0x40000000) ? 3 : 2; - /* subtract 0.5 or 0.75 */ - exponent -= 2; - XSIG_LL(argSignif) <<= 2; - Xll <<= 2; - } - else if ( exponent == -2 ) - { - shift = 1; - /* subtract 0.25 */ - exponent--; - XSIG_LL(argSignif) <<= 1; - Xll <<= 1; - } - else - shift = 0; - - if ( exponent < -2 ) - { - /* Shift the argument right by the required places. */ - if ( shrx(&Xll, -2-exponent) >= 0x80000000U ) - Xll++; /* round up */ - } - - accumulator.lsw = accumulator.midw = accumulator.msw = 0; - polynomial_Xsig(&accumulator, &Xll, lterms, HIPOWER-1); - mul_Xsig_Xsig(&accumulator, &argSignif); - shr_Xsig(&accumulator, 3); - - mul_Xsig_Xsig(&argSignif, &hiterm); /* The leading term */ - add_two_Xsig(&accumulator, &argSignif, &exponent); - - if ( shift ) - { - /* The argument is large, use the identity: - f(x+a) = f(a) * (f(x) + 1) - 1; - */ - shr_Xsig(&accumulator, - exponent); - accumulator.msw |= 0x80000000; /* add 1.0 */ - mul_Xsig_Xsig(&accumulator, shiftterm[shift]); - accumulator.msw &= 0x3fffffff; /* subtract 1.0 */ - exponent = 1; - } - - if ( arg->sign != SIGN_POS ) - { - /* The argument is negative, use the identity: - f(-x) = -f(x) / (1 + f(x)) - */ - Denom.lsw = accumulator.lsw; - XSIG_LL(Denom) = XSIG_LL(accumulator); - if ( exponent < 0 ) - shr_Xsig(&Denom, - exponent); - else if ( exponent > 0 ) - { - /* exponent must be 1 here */ - XSIG_LL(Denom) <<= 1; - if ( Denom.lsw & 0x80000000 ) - XSIG_LL(Denom) |= 1; - (Denom.lsw) <<= 1; - } - Denom.msw |= 0x80000000; /* add 1.0 */ - div_Xsig(&accumulator, &Denom, &accumulator); - } - - /* Convert to 64 bit signed-compatible */ - exponent += round_Xsig(&accumulator); - - significand(result) = XSIG_LL(accumulator); - result->tag = TW_Valid; - result->exp = exponent + EXP_BIAS; - result->sign = arg->sign; - - return 0; - -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/poly_atan.c linux/drivers/FPU-emu/poly_atan.c --- v1.1.76/linux/drivers/FPU-emu/poly_atan.c Mon Aug 1 08:19:14 1994 +++ linux/drivers/FPU-emu/poly_atan.c Thu Jan 1 02:00:00 1970 @@ -1,197 +0,0 @@ -/*---------------------------------------------------------------------------+ - | poly_atan.c | - | | - | Compute the arctan of a FPU_REG, using a polynomial approximation. | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -#include "exception.h" -#include "reg_constant.h" -#include "fpu_emu.h" -#include "status_w.h" -#include "control_w.h" -#include "poly.h" - - -#define HIPOWERon 6 /* odd poly, negative terms */ -static const unsigned long long oddnegterms[HIPOWERon] = -{ - 0x0000000000000000LL, /* Dummy (not for - 1.0) */ - 0x015328437f756467LL, - 0x0005dda27b73dec6LL, - 0x0000226bf2bfb91aLL, - 0x000000ccc439c5f7LL, - 0x0000000355438407LL -} ; - -#define HIPOWERop 6 /* odd poly, positive terms */ -static const unsigned long long oddplterms[HIPOWERop] = -{ -/* 0xaaaaaaaaaaaaaaabLL, transferred to fixedpterm[] */ - 0x0db55a71875c9ac2LL, - 0x0029fce2d67880b0LL, - 0x0000dfd3908b4596LL, - 0x00000550fd61dab4LL, - 0x0000001c9422b3f9LL, - 0x000000003e3301e1LL -}; - -static const unsigned long long denomterm = 0xebd9b842c5c53a0eLL; - -static const Xsig fixedpterm = MK_XSIG(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa); - -static const Xsig pi_signif = MK_XSIG(0xc90fdaa2, 0x2168c234, 0xc4c6628b); - - -/*--- poly_atan() -----------------------------------------------------------+ - | | - +---------------------------------------------------------------------------*/ -void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result) -{ - char transformed, inverted, - sign1 = arg1->sign, sign2 = arg2->sign; - long int exponent, dummy_exp; - Xsig accumulator, Numer, Denom, accumulatore, argSignif, - argSq, argSqSq; - - - arg1->sign = arg2->sign = SIGN_POS; - if ( (compare(arg2) & ~COMP_Denormal) == COMP_A_lt_B ) - { - inverted = 1; - exponent = arg1->exp - arg2->exp; - Numer.lsw = Denom.lsw = 0; - XSIG_LL(Numer) = significand(arg1); - XSIG_LL(Denom) = significand(arg2); - } - else - { - inverted = 0; - exponent = arg2->exp - arg1->exp; - Numer.lsw = Denom.lsw = 0; - XSIG_LL(Numer) = significand(arg2); - XSIG_LL(Denom) = significand(arg1); - } - div_Xsig(&Numer, &Denom, &argSignif); - exponent += norm_Xsig(&argSignif); - - if ( (exponent >= -1) - || ((exponent == -2) && (argSignif.msw > 0xd413ccd0)) ) - { - /* The argument is greater than sqrt(2)-1 (=0.414213562...) */ - /* Convert the argument by an identity for atan */ - transformed = 1; - - if ( exponent >= 0 ) - { -#ifdef PARANOID - if ( !( (exponent == 0) && - (argSignif.lsw == 0) && (argSignif.midw == 0) && - (argSignif.msw == 0x80000000) ) ) - { - EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */ - return; - } -#endif PARANOID - argSignif.msw = 0; /* Make the transformed arg -> 0.0 */ - } - else - { - Numer.lsw = Denom.lsw = argSignif.lsw; - XSIG_LL(Numer) = XSIG_LL(Denom) = XSIG_LL(argSignif); - - if ( exponent < -1 ) - shr_Xsig(&Numer, -1-exponent); - negate_Xsig(&Numer); - - shr_Xsig(&Denom, -exponent); - Denom.msw |= 0x80000000; - - div_Xsig(&Numer, &Denom, &argSignif); - - exponent = -1 + norm_Xsig(&argSignif); - } - } - else - { - transformed = 0; - } - - argSq.lsw = argSignif.lsw; argSq.midw = argSignif.midw; - argSq.msw = argSignif.msw; - mul_Xsig_Xsig(&argSq, &argSq); - - argSqSq.lsw = argSq.lsw; argSqSq.midw = argSq.midw; argSqSq.msw = argSq.msw; - mul_Xsig_Xsig(&argSqSq, &argSqSq); - - accumulatore.lsw = argSq.lsw; - XSIG_LL(accumulatore) = XSIG_LL(argSq); - - shr_Xsig(&argSq, 2*(-1-exponent-1)); - shr_Xsig(&argSqSq, 4*(-1-exponent-1)); - - /* Now have argSq etc with binary point at the left - .1xxxxxxxx */ - - /* Do the basic fixed point polynomial evaluation */ - accumulator.msw = accumulator.midw = accumulator.lsw = 0; - polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), - oddplterms, HIPOWERop-1); - mul64_Xsig(&accumulator, &XSIG_LL(argSq)); - negate_Xsig(&accumulator); - polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddnegterms, HIPOWERon-1); - negate_Xsig(&accumulator); - add_two_Xsig(&accumulator, &fixedpterm, &dummy_exp); - - mul64_Xsig(&accumulatore, &denomterm); - shr_Xsig(&accumulatore, 1 + 2*(-1-exponent)); - accumulatore.msw |= 0x80000000; - - div_Xsig(&accumulator, &accumulatore, &accumulator); - - mul_Xsig_Xsig(&accumulator, &argSignif); - mul_Xsig_Xsig(&accumulator, &argSq); - - shr_Xsig(&accumulator, 3); - negate_Xsig(&accumulator); - add_Xsig_Xsig(&accumulator, &argSignif); - - if ( transformed ) - { - /* compute pi/4 - accumulator */ - shr_Xsig(&accumulator, -1-exponent); - negate_Xsig(&accumulator); - add_Xsig_Xsig(&accumulator, &pi_signif); - exponent = -1; - } - - if ( inverted ) - { - /* compute pi/2 - accumulator */ - shr_Xsig(&accumulator, -exponent); - negate_Xsig(&accumulator); - add_Xsig_Xsig(&accumulator, &pi_signif); - exponent = 0; - } - - if ( sign1 ) - { - /* compute pi - accumulator */ - shr_Xsig(&accumulator, 1 - exponent); - negate_Xsig(&accumulator); - add_Xsig_Xsig(&accumulator, &pi_signif); - exponent = 1; - } - - exponent += round_Xsig(&accumulator); - significand(result) = XSIG_LL(accumulator); - result->exp = exponent + EXP_BIAS; - result->tag = TW_Valid; - result->sign = sign2; - -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/poly_l2.c linux/drivers/FPU-emu/poly_l2.c --- v1.1.76/linux/drivers/FPU-emu/poly_l2.c Mon Aug 1 08:19:14 1994 +++ linux/drivers/FPU-emu/poly_l2.c Thu Jan 1 02:00:00 1970 @@ -1,255 +0,0 @@ -/*---------------------------------------------------------------------------+ - | poly_l2.c | - | | - | Compute the base 2 log of a FPU_REG, using a polynomial approximation. | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - - -#include "exception.h" -#include "reg_constant.h" -#include "fpu_emu.h" -#include "control_w.h" -#include "poly.h" - - - -static void log2_kernel(FPU_REG const *arg, - Xsig *accum_result, long int *expon); - - -/*--- poly_l2() -------------------------------------------------------------+ - | Base 2 logarithm by a polynomial approximation. | - +---------------------------------------------------------------------------*/ -void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result) -{ - long int exponent, expon, expon_expon; - Xsig accumulator, expon_accum, yaccum; - char sign; - FPU_REG x; - - - exponent = arg->exp - EXP_BIAS; - - /* From arg, make a number > sqrt(2)/2 and < sqrt(2) */ - if ( arg->sigh > (unsigned)0xb504f334 ) - { - /* Treat as sqrt(2)/2 < arg < 1 */ - significand(&x) = - significand(arg); - x.sign = SIGN_NEG; - x.tag = TW_Valid; - x.exp = EXP_BIAS-1; - exponent++; - normalize(&x); - } - else - { - /* Treat as 1 <= arg < sqrt(2) */ - x.sigh = arg->sigh - 0x80000000; - x.sigl = arg->sigl; - x.sign = SIGN_POS; - x.tag = TW_Valid; - x.exp = EXP_BIAS; - normalize(&x); - } - - if ( x.tag == TW_Zero ) - { - expon = 0; - accumulator.msw = accumulator.midw = accumulator.lsw = 0; - } - else - { - log2_kernel(&x, &accumulator, &expon); - } - - sign = exponent < 0; - if ( sign ) exponent = -exponent; - expon_accum.msw = exponent; expon_accum.midw = expon_accum.lsw = 0; - if ( exponent ) - { - expon_expon = 31 + norm_Xsig(&expon_accum); - shr_Xsig(&accumulator, expon_expon - expon); - - if ( sign ^ (x.sign == SIGN_NEG) ) - negate_Xsig(&accumulator); - add_Xsig_Xsig(&accumulator, &expon_accum); - } - else - { - expon_expon = expon; - sign = x.sign; - } - - yaccum.lsw = 0; XSIG_LL(yaccum) = significand(y); - mul_Xsig_Xsig(&accumulator, &yaccum); - - expon_expon += round_Xsig(&accumulator); - - if ( accumulator.msw == 0 ) - { - reg_move(&CONST_Z, y); - } - else - { - result->exp = expon_expon + y->exp + 1; - significand(result) = XSIG_LL(accumulator); - result->tag = TW_Valid; /* set the tags to Valid */ - result->sign = sign ^ y->sign; - } - - return; -} - - -/*--- poly_l2p1() -----------------------------------------------------------+ - | Base 2 logarithm by a polynomial approximation. | - | log2(x+1) | - +---------------------------------------------------------------------------*/ -int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result) -{ - char sign; - long int exponent; - Xsig accumulator, yaccum; - - - sign = arg->sign; - - if ( arg->exp < EXP_BIAS ) - { - log2_kernel(arg, &accumulator, &exponent); - - yaccum.lsw = 0; - XSIG_LL(yaccum) = significand(y); - mul_Xsig_Xsig(&accumulator, &yaccum); - - exponent += round_Xsig(&accumulator); - - result->exp = exponent + y->exp + 1; - significand(result) = XSIG_LL(accumulator); - result->tag = TW_Valid; /* set the tags to Valid */ - result->sign = sign ^ y->sign; - - return 0; - } - else - { - /* The magnitude of arg is far too large. */ - reg_move(y, result); - if ( sign != SIGN_POS ) - { - /* Trying to get the log of a negative number. */ - return 1; - } - else - { - return 0; - } - } - -} - - - - -#undef HIPOWER -#define HIPOWER 10 -static const unsigned long long logterms[HIPOWER] = -{ - 0x2a8eca5705fc2ef0LL, - 0xf6384ee1d01febceLL, - 0x093bb62877cdf642LL, - 0x006985d8a9ec439bLL, - 0x0005212c4f55a9c8LL, - 0x00004326a16927f0LL, - 0x0000038d1d80a0e7LL, - 0x0000003141cc80c6LL, - 0x00000002b1668c9fLL, - 0x000000002c7a46aaLL -}; - -static const unsigned long leadterm = 0xb8000000; - - -/*--- log2_kernel() ---------------------------------------------------------+ - | Base 2 logarithm by a polynomial approximation. | - | log2(x+1) | - +---------------------------------------------------------------------------*/ -static void log2_kernel(FPU_REG const *arg, Xsig *accum_result, - long int *expon) -{ - char sign; - long int exponent, adj; - unsigned long long Xsq; - Xsig accumulator, Numer, Denom, argSignif, arg_signif; - - sign = arg->sign; - - exponent = arg->exp - EXP_BIAS; - Numer.lsw = Denom.lsw = 0; - XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg); - if ( sign == SIGN_POS ) - { - shr_Xsig(&Denom, 2 - (1 + exponent)); - Denom.msw |= 0x80000000; - div_Xsig(&Numer, &Denom, &argSignif); - } - else - { - shr_Xsig(&Denom, 1 - (1 + exponent)); - negate_Xsig(&Denom); - if ( Denom.msw & 0x80000000 ) - { - div_Xsig(&Numer, &Denom, &argSignif); - exponent ++; - } - else - { - /* Denom must be 1.0 */ - argSignif.lsw = Numer.lsw; argSignif.midw = Numer.midw; - argSignif.msw = Numer.msw; - } - } - -#ifndef PECULIAR_486 - /* Should check here that |local_arg| is within the valid range */ - if ( exponent >= -2 ) - { - if ( (exponent > -2) || - (argSignif.msw > (unsigned)0xafb0ccc0) ) - { - /* The argument is too large */ - } - } -#endif PECULIAR_486 - - arg_signif.lsw = argSignif.lsw; XSIG_LL(arg_signif) = XSIG_LL(argSignif); - adj = norm_Xsig(&argSignif); - accumulator.lsw = argSignif.lsw; XSIG_LL(accumulator) = XSIG_LL(argSignif); - mul_Xsig_Xsig(&accumulator, &accumulator); - shr_Xsig(&accumulator, 2*(-1 - (1 + exponent + adj))); - Xsq = XSIG_LL(accumulator); - if ( accumulator.lsw & 0x80000000 ) - Xsq++; - - accumulator.msw = accumulator.midw = accumulator.lsw = 0; - /* Do the basic fixed point polynomial evaluation */ - polynomial_Xsig(&accumulator, &Xsq, logterms, HIPOWER-1); - - mul_Xsig_Xsig(&accumulator, &argSignif); - shr_Xsig(&accumulator, 6 - adj); - - mul32_Xsig(&arg_signif, leadterm); - add_two_Xsig(&accumulator, &arg_signif, &exponent); - - *expon = exponent + 1; - accum_result->lsw = accumulator.lsw; - accum_result->midw = accumulator.midw; - accum_result->msw = accumulator.msw; - -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/poly_sin.c linux/drivers/FPU-emu/poly_sin.c --- v1.1.76/linux/drivers/FPU-emu/poly_sin.c Mon Aug 1 08:19:15 1994 +++ linux/drivers/FPU-emu/poly_sin.c Thu Jan 1 02:00:00 1970 @@ -1,408 +0,0 @@ -/*---------------------------------------------------------------------------+ - | poly_sin.c | - | | - | Computation of an approximation of the sin function and the cosine | - | function by a polynomial. | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - - -#include "exception.h" -#include "reg_constant.h" -#include "fpu_emu.h" -#include "control_w.h" -#include "poly.h" - - -#define N_COEFF_P 4 -#define N_COEFF_N 4 - -static const unsigned long long pos_terms_l[N_COEFF_P] = -{ - 0xaaaaaaaaaaaaaaabLL, - 0x00d00d00d00cf906LL, - 0x000006b99159a8bbLL, - 0x000000000d7392e6LL -}; - -static const unsigned long long neg_terms_l[N_COEFF_N] = -{ - 0x2222222222222167LL, - 0x0002e3bc74aab624LL, - 0x0000000b09229062LL, - 0x00000000000c7973LL -}; - - - -#define N_COEFF_PH 4 -#define N_COEFF_NH 4 -static const unsigned long long pos_terms_h[N_COEFF_PH] = -{ - 0x0000000000000000LL, - 0x05b05b05b05b0406LL, - 0x000049f93edd91a9LL, - 0x00000000c9c9ed62LL -}; - -static const unsigned long long neg_terms_h[N_COEFF_NH] = -{ - 0xaaaaaaaaaaaaaa98LL, - 0x001a01a01a019064LL, - 0x0000008f76c68a77LL, - 0x0000000000d58f5eLL -}; - - -/*--- poly_sine() -----------------------------------------------------------+ - | | - +---------------------------------------------------------------------------*/ -void poly_sine(FPU_REG const *arg, FPU_REG *result) -{ - int exponent, echange; - Xsig accumulator, argSqrd, argTo4; - unsigned long fix_up, adj; - unsigned long long fixed_arg; - - -#ifdef PARANOID - if ( arg->tag == TW_Zero ) - { - /* Return 0.0 */ - reg_move(&CONST_Z, result); - return; - } -#endif PARANOID - - exponent = arg->exp - EXP_BIAS; - - accumulator.lsw = accumulator.midw = accumulator.msw = 0; - - /* Split into two ranges, for arguments below and above 1.0 */ - /* The boundary between upper and lower is approx 0.88309101259 */ - if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xe21240aa)) ) - { - /* The argument is <= 0.88309101259 */ - - argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; - mul64_Xsig(&argSqrd, &significand(arg)); - shr_Xsig(&argSqrd, 2*(-1-exponent)); - argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; - argTo4.lsw = argSqrd.lsw; - mul_Xsig_Xsig(&argTo4, &argTo4); - - polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_l, - N_COEFF_N-1); - mul_Xsig_Xsig(&accumulator, &argSqrd); - negate_Xsig(&accumulator); - - polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_l, - N_COEFF_P-1); - - shr_Xsig(&accumulator, 2); /* Divide by four */ - accumulator.msw |= 0x80000000; /* Add 1.0 */ - - mul64_Xsig(&accumulator, &significand(arg)); - mul64_Xsig(&accumulator, &significand(arg)); - mul64_Xsig(&accumulator, &significand(arg)); - - /* Divide by four, FPU_REG compatible, etc */ - exponent = 3*exponent + EXP_BIAS; - - /* The minimum exponent difference is 3 */ - shr_Xsig(&accumulator, arg->exp - exponent); - - negate_Xsig(&accumulator); - XSIG_LL(accumulator) += significand(arg); - - echange = round_Xsig(&accumulator); - - result->exp = arg->exp + echange; - } - else - { - /* The argument is > 0.88309101259 */ - /* We use sin(arg) = cos(pi/2-arg) */ - - fixed_arg = significand(arg); - - if ( exponent == 0 ) - { - /* The argument is >= 1.0 */ - - /* Put the binary point at the left. */ - fixed_arg <<= 1; - } - /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ - fixed_arg = 0x921fb54442d18469LL - fixed_arg; - - XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0; - mul64_Xsig(&argSqrd, &fixed_arg); - - XSIG_LL(argTo4) = XSIG_LL(argSqrd); argTo4.lsw = argSqrd.lsw; - mul_Xsig_Xsig(&argTo4, &argTo4); - - polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_h, - N_COEFF_NH-1); - mul_Xsig_Xsig(&accumulator, &argSqrd); - negate_Xsig(&accumulator); - - polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_h, - N_COEFF_PH-1); - negate_Xsig(&accumulator); - - mul64_Xsig(&accumulator, &fixed_arg); - mul64_Xsig(&accumulator, &fixed_arg); - - shr_Xsig(&accumulator, 3); - negate_Xsig(&accumulator); - - add_Xsig_Xsig(&accumulator, &argSqrd); - - shr_Xsig(&accumulator, 1); - - accumulator.lsw |= 1; /* A zero accumulator here would cause problems */ - negate_Xsig(&accumulator); - - /* The basic computation is complete. Now fix the answer to - compensate for the error due to the approximation used for - pi/2 - */ - - /* This has an exponent of -65 */ - fix_up = 0x898cc517; - /* The fix-up needs to be improved for larger args */ - if ( argSqrd.msw & 0xffc00000 ) - { - /* Get about 32 bit precision in these: */ - mul_32_32(0x898cc517, argSqrd.msw, &adj); - fix_up -= adj/6; - } - mul_32_32(fix_up, LL_MSW(fixed_arg), &fix_up); - - adj = accumulator.lsw; /* temp save */ - accumulator.lsw -= fix_up; - if ( accumulator.lsw > adj ) - XSIG_LL(accumulator) --; - - echange = round_Xsig(&accumulator); - - result->exp = EXP_BIAS - 1 + echange; - } - - significand(result) = XSIG_LL(accumulator); - result->tag = TW_Valid; - result->sign = arg->sign; - -#ifdef PARANOID - if ( (result->exp >= EXP_BIAS) - && (significand(result) > 0x8000000000000000LL) ) - { - EXCEPTION(EX_INTERNAL|0x150); - } -#endif PARANOID - -} - - - -/*--- poly_cos() ------------------------------------------------------------+ - | | - +---------------------------------------------------------------------------*/ -void poly_cos(FPU_REG const *arg, FPU_REG *result) -{ - long int exponent, exp2, echange; - Xsig accumulator, argSqrd, fix_up, argTo4; - unsigned long adj; - unsigned long long fixed_arg; - - -#ifdef PARANOID - if ( arg->tag == TW_Zero ) - { - /* Return 1.0 */ - reg_move(&CONST_1, result); - return; - } - - if ( (arg->exp > EXP_BIAS) - || ((arg->exp == EXP_BIAS) - && (significand(arg) > 0xc90fdaa22168c234LL)) ) - { - EXCEPTION(EX_Invalid); - reg_move(&CONST_QNaN, result); - return; - } -#endif PARANOID - - exponent = arg->exp - EXP_BIAS; - - accumulator.lsw = accumulator.midw = accumulator.msw = 0; - - if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xb00d6f54)) ) - { - /* arg is < 0.687705 */ - - argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; - mul64_Xsig(&argSqrd, &significand(arg)); - - if ( exponent < -1 ) - { - /* shift the argument right by the required places */ - shr_Xsig(&argSqrd, 2*(-1-exponent)); - } - - argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; - argTo4.lsw = argSqrd.lsw; - mul_Xsig_Xsig(&argTo4, &argTo4); - - polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_h, - N_COEFF_NH-1); - mul_Xsig_Xsig(&accumulator, &argSqrd); - negate_Xsig(&accumulator); - - polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_h, - N_COEFF_PH-1); - negate_Xsig(&accumulator); - - mul64_Xsig(&accumulator, &significand(arg)); - mul64_Xsig(&accumulator, &significand(arg)); - shr_Xsig(&accumulator, -2*(1+exponent)); - - shr_Xsig(&accumulator, 3); - negate_Xsig(&accumulator); - - add_Xsig_Xsig(&accumulator, &argSqrd); - - shr_Xsig(&accumulator, 1); - - /* It doesn't matter if accumulator is all zero here, the - following code will work ok */ - negate_Xsig(&accumulator); - - if ( accumulator.lsw & 0x80000000 ) - XSIG_LL(accumulator) ++; - if ( accumulator.msw == 0 ) - { - /* The result is 1.0 */ - reg_move(&CONST_1, result); - } - else - { - significand(result) = XSIG_LL(accumulator); - - /* will be a valid positive nr with expon = -1 */ - *(short *)&(result->sign) = 0; - result->exp = EXP_BIAS - 1; - } - } - else - { - fixed_arg = significand(arg); - - if ( exponent == 0 ) - { - /* The argument is >= 1.0 */ - - /* Put the binary point at the left. */ - fixed_arg <<= 1; - } - /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ - fixed_arg = 0x921fb54442d18469LL - fixed_arg; - - exponent = -1; - exp2 = -1; - - /* A shift is needed here only for a narrow range of arguments, - i.e. for fixed_arg approx 2^-32, but we pick up more... */ - if ( !(LL_MSW(fixed_arg) & 0xffff0000) ) - { - fixed_arg <<= 16; - exponent -= 16; - exp2 -= 16; - } - - XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0; - mul64_Xsig(&argSqrd, &fixed_arg); - - if ( exponent < -1 ) - { - /* shift the argument right by the required places */ - shr_Xsig(&argSqrd, 2*(-1-exponent)); - } - - argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; - argTo4.lsw = argSqrd.lsw; - mul_Xsig_Xsig(&argTo4, &argTo4); - - polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_l, - N_COEFF_N-1); - mul_Xsig_Xsig(&accumulator, &argSqrd); - negate_Xsig(&accumulator); - - polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_l, - N_COEFF_P-1); - - shr_Xsig(&accumulator, 2); /* Divide by four */ - accumulator.msw |= 0x80000000; /* Add 1.0 */ - - mul64_Xsig(&accumulator, &fixed_arg); - mul64_Xsig(&accumulator, &fixed_arg); - mul64_Xsig(&accumulator, &fixed_arg); - - /* Divide by four, FPU_REG compatible, etc */ - exponent = 3*exponent; - - /* The minimum exponent difference is 3 */ - shr_Xsig(&accumulator, exp2 - exponent); - - negate_Xsig(&accumulator); - XSIG_LL(accumulator) += fixed_arg; - - /* The basic computation is complete. Now fix the answer to - compensate for the error due to the approximation used for - pi/2 - */ - - /* This has an exponent of -65 */ - XSIG_LL(fix_up) = 0x898cc51701b839a2ll; - fix_up.lsw = 0; - - /* The fix-up needs to be improved for larger args */ - if ( argSqrd.msw & 0xffc00000 ) - { - /* Get about 32 bit precision in these: */ - mul_32_32(0x898cc517, argSqrd.msw, &adj); - fix_up.msw -= adj/2; - mul_32_32(0x898cc517, argTo4.msw, &adj); - fix_up.msw += adj/24; - } - - exp2 += norm_Xsig(&accumulator); - shr_Xsig(&accumulator, 1); /* Prevent overflow */ - exp2++; - shr_Xsig(&fix_up, 65 + exp2); - - add_Xsig_Xsig(&accumulator, &fix_up); - - echange = round_Xsig(&accumulator); - - result->exp = exp2 + EXP_BIAS + echange; - *(short *)&(result->sign) = 0; /* Is a valid positive nr */ - significand(result) = XSIG_LL(accumulator); - } - -#ifdef PARANOID - if ( (result->exp >= EXP_BIAS) - && (significand(result) > 0x8000000000000000LL) ) - { - EXCEPTION(EX_INTERNAL|0x151); - } -#endif PARANOID - -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/poly_tan.c linux/drivers/FPU-emu/poly_tan.c --- v1.1.76/linux/drivers/FPU-emu/poly_tan.c Fri Aug 19 08:54:01 1994 +++ linux/drivers/FPU-emu/poly_tan.c Thu Jan 1 02:00:00 1970 @@ -1,213 +0,0 @@ -/*---------------------------------------------------------------------------+ - | poly_tan.c | - | | - | Compute the tan of a FPU_REG, using a polynomial approximation. | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -#include "exception.h" -#include "reg_constant.h" -#include "fpu_emu.h" -#include "control_w.h" -#include "poly.h" - - -#define HiPOWERop 3 /* odd poly, positive terms */ -static const unsigned long long oddplterm[HiPOWERop] = -{ - 0x0000000000000000LL, - 0x0051a1cf08fca228LL, - 0x0000000071284ff7LL -}; - -#define HiPOWERon 2 /* odd poly, negative terms */ -static const unsigned long long oddnegterm[HiPOWERon] = -{ - 0x1291a9a184244e80LL, - 0x0000583245819c21LL -}; - -#define HiPOWERep 2 /* even poly, positive terms */ -static const unsigned long long evenplterm[HiPOWERep] = -{ - 0x0e848884b539e888LL, - 0x00003c7f18b887daLL -}; - -#define HiPOWERen 2 /* even poly, negative terms */ -static const unsigned long long evennegterm[HiPOWERen] = -{ - 0xf1f0200fd51569ccLL, - 0x003afb46105c4432LL -}; - -static const unsigned long long twothirds = 0xaaaaaaaaaaaaaaabLL; - - -/*--- poly_tan() ------------------------------------------------------------+ - | | - +---------------------------------------------------------------------------*/ -void poly_tan(FPU_REG const *arg, FPU_REG *result) -{ - long int exponent; - int invert; - Xsig argSq, argSqSq, accumulatoro, accumulatore, accum, - argSignif, fix_up; - unsigned long adj; - - exponent = arg->exp - EXP_BIAS; - -#ifdef PARANOID - if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */ - { arith_invalid(result); return; } /* Need a positive number */ -#endif PARANOID - - /* Split the problem into two domains, smaller and larger than pi/4 */ - if ( (exponent == 0) || ((exponent == -1) && (arg->sigh > 0xc90fdaa2)) ) - { - /* The argument is greater than (approx) pi/4 */ - invert = 1; - accum.lsw = 0; - XSIG_LL(accum) = significand(arg); - - if ( exponent == 0 ) - { - /* The argument is >= 1.0 */ - /* Put the binary point at the left. */ - XSIG_LL(accum) <<= 1; - } - /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ - XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum); - - argSignif.lsw = accum.lsw; - XSIG_LL(argSignif) = XSIG_LL(accum); - exponent = -1 + norm_Xsig(&argSignif); - } - else - { - invert = 0; - argSignif.lsw = 0; - XSIG_LL(accum) = XSIG_LL(argSignif) = significand(arg); - - if ( exponent < -1 ) - { - /* shift the argument right by the required places */ - if ( shrx(&XSIG_LL(accum), -1-exponent) >= 0x80000000U ) - XSIG_LL(accum) ++; /* round up */ - } - } - - XSIG_LL(argSq) = XSIG_LL(accum); argSq.lsw = accum.lsw; - mul_Xsig_Xsig(&argSq, &argSq); - XSIG_LL(argSqSq) = XSIG_LL(argSq); argSqSq.lsw = argSq.lsw; - mul_Xsig_Xsig(&argSqSq, &argSqSq); - - /* Compute the negative terms for the numerator polynomial */ - accumulatoro.msw = accumulatoro.midw = accumulatoro.lsw = 0; - polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddnegterm, HiPOWERon-1); - mul_Xsig_Xsig(&accumulatoro, &argSq); - negate_Xsig(&accumulatoro); - /* Add the positive terms */ - polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddplterm, HiPOWERop-1); - - - /* Compute the positive terms for the denominator polynomial */ - accumulatore.msw = accumulatore.midw = accumulatore.lsw = 0; - polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evenplterm, HiPOWERep-1); - mul_Xsig_Xsig(&accumulatore, &argSq); - negate_Xsig(&accumulatore); - /* Add the negative terms */ - polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evennegterm, HiPOWERen-1); - /* Multiply by arg^2 */ - mul64_Xsig(&accumulatore, &XSIG_LL(argSignif)); - mul64_Xsig(&accumulatore, &XSIG_LL(argSignif)); - /* de-normalize and divide by 2 */ - shr_Xsig(&accumulatore, -2*(1+exponent) + 1); - negate_Xsig(&accumulatore); /* This does 1 - accumulator */ - - /* Now find the ratio. */ - if ( accumulatore.msw == 0 ) - { - /* accumulatoro must contain 1.0 here, (actually, 0) but it - really doesn't matter what value we use because it will - have negligible effect in later calculations - */ - XSIG_LL(accum) = 0x8000000000000000LL; - accum.lsw = 0; - } - else - { - div_Xsig(&accumulatoro, &accumulatore, &accum); - } - - /* Multiply by 1/3 * arg^3 */ - mul64_Xsig(&accum, &XSIG_LL(argSignif)); - mul64_Xsig(&accum, &XSIG_LL(argSignif)); - mul64_Xsig(&accum, &XSIG_LL(argSignif)); - mul64_Xsig(&accum, &twothirds); - shr_Xsig(&accum, -2*(exponent+1)); - - /* tan(arg) = arg + accum */ - add_two_Xsig(&accum, &argSignif, &exponent); - - if ( invert ) - { - /* We now have the value of tan(pi_2 - arg) where pi_2 is an - approximation for pi/2 - */ - /* The next step is to fix the answer to compensate for the - error due to the approximation used for pi/2 - */ - - /* This is (approx) delta, the error in our approx for pi/2 - (see above). It has an exponent of -65 - */ - XSIG_LL(fix_up) = 0x898cc51701b839a2LL; - fix_up.lsw = 0; - - if ( exponent == 0 ) - adj = 0xffffffff; /* We want approx 1.0 here, but - this is close enough. */ - else if ( exponent > -30 ) - { - adj = accum.msw >> -(exponent+1); /* tan */ - mul_32_32(adj, adj, &adj); /* tan^2 */ - } - else - adj = 0; - mul_32_32(0x898cc517, adj, &adj); /* delta * tan^2 */ - - fix_up.msw += adj; - if ( !(fix_up.msw & 0x80000000) ) /* did fix_up overflow ? */ - { - /* Yes, we need to add an msb */ - shr_Xsig(&fix_up, 1); - fix_up.msw |= 0x80000000; - shr_Xsig(&fix_up, 64 + exponent); - } - else - shr_Xsig(&fix_up, 65 + exponent); - - add_two_Xsig(&accum, &fix_up, &exponent); - - /* accum now contains tan(pi/2 - arg). - Use tan(arg) = 1.0 / tan(pi/2 - arg) - */ - accumulatoro.lsw = accumulatoro.midw = 0; - accumulatoro.msw = 0x80000000; - div_Xsig(&accumulatoro, &accum, &accum); - exponent = - exponent - 1; - } - - /* Transfer the result */ - round_Xsig(&accum); - *(short *)&(result->sign) = 0; - significand(result) = XSIG_LL(accum); - result->exp = EXP_BIAS + exponent; - -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/polynom_Xsig.S linux/drivers/FPU-emu/polynom_Xsig.S --- v1.1.76/linux/drivers/FPU-emu/polynom_Xsig.S Mon Aug 1 08:19:15 1994 +++ linux/drivers/FPU-emu/polynom_Xsig.S Thu Jan 1 02:00:00 1970 @@ -1,137 +0,0 @@ -/*---------------------------------------------------------------------------+ - | polynomial_Xsig.S | - | | - | Fixed point arithmetic polynomial evaluation. | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | Call from C as: | - | void polynomial_Xsig(Xsig *accum, unsigned long long x, | - | unsigned long long terms[], int n) | - | | - | Computes: | - | terms[0] + (terms[1] + (terms[2] + ... + (terms[n-1]*x)*x)*x)*x) ... )*x | - | and adds the result to the 12 byte Xsig. | - | The terms[] are each 8 bytes, but all computation is performed to 12 byte | - | precision. | - | | - | This function must be used carefully: most overflow of intermediate | - | results is controlled, but overflow of the result is not. | - | | - +---------------------------------------------------------------------------*/ - .file "polynomial_Xsig.S" - -#include "fpu_asm.h" - - -#define TERM_SIZE $8 -#define SUM_MS -20(%ebp) /* sum ms long */ -#define SUM_MIDDLE -24(%ebp) /* sum middle long */ -#define SUM_LS -28(%ebp) /* sum ls long */ -#define ACCUM_MS -4(%ebp) /* accum ms long */ -#define ACCUM_MIDDLE -8(%ebp) /* accum middle long */ -#define ACCUM_LS -12(%ebp) /* accum ls long */ -#define OVERFLOWED -16(%ebp) /* addition overflow flag */ - -.text - .align 2,144 -.globl _polynomial_Xsig -_polynomial_Xsig: - pushl %ebp - movl %esp,%ebp - subl $32,%esp - pushl %esi - pushl %edi - pushl %ebx - - movl PARAM2,%esi /* x */ - movl PARAM3,%edi /* terms */ - - movl TERM_SIZE,%eax - mull PARAM4 /* n */ - addl %eax,%edi - - movl 4(%edi),%edx /* terms[n] */ - movl %edx,SUM_MS - movl (%edi),%edx /* terms[n] */ - movl %edx,SUM_MIDDLE - xor %eax,%eax - movl %eax,SUM_LS - movb %al,OVERFLOWED - - subl TERM_SIZE,%edi - decl PARAM4 - js L_accum_done - -L_accum_loop: - xor %eax,%eax - movl %eax,ACCUM_MS - movl %eax,ACCUM_MIDDLE - - movl SUM_MIDDLE,%eax - mull (%esi) /* x ls long */ - movl %edx,ACCUM_LS - - movl SUM_MIDDLE,%eax - mull 4(%esi) /* x ms long */ - addl %eax,ACCUM_LS - adcl %edx,ACCUM_MIDDLE - adcl $0,ACCUM_MS - - movl SUM_MS,%eax - mull (%esi) /* x ls long */ - addl %eax,ACCUM_LS - adcl %edx,ACCUM_MIDDLE - adcl $0,ACCUM_MS - - movl SUM_MS,%eax - mull 4(%esi) /* x ms long */ - addl %eax,ACCUM_MIDDLE - adcl %edx,ACCUM_MS - - testb $0xff,OVERFLOWED - jz L_no_overflow - - movl (%esi),%eax - addl %eax,ACCUM_MIDDLE - movl 4(%esi),%eax - adcl %eax,ACCUM_MS /* This could overflow too */ - -L_no_overflow: - -/* - * Now put the sum of next term and the accumulator - * into the sum register - */ - movl ACCUM_LS,%eax - addl (%edi),%eax /* term ls long */ - movl %eax,SUM_LS - movl ACCUM_MIDDLE,%eax - adcl (%edi),%eax /* term ls long */ - movl %eax,SUM_MIDDLE - movl ACCUM_MS,%eax - adcl 4(%edi),%eax /* term ms long */ - movl %eax,SUM_MS - sbbb %al,%al - movb %al,OVERFLOWED /* Used in the next iteration */ - - subl TERM_SIZE,%edi - decl PARAM4 - jns L_accum_loop - -L_accum_done: - movl PARAM1,%edi /* accum */ - movl SUM_LS,%eax - addl %eax,(%edi) - movl SUM_MIDDLE,%eax - adcl %eax,4(%edi) - movl SUM_MS,%eax - adcl %eax,8(%edi) - - popl %ebx - popl %edi - popl %esi - leave - ret diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_add_sub.c linux/drivers/FPU-emu/reg_add_sub.c --- v1.1.76/linux/drivers/FPU-emu/reg_add_sub.c Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/reg_add_sub.c Thu Jan 1 02:00:00 1970 @@ -1,318 +0,0 @@ -/*---------------------------------------------------------------------------+ - | 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 billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | 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" - - -int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w) -{ - char saved_sign = dest->sign; - int diff; - - if ( !(a->tag | b->tag) ) - { - /* Both registers are valid */ - if (!(a->sign ^ b->sign)) - { - /* signs are the same */ - dest->sign = a->sign; - if ( reg_u_add(a, b, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - return 0; - } - - /* 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) - { - dest->sign = a->sign; - if ( reg_u_sub(a, b, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - } - else if ( diff == 0 ) - { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(&CONST_Z, dest); - /* sign depends upon rounding mode */ - dest->sign = ((control_w & CW_RC) != RC_DOWN) - ? SIGN_POS : SIGN_NEG; - } - else - { - dest->sign = b->sign; - if ( reg_u_sub(b, a, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - } - return 0; - } - else - { - if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) - { return real_2op_NaN(a, b, dest); } - else if (a->tag == TW_Zero) - { - if (b->tag == TW_Zero) - { - char different_signs = a->sign ^ b->sign; - /* Both are zero, result will be zero. */ - reg_move(a, dest); - if (different_signs) - { - /* Signs are different. */ - /* Sign of answer depends upon rounding mode. */ - dest->sign = ((control_w & CW_RC) != RC_DOWN) - ? SIGN_POS : SIGN_NEG; - } - } - else - { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(b, dest); - } - return 0; - } - else if (b->tag == TW_Zero) - { -#ifdef DENORM_OPERAND - if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(a, dest); return 0; - } - else if (a->tag == TW_Infinity) - { - if (b->tag != TW_Infinity) - { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(a, dest); return 0; - } - if (a->sign == b->sign) - { - /* They are both + or - infinity */ - reg_move(a, dest); return 0; - } - return arith_invalid(dest); /* Infinity-Infinity is undefined. */ - } - else if (b->tag == TW_Infinity) - { -#ifdef DENORM_OPERAND - if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(b, dest); return 0; - } - } -#ifdef PARANOID - EXCEPTION(EX_INTERNAL|0x101); -#endif - return 1; -} - - -/* Subtract b from a. (a-b) -> dest */ -int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w) -{ - char saved_sign = dest->sign; - 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) - { - /* |a| > |b| */ - dest->sign = a->sign; - if ( reg_u_sub(a, b, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - return 0; - } - else if ( diff == 0 ) - { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(&CONST_Z, dest); - /* sign depends upon rounding mode */ - dest->sign = ((control_w & CW_RC) != RC_DOWN) - ? SIGN_POS : SIGN_NEG; - } - else - { - dest->sign = a->sign ^ SIGN_POS^SIGN_NEG; - if ( reg_u_sub(b, a, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - } - break; - case 1: /* P - N */ - dest->sign = SIGN_POS; - if ( reg_u_add(a, b, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - break; - case 2: /* N - P */ - dest->sign = SIGN_NEG; - if ( reg_u_add(a, b, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - break; - } - return 0; - } - else - { - if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) - { return real_2op_NaN(b, a, dest); } - else if (b->tag == TW_Zero) - { - if (a->tag == TW_Zero) - { - char same_signs = !(a->sign ^ b->sign); - /* Both are zero, result will be zero. */ - reg_move(a, dest); /* Answer for different signs. */ - if (same_signs) - { - /* Sign depends upon rounding mode */ - dest->sign = ((control_w & CW_RC) != RC_DOWN) - ? SIGN_POS : SIGN_NEG; - } - } - else - { -#ifdef DENORM_OPERAND - if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(a, dest); - } - return 0; - } - else if (a->tag == TW_Zero) - { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(b, dest); - dest->sign ^= SIGN_POS^SIGN_NEG; - return 0; - } - 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 1; -#endif DENORM_OPERAND - reg_move(a, dest); return 0; - } - /* Both args are Infinity */ - if (a->sign == b->sign) - { - /* Infinity-Infinity is undefined. */ - return arith_invalid(dest); - } - reg_move(a, dest); - return 0; - } - else if (b->tag == TW_Infinity) - { -#ifdef DENORM_OPERAND - if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(b, dest); - dest->sign ^= SIGN_POS^SIGN_NEG; - return 0; - } - } -#ifdef PARANOID - EXCEPTION(EX_INTERNAL|0x110); -#endif - return 1; -} - diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_compare.c linux/drivers/FPU-emu/reg_compare.c --- v1.1.76/linux/drivers/FPU-emu/reg_compare.c Thu Jun 2 10:28:27 1994 +++ linux/drivers/FPU-emu/reg_compare.c Thu Jan 1 02:00:00 1970 @@ -1,378 +0,0 @@ -/*---------------------------------------------------------------------------+ - | reg_compare.c | - | | - | Compare two floating point registers | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | compare() is the core FPU_REG comparison function | - +---------------------------------------------------------------------------*/ - -#include "fpu_system.h" -#include "exception.h" -#include "fpu_emu.h" -#include "control_w.h" -#include "status_w.h" - - -int compare(FPU_REG const *b) -{ - int diff; - char st0_tag; - FPU_REG *st0_ptr; - - st0_ptr = &st(0); - st0_tag = st0_ptr->tag; - - if ( st0_tag | b->tag ) - { - if ( st0_tag == TW_Zero ) - { - if ( b->tag == TW_Zero ) return COMP_A_eq_B; - if ( b->tag == TW_Valid ) - { - return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) -#ifdef DENORM_OPERAND - | ((b->exp <= EXP_UNDER) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; - } - } - else if ( b->tag == TW_Zero ) - { - if ( st0_tag == TW_Valid ) - { - return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B - : COMP_A_lt_B) -#ifdef DENORM_OPERAND - | ((st0_ptr->exp <= EXP_UNDER ) - ? COMP_Denormal : 0 ) -#endif DENORM_OPERAND - ; - } - } - - if ( st0_tag == TW_Infinity ) - { - if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) ) - { - return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B - : COMP_A_lt_B) -#ifdef DENORM_OPERAND - | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ? - COMP_Denormal : 0 ) -#endif DENORM_OPERAND -; - } - else if ( b->tag == TW_Infinity ) - { - /* The 80486 book says that infinities can be equal! */ - return (st0_ptr->sign == b->sign) ? COMP_A_eq_B : - ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); - } - /* Fall through to the NaN code */ - } - else if ( b->tag == TW_Infinity ) - { - if ( (st0_tag == TW_Valid) || (st0_tag == TW_Zero) ) - { - return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) -#ifdef DENORM_OPERAND - | (((st0_tag == TW_Valid) - && (st0_ptr->exp <= EXP_UNDER)) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; - } - /* Fall through to the NaN code */ - } - - /* The only possibility now should be that one of the arguments - is a NaN */ - if ( (st0_tag == TW_NaN) || (b->tag == TW_NaN) ) - { - if ( ((st0_tag == TW_NaN) && !(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 (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); - if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); -#endif PARANOID - - - if (st0_ptr->sign != b->sign) - { - return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) -#ifdef DENORM_OPERAND - | - ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; - } - - diff = st0_ptr->exp - b->exp; - if ( diff == 0 ) - { - diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are - identical */ - if ( diff == 0 ) - { - diff = st0_ptr->sigl > b->sigl; - if ( diff == 0 ) - diff = -(st0_ptr->sigl < b->sigl); - } - } - - if ( diff > 0 ) - { - return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) -#ifdef DENORM_OPERAND - | - ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; - } - if ( diff < 0 ) - { - return ((st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) -#ifdef DENORM_OPERAND - | - ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; - } - - return COMP_A_eq_B -#ifdef DENORM_OPERAND - | - ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; - -} - - -/* This function requires that st(0) is not empty */ -int compare_st_data(FPU_REG const *loaded_data) -{ - int f, c; - - c = compare(loaded_data); - - if (c & COMP_NaN) - { - EXCEPTION(EX_Invalid); - f = SW_C3 | SW_C2 | SW_C0; - } - else - switch (c & 7) - { - 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); - if (c & COMP_Denormal) - { - return denormal_operand(); - } - return 0; -} - - -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) - { - setcc(SW_C3 | SW_C2 | SW_C0); - EXCEPTION(EX_Invalid); - return !(control_word & CW_Invalid); - } - else - switch (c & 7) - { - 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); - if (c & COMP_Denormal) - { - return denormal_operand(); - } - return 0; -} - - -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) - { - 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 0; - } - else - switch (c & 7) - { - 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); - if (c & COMP_Denormal) - { - return denormal_operand(); - } - return 0; -} - -/*---------------------------------------------------------------------------*/ - -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) - { - FPU_illegal(); - return; - } - if ( !compare_st_st(1) ) - poppop(); -} - - -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) ) - poppop(); - } - else - FPU_illegal(); -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_constant.c linux/drivers/FPU-emu/reg_constant.c --- v1.1.76/linux/drivers/FPU-emu/reg_constant.c Thu Jun 2 10:28:27 1994 +++ linux/drivers/FPU-emu/reg_constant.c Thu Jan 1 02:00:00 1970 @@ -1,116 +0,0 @@ -/*---------------------------------------------------------------------------+ - | reg_constant.c | - | | - | All of the constant FPU_REGs | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -#include "fpu_system.h" -#include "fpu_emu.h" -#include "status_w.h" -#include "reg_constant.h" - - -FPU_REG const CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS, - 0x00000000, 0x80000000 }; -FPU_REG const CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1, - 0x00000000, 0x80000000 }; -FPU_REG const CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1, - 0x00000000, 0x80000000 }; -FPU_REG const CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1, - 0xcd1b8afe, 0xd49a784b }; -FPU_REG const CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS, - 0x5c17f0bc, 0xb8aa3b29 }; -FPU_REG const CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1, - 0x2168c235, 0xc90fdaa2 }; -FPU_REG const CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS, - 0x2168c235, 0xc90fdaa2 }; -FPU_REG const CONST_PI4 = { SIGN_POS, TW_Valid, EXP_BIAS-1, - 0x2168c235, 0xc90fdaa2 }; -FPU_REG const CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2, - 0xfbcff799, 0x9a209a84 }; -FPU_REG const CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1, - 0xd1cf79ac, 0xb17217f7 }; - -/* Extra bits to take pi/2 to more than 128 bits precision. */ -FPU_REG const CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66, - 0xfc8f8cbb, 0xece675d1 }; - -/* Only the sign (and tag) is used in internal zeroes */ -FPU_REG const CONST_Z = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 }; - -/* Only the sign and significand (and tag) are used in internal NaNs */ -/* The 80486 never generates one of these -FPU_REG const CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 }; - */ -/* This is the real indefinite QNaN */ -FPU_REG const CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 }; - -/* Only the sign (and tag) is used in internal infinities */ -FPU_REG const CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 }; - - - -static void fld_const(FPU_REG const *c) -{ - FPU_REG *st_new_ptr; - - if ( STACK_OVERFLOW ) - { - stack_overflow(); - return; - } - push(); - reg_move(c, st_new_ptr); - clear_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, FPU_illegal -}; - -void fconst(void) -{ - (constants_table[FPU_rm])(); -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_constant.h linux/drivers/FPU-emu/reg_constant.h --- v1.1.76/linux/drivers/FPU-emu/reg_constant.h Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/reg_constant.h Thu Jan 1 02:00:00 1970 @@ -1,31 +0,0 @@ -/*---------------------------------------------------------------------------+ - | reg_constant.h | - | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - +---------------------------------------------------------------------------*/ - -#ifndef _REG_CONSTANT_H_ -#define _REG_CONSTANT_H_ - -#include "fpu_emu.h" - -extern FPU_REG const CONST_1; -extern FPU_REG const CONST_2; -extern FPU_REG const CONST_HALF; -extern FPU_REG const CONST_L2T; -extern FPU_REG const CONST_L2E; -extern FPU_REG const CONST_PI; -extern FPU_REG const CONST_PI2; -extern FPU_REG const CONST_PI2extra; -extern FPU_REG const CONST_PI4; -extern FPU_REG const CONST_LG2; -extern FPU_REG const CONST_LN2; -extern FPU_REG const CONST_Z; -extern FPU_REG const CONST_PINF; -extern FPU_REG const CONST_INF; -extern FPU_REG const CONST_MINF; -extern FPU_REG const CONST_QNaN; - -#endif _REG_CONSTANT_H_ diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_div.S linux/drivers/FPU-emu/reg_div.S --- v1.1.76/linux/drivers/FPU-emu/reg_div.S Thu Jun 2 10:28:27 1994 +++ linux/drivers/FPU-emu/reg_div.S Thu Jan 1 02:00:00 1970 @@ -1,251 +0,0 @@ - .file "reg_div.S" -/*---------------------------------------------------------------------------+ - | reg_div.S | - | | - | Divide one FPU_REG by another and put the result in a destination FPU_REG.| - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | Call from C as: | - | void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, | - | unsigned int control_word) | - | | - +---------------------------------------------------------------------------*/ - -#include "exception.h" -#include "fpu_asm.h" - - -.text - .align 2 - -.globl _reg_div -_reg_div: - pushl %ebp - movl %esp,%ebp -#ifndef NON_REENTRANT_FPU - subl $28,%esp /* Needed by divide_kernel */ -#endif NON_REENTRANT_FPU - - pushl %esi - pushl %edi - pushl %ebx - - movl PARAM1,%esi - movl PARAM2,%ebx - movl PARAM3,%edi - - movb TAG(%esi),%al - orb TAG(%ebx),%al - - jne L_div_special /* Not (both numbers TW_Valid) */ - -#ifdef DENORM_OPERAND -/* Check for denormals */ - cmpl EXP_UNDER,EXP(%esi) - jg xL_arg1_not_denormal - - call _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 %esi - pushl %ebx /* Ordering is important here */ - 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) - -LDiv_set_result_sign: - movb SIGN(%esi),%cl - cmpb %cl,SIGN(%ebx) - jne LDiv_negative_result - - movb SIGN_POS,SIGN(%edi) - xorl %eax,%eax /* Valid result */ - jmp LDiv_exit - -LDiv_negative_result: - movb SIGN_NEG,SIGN(%edi) - xorl %eax,%eax /* Valid result */ - -LDiv_exit: -#ifndef NON_REENTRANT_FPU - leal -40(%ebp),%esp -#else - leal -12(%ebp),%esp -#endif NON_REENTRANT_FPU - - popl %ebx - popl %edi - popl %esi - leave - ret - - -L_return_zero: - xorl %eax,%eax - movl %eax,SIGH(%edi) - movl %eax,SIGL(%edi) - movl EXP_UNDER,EXP(%edi) - movb TW_Zero,TAG(%edi) - jmp LDiv_set_result_sign - -#ifdef PARANOID -L_unknown_tags: - pushl EX_INTERNAL | 0x208 - call EXCEPTION - - /* Generate a NaN for unknown tags */ - movl _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 /* %eax is nz */ -#endif PARANOID diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_ld_str.c linux/drivers/FPU-emu/reg_ld_str.c --- v1.1.76/linux/drivers/FPU-emu/reg_ld_str.c Fri Aug 19 08:54:01 1994 +++ linux/drivers/FPU-emu/reg_ld_str.c Thu Jan 1 02:00:00 1970 @@ -1,1438 +0,0 @@ -/*---------------------------------------------------------------------------+ - | reg_ld_str.c | - | | - | All of the functions which transfer data between user memory and FPU_REGs.| - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | 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 - -#include "fpu_system.h" -#include "exception.h" -#include "reg_constant.h" -#include "fpu_emu.h" -#include "control_w.h" -#include "status_w.h" - - -#define EXTENDED_Ebias 0x3fff -#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */ - -#define DOUBLE_Emax 1023 /* largest valid exponent */ -#define DOUBLE_Ebias 1023 -#define DOUBLE_Emin (-1022) /* smallest valid exponent */ - -#define SINGLE_Emax 127 /* largest valid exponent */ -#define SINGLE_Ebias 127 -#define SINGLE_Emin (-126) /* smallest valid exponent */ - -static void write_to_extended(FPU_REG *rp, char *d); - - -/* Get a long double from user memory */ -int reg_load_extended(long double *s, FPU_REG *loaded_data) -{ - unsigned long sigl, sigh, exp; - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, s, 10); - sigl = get_fs_long((unsigned long *) s); - sigh = get_fs_long(1 + (unsigned long *) s); - exp = get_fs_word(4 + (unsigned short *) s); - RE_ENTRANT_CHECK_ON; - - loaded_data->tag = TW_Valid; /* Default */ - loaded_data->sigl = sigl; - loaded_data->sigh = sigh; - if (exp & 0x8000) - loaded_data->sign = SIGN_NEG; - else - loaded_data->sign = SIGN_POS; - exp &= 0x7fff; - loaded_data->exp = exp - EXTENDED_Ebias + EXP_BIAS; - - if ( exp == 0 ) - { - if ( !(sigh | sigl) ) - { - loaded_data->tag = TW_Zero; - return 0; - } - /* The number is a de-normal or pseudodenormal. */ - if (sigh & 0x80000000) - { - /* Is a pseudodenormal. */ - /* Convert it for internal use. */ - /* This is non-80486 behaviour because the number - loses its 'denormal' identity. */ - loaded_data->exp++; - return 1; - } - else - { - /* Is a denormal. */ - /* Convert it for internal use. */ - loaded_data->exp++; - normalize_nuo(loaded_data); - return 0; - } - } - else if ( exp == 0x7fff ) - { - if ( !((sigh ^ 0x80000000) | sigl) ) - { - /* Matches the bit pattern for Infinity. */ - loaded_data->exp = EXP_Infinity; - loaded_data->tag = TW_Infinity; - return 0; - } - - loaded_data->exp = EXP_NaN; - loaded_data->tag = TW_NaN; - if ( !(sigh & 0x80000000) ) - { - /* NaNs have the ms bit set to 1. */ - /* This is therefore an Unsupported NaN data type. */ - /* This is non 80486 behaviour */ - /* This should generate an Invalid Operand exception - later, so we convert it to a SNaN */ - loaded_data->sigh = 0x80000000; - loaded_data->sigl = 0x00000001; - loaded_data->sign = SIGN_NEG; - return 1; - } - return 0; - } - - if ( !(sigh & 0x80000000) ) - { - /* Unsupported data type. */ - /* Valid numbers have the ms bit set to 1. */ - /* Unnormal. */ - /* Convert it for internal use. */ - /* This is non-80486 behaviour */ - /* This should generate an Invalid Operand exception - later, so we convert it to a SNaN */ - loaded_data->sigh = 0x80000000; - loaded_data->sigl = 0x00000001; - loaded_data->sign = SIGN_NEG; - loaded_data->exp = EXP_NaN; - loaded_data->tag = TW_NaN; - return 1; - } - return 0; -} - - -/* Get a double from user memory */ -int reg_load_double(double *dfloat, FPU_REG *loaded_data) -{ - int exp; - unsigned m64, l64; - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, dfloat, 8); - m64 = get_fs_long(1 + (unsigned long *) dfloat); - l64 = get_fs_long((unsigned long *) dfloat); - RE_ENTRANT_CHECK_ON; - - if (m64 & 0x80000000) - loaded_data->sign = SIGN_NEG; - else - 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 */ - loaded_data->sigh = 0x80000000; - loaded_data->sigl = 0x00000000; - loaded_data->exp = EXP_Infinity; - loaded_data->tag = TW_Infinity; - return 0; - } - else - { - /* Must be a signaling or quiet NaN */ - loaded_data->exp = EXP_NaN; - loaded_data->tag = TW_NaN; - loaded_data->sigh = (m64 << 11) | 0x80000000; - loaded_data->sigh |= l64 >> 21; - loaded_data->sigl = l64 << 11; - return 0; /* The calling function must look for NaNs */ - } - } - else if ( exp < DOUBLE_Emin ) - { - /* Zero or de-normal */ - if ((m64 == 0) && (l64 == 0)) - { - /* Zero */ - int c = loaded_data->sign; - reg_move(&CONST_Z, loaded_data); - loaded_data->sign = c; - return 0; - } - else - { - /* De-normal */ - loaded_data->exp = DOUBLE_Emin + EXP_BIAS; - loaded_data->tag = TW_Valid; - loaded_data->sigh = m64 << 11; - loaded_data->sigh |= l64 >> 21; - loaded_data->sigl = l64 << 11; - normalize_nuo(loaded_data); - return denormal_operand(); - } - } - else - { - loaded_data->exp = exp + EXP_BIAS; - loaded_data->tag = TW_Valid; - loaded_data->sigh = (m64 << 11) | 0x80000000; - loaded_data->sigh |= l64 >> 21; - loaded_data->sigl = l64 << 11; - - return 0; - } -} - - -/* Get a float from user memory */ -int reg_load_single(float *single, FPU_REG *loaded_data) -{ - unsigned m32; - int exp; - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, single, 4); - m32 = get_fs_long((unsigned long *) single); - RE_ENTRANT_CHECK_ON; - - if (m32 & 0x80000000) - loaded_data->sign = SIGN_NEG; - else - loaded_data->sign = SIGN_POS; - if (!(m32 & 0x7fffffff)) - { - /* Zero */ - int c = loaded_data->sign; - reg_move(&CONST_Z, loaded_data); - loaded_data->sign = c; - return 0; - } - exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias; - m32 = (m32 & 0x7fffff) << 8; - if ( exp < SINGLE_Emin ) - { - /* De-normals */ - loaded_data->exp = SINGLE_Emin + EXP_BIAS; - loaded_data->tag = TW_Valid; - loaded_data->sigh = m32; - loaded_data->sigl = 0; - normalize_nuo(loaded_data); - return denormal_operand(); - } - else if ( exp > SINGLE_Emax ) - { - /* Infinity or NaN */ - if ( m32 == 0 ) - { - /* +- infinity */ - loaded_data->sigh = 0x80000000; - loaded_data->sigl = 0x00000000; - loaded_data->exp = EXP_Infinity; - loaded_data->tag = TW_Infinity; - return 0; - } - else - { - /* Must be a signaling or quiet NaN */ - loaded_data->exp = EXP_NaN; - loaded_data->tag = TW_NaN; - loaded_data->sigh = m32 | 0x80000000; - loaded_data->sigl = 0; - return 0; /* The calling function must look for NaNs */ - } - } - else - { - loaded_data->exp = exp + EXP_BIAS; - loaded_data->sigh = m32 | 0x80000000; - loaded_data->sigl = 0; - loaded_data->tag = TW_Valid; - return 0; - } -} - - -/* Get a long long from user memory */ -void reg_load_int64(long long *_s, FPU_REG *loaded_data) -{ - int e; - long long s; - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, _s, 8); - ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s); - ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s); - RE_ENTRANT_CHECK_ON; - - if (s == 0) - { reg_move(&CONST_Z, loaded_data); return; } - - if (s > 0) - loaded_data->sign = SIGN_POS; - else - { - s = -s; - loaded_data->sign = SIGN_NEG; - } - - e = EXP_BIAS + 63; - significand(loaded_data) = s; - loaded_data->exp = e; - loaded_data->tag = TW_Valid; - normalize_nuo(loaded_data); -} - - -/* Get a long from user memory */ -void reg_load_int32(long *_s, FPU_REG *loaded_data) -{ - long s; - int e; - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, _s, 4); - s = (long)get_fs_long((unsigned long *) _s); - RE_ENTRANT_CHECK_ON; - - if (s == 0) - { reg_move(&CONST_Z, loaded_data); return; } - - if (s > 0) - loaded_data->sign = SIGN_POS; - else - { - s = -s; - loaded_data->sign = SIGN_NEG; - } - - e = EXP_BIAS + 31; - loaded_data->sigh = s; - loaded_data->sigl = 0; - loaded_data->exp = e; - loaded_data->tag = TW_Valid; - normalize_nuo(loaded_data); -} - - -/* Get a short from user memory */ -void reg_load_int16(short *_s, FPU_REG *loaded_data) -{ - int s, e; - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, _s, 2); - /* Cast as short to get the sign extended. */ - s = (short)get_fs_word((unsigned short *) _s); - RE_ENTRANT_CHECK_ON; - - if (s == 0) - { reg_move(&CONST_Z, loaded_data); return; } - - if (s > 0) - loaded_data->sign = SIGN_POS; - else - { - s = -s; - loaded_data->sign = SIGN_NEG; - } - - e = EXP_BIAS + 15; - loaded_data->sigh = s << 16; - - loaded_data->sigl = 0; - loaded_data->exp = e; - loaded_data->tag = TW_Valid; - normalize_nuo(loaded_data); -} - - -/* Get a packed bcd array from user memory */ -void reg_load_bcd(char *s, FPU_REG *loaded_data) -{ - int pos; - unsigned char bcd; - long long l=0; - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, s, 10); - RE_ENTRANT_CHECK_ON; - for ( pos = 8; pos >= 0; pos--) - { - l *= 10; - RE_ENTRANT_CHECK_OFF; - bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos); - RE_ENTRANT_CHECK_ON; - l += bcd >> 4; - l *= 10; - l += bcd & 0x0f; - } - - RE_ENTRANT_CHECK_OFF; - loaded_data->sign = - ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ? - SIGN_NEG : SIGN_POS; - RE_ENTRANT_CHECK_ON; - - if (l == 0) - { - char sign = loaded_data->sign; - reg_move(&CONST_Z, loaded_data); - loaded_data->sign = sign; - } - else - { - significand(loaded_data) = l; - loaded_data->exp = EXP_BIAS + 63; - loaded_data->tag = TW_Valid; - normalize_nuo(loaded_data); - } -} - -/*===========================================================================*/ - -/* Put a long double into user memory */ -int reg_store_extended(long double *d, FPU_REG *st0_ptr) -{ - /* - The only exception raised by an attempt to store to an - extended format is the Invalid Stack exception, i.e. - attempting to store from an empty register. - */ - - if ( st0_ptr->tag != TW_Empty ) - { - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE, d, 10); - RE_ENTRANT_CHECK_ON; - write_to_extended(st0_ptr, (char *) d); - return 1; - } - - /* Empty register (stack underflow) */ - EXCEPTION(EX_StackUnder); - if ( control_word & CW_Invalid ) - { - /* The masked response */ - /* Put out the QNaN indefinite */ - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,d,10); - put_fs_long(0, (unsigned long *) d); - put_fs_long(0xc0000000, 1 + (unsigned long *) d); - put_fs_word(0xffff, 4 + (short *) d); - RE_ENTRANT_CHECK_ON; - return 1; - } - else - return 0; - -} - - -/* Put a double into user memory */ -int reg_store_double(double *dfloat, FPU_REG *st0_ptr) -{ - unsigned long l[2]; - unsigned long increment = 0; /* avoid gcc warnings */ - char st0_tag = st0_ptr->tag; - - if (st0_tag == TW_Valid) - { - int exp; - FPU_REG tmp; - - reg_move(st0_ptr, &tmp); - exp = tmp.exp - EXP_BIAS; - - if ( exp < DOUBLE_Emin ) /* It may be a denormal */ - { - int precision_loss; - - /* A denormal will always underflow. */ -#ifndef PECULIAR_486 - /* An 80486 is supposed to be able to generate - a denormal exception here, but... */ - if ( st0_ptr->exp <= EXP_UNDER ) - { - /* Underflow has priority. */ - if ( control_word & CW_Underflow ) - denormal_operand(); - } -#endif PECULIAR_486 - - 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) && - (st0_ptr->sigl & 0x000007ff)) ) -#endif PECULIAR_486 - { - EXCEPTION(EX_Underflow); - /* This is a special case: see sec 16.2.5.1 of - the 80486 book */ - if ( !(control_word & CW_Underflow) ) - return 0; - } - EXCEPTION(precision_loss); - if ( !(control_word & CW_Precision) ) - return 0; - } - l[0] = tmp.sigl; - l[1] = tmp.sigh; - } - else - { - if ( tmp.sigl & 0x000007ff ) - { - 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); - if ( !(control_word & CW_Overflow) ) - return 0; - set_precision_flag_up(); - if ( !(control_word & CW_Precision) ) - return 0; - - /* This is a special case: see sec 16.2.5.1 of the 80486 book */ - /* Overflow to infinity */ - l[0] = 0x00000000; /* Set to */ - l[1] = 0x7ff00000; /* + INF */ - } - else - { - /* Add the exponent */ - l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20); - } - } - } - else if (st0_tag == TW_Zero) - { - /* Number is zero */ - l[0] = 0; - l[1] = 0; - } - else if (st0_tag == TW_Infinity) - { - l[0] = 0; - l[1] = 0x7ff00000; - } - else if (st0_tag == TW_NaN) - { - /* See if we can get a valid NaN from the FPU_REG */ - l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21); - l[1] = ((st0_ptr->sigh >> 11) & 0xfffff); - if ( !(st0_ptr->sigh & 0x40000000) ) - { - /* It is a signalling NaN */ - EXCEPTION(EX_Invalid); - if ( !(control_word & CW_Invalid) ) - return 0; - l[1] |= (0x40000000 >> 11); - } - l[1] |= 0x7ff00000; - } - else if ( st0_tag == TW_Empty ) - { - /* Empty register (stack underflow) */ - EXCEPTION(EX_StackUnder); - if ( control_word & CW_Invalid ) - { - /* The masked response */ - /* Put out the QNaN indefinite */ - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8); - put_fs_long(0, (unsigned long *) dfloat); - put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat); - RE_ENTRANT_CHECK_ON; - return 1; - } - else - return 0; - } - if ( st0_ptr->sign ) - l[1] |= 0x80000000; - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8); - put_fs_long(l[0], (unsigned long *)dfloat); - put_fs_long(l[1], 1 + (unsigned long *)dfloat); - RE_ENTRANT_CHECK_ON; - - return 1; -} - - -/* Put a float into user memory */ -int reg_store_single(float *single, FPU_REG *st0_ptr) -{ - long templ; - unsigned long increment = 0; /* avoid gcc warnings */ - char st0_tag = st0_ptr->tag; - - if (st0_tag == TW_Valid) - { - int exp; - FPU_REG tmp; - - reg_move(st0_ptr, &tmp); - exp = tmp.exp - EXP_BIAS; - - if ( exp < SINGLE_Emin ) - { - int precision_loss; - - /* A denormal will always underflow. */ -#ifndef PECULIAR_486 - /* An 80486 is supposed to be able to generate - a denormal exception here, but... */ - if ( st0_ptr->exp <= EXP_UNDER ) - { - /* Underflow has priority. */ - if ( control_word & CW_Underflow ) - denormal_operand(); - } -#endif PECULIAR_486 - - 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) && - ((st0_ptr->sigh & 0x000000ff) || st0_ptr->sigl)) ) -#endif PECULIAR_486 - { - EXCEPTION(EX_Underflow); - /* This is a special case: see sec 16.2.5.1 of - the 80486 book */ - if ( !(control_word & EX_Underflow) ) - return 0; - } - EXCEPTION(precision_loss); - if ( !(control_word & EX_Precision) ) - return 0; - } - templ = tmp.sigl; - } - else - { - if ( tmp.sigl | (tmp.sigh & 0x000000ff) ) - { - 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); - if ( !(control_word & CW_Overflow) ) - return 0; - set_precision_flag_up(); - if ( !(control_word & CW_Precision) ) - return 0; - - /* This is a special case: see sec 16.2.5.1 of the 80486 book. */ - /* Masked response is overflow to infinity. */ - templ = 0x7f800000; - } - else - templ |= ((exp+SINGLE_Ebias) & 0xff) << 23; - } - } - else if (st0_tag == TW_Zero) - { - templ = 0; - } - else if (st0_tag == TW_Infinity) - { - templ = 0x7f800000; - } - else if (st0_tag == TW_NaN) - { - /* See if we can get a valid NaN from the FPU_REG */ - templ = st0_ptr->sigh >> 8; - if ( !(st0_ptr->sigh & 0x40000000) ) - { - /* It is a signalling NaN */ - EXCEPTION(EX_Invalid); - if ( !(control_word & CW_Invalid) ) - return 0; - templ |= (0x40000000 >> 8); - } - templ |= 0x7f800000; - } - else if ( st0_tag == TW_Empty ) - { - /* Empty register (stack underflow) */ - EXCEPTION(EX_StackUnder); - if ( control_word & EX_Invalid ) - { - /* The masked response */ - /* Put out the QNaN indefinite */ - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,(void *)single,4); - put_fs_long(0xffc00000, (unsigned long *) single); - RE_ENTRANT_CHECK_ON; - return 1; - } - else - return 0; - } -#ifdef PARANOID - else - { - EXCEPTION(EX_INTERNAL|0x163); - return 0; - } -#endif - if (st0_ptr->sign) - templ |= 0x80000000; - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,(void *)single,4); - put_fs_long(templ,(unsigned long *) single); - RE_ENTRANT_CHECK_ON; - - return 1; -} - - -/* Put a long long into user memory */ -int reg_store_int64(long long *d, FPU_REG *st0_ptr) -{ - FPU_REG t; - long long tll; - int precision_loss; - char st0_tag = st0_ptr->tag; - - if ( st0_tag == TW_Empty ) - { - /* Empty register (stack underflow) */ - EXCEPTION(EX_StackUnder); - goto invalid_operand; - } - else if ( (st0_tag == TW_Infinity) || - (st0_tag == TW_NaN) ) - { - EXCEPTION(EX_Invalid); - goto invalid_operand; - } - - reg_move(st0_ptr, &t); - precision_loss = round_to_int(&t); - ((long *)&tll)[0] = t.sigl; - ((long *)&tll)[1] = t.sigh; - if ( (precision_loss == 1) || - ((t.sigh & 0x80000000) && - !((t.sigh == 0x80000000) && (t.sigl == 0) && - (t.sign == SIGN_NEG))) ) - { - EXCEPTION(EX_Invalid); - /* This is a special case: see sec 16.2.5.1 of the 80486 book */ - invalid_operand: - if ( control_word & EX_Invalid ) - { - /* Produce something like QNaN "indefinite" */ - tll = 0x8000000000000000LL; - } - else - return 0; - } - else - { - if ( precision_loss ) - set_precision_flag(precision_loss); - if ( t.sign ) - tll = - tll; - } - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,(void *)d,8); - put_fs_long(((long *)&tll)[0],(unsigned long *) d); - put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d); - RE_ENTRANT_CHECK_ON; - - return 1; -} - - -/* Put a long into user memory */ -int reg_store_int32(long *d, FPU_REG *st0_ptr) -{ - FPU_REG t; - int precision_loss; - char st0_tag = st0_ptr->tag; - - if ( st0_tag == TW_Empty ) - { - /* Empty register (stack underflow) */ - EXCEPTION(EX_StackUnder); - goto invalid_operand; - } - else if ( (st0_tag == TW_Infinity) || - (st0_tag == TW_NaN) ) - { - EXCEPTION(EX_Invalid); - goto invalid_operand; - } - - reg_move(st0_ptr, &t); - precision_loss = round_to_int(&t); - 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 */ - invalid_operand: - if ( control_word & EX_Invalid ) - { - /* Produce something like QNaN "indefinite" */ - t.sigl = 0x80000000; - } - else - return 0; - } - else - { - if ( precision_loss ) - set_precision_flag(precision_loss); - if ( t.sign ) - t.sigl = -(long)t.sigl; - } - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,d,4); - put_fs_long(t.sigl, (unsigned long *) d); - RE_ENTRANT_CHECK_ON; - - return 1; -} - - -/* Put a short into user memory */ -int reg_store_int16(short *d, FPU_REG *st0_ptr) -{ - FPU_REG t; - int precision_loss; - char st0_tag = st0_ptr->tag; - - if ( st0_tag == TW_Empty ) - { - /* Empty register (stack underflow) */ - EXCEPTION(EX_StackUnder); - goto invalid_operand; - } - else if ( (st0_tag == TW_Infinity) || - (st0_tag == TW_NaN) ) - { - EXCEPTION(EX_Invalid); - goto invalid_operand; - } - - reg_move(st0_ptr, &t); - precision_loss = round_to_int(&t); - 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 */ - invalid_operand: - if ( control_word & EX_Invalid ) - { - /* Produce something like QNaN "indefinite" */ - t.sigl = 0x8000; - } - else - return 0; - } - else - { - if ( precision_loss ) - set_precision_flag(precision_loss); - if ( t.sign ) - t.sigl = -t.sigl; - } - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,d,2); - put_fs_word((short)t.sigl,(short *) d); - RE_ENTRANT_CHECK_ON; - - return 1; -} - - -/* Put a packed bcd array into user memory */ -int reg_store_bcd(char *d, FPU_REG *st0_ptr) -{ - FPU_REG t; - unsigned long long ll; - unsigned char b; - int i, precision_loss; - unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0; - char st0_tag = st0_ptr->tag; - - if ( st0_tag == TW_Empty ) - { - /* Empty register (stack underflow) */ - EXCEPTION(EX_StackUnder); - goto invalid_operand; - } - - reg_move(st0_ptr, &t); - precision_loss = round_to_int(&t); - ll = significand(&t); - - /* 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 */ - invalid_operand: - if ( control_word & CW_Invalid ) - { - /* Produce the QNaN "indefinite" */ - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,d,10); - for ( i = 0; i < 7; i++) - put_fs_byte(0, (unsigned char *) d+i); /* These bytes "undefined" */ - put_fs_byte(0xc0, (unsigned char *) d+7); /* This byte "undefined" */ - put_fs_byte(0xff, (unsigned char *) d+8); - put_fs_byte(0xff, (unsigned char *) d+9); - RE_ENTRANT_CHECK_ON; - return 1; - } - else - return 0; - } - else if ( precision_loss ) - { - /* Precision loss doesn't stop the data transfer */ - set_precision_flag(precision_loss); - } - - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,d,10); - RE_ENTRANT_CHECK_ON; - for ( i = 0; i < 9; i++) - { - b = div_small(&ll, 10); - b |= (div_small(&ll, 10)) << 4; - RE_ENTRANT_CHECK_OFF; - put_fs_byte(b,(unsigned char *) d+i); - RE_ENTRANT_CHECK_ON; - } - RE_ENTRANT_CHECK_OFF; - put_fs_byte(sign,(unsigned char *) d+9); - RE_ENTRANT_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 - largest possible value */ -int round_to_int(FPU_REG *r) -{ - char very_big; - unsigned eax; - - if (r->tag == TW_Zero) - { - /* Make sure that zero is returned */ - significand(r) = 0; - return 0; /* o.k. */ - } - - if (r->exp > EXP_BIAS + 63) - { - r->sigl = r->sigh = ~0; /* The largest representable number */ - return 1; /* overflow */ - } - - eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp); - very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */ -#define half_or_more (eax & 0x80000000) -#define frac_part (eax) -#define more_than_half ((eax & 0x80000001) == 0x80000001) - switch (control_word & CW_RC) - { - case RC_RND: - if ( more_than_half /* nearest */ - || (half_or_more && (r->sigl & 1)) ) /* odd -> even */ - { - if ( very_big ) return 1; /* overflow */ - significand(r) ++; - return PRECISION_LOST_UP; - } - break; - case RC_DOWN: - if (frac_part && r->sign) - { - if ( very_big ) return 1; /* overflow */ - significand(r) ++; - return PRECISION_LOST_UP; - } - break; - case RC_UP: - if (frac_part && !r->sign) - { - if ( very_big ) return 1; /* overflow */ - significand(r) ++; - return PRECISION_LOST_UP; - } - break; - case RC_CHOP: - break; - } - - return eax ? PRECISION_LOST_DOWN : 0; - -} - -/*===========================================================================*/ - -char *fldenv(fpu_addr_modes addr_modes, char *s) -{ - unsigned short tag_word = 0; - unsigned char tag; - int i; - - if ( (addr_modes.default_mode == VM86) || - ((addr_modes.default_mode == PM16) - ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) ) - { - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, s, 0x0e); - control_word = get_fs_word((unsigned short *) s); - partial_status = get_fs_word((unsigned short *) (s+2)); - tag_word = get_fs_word((unsigned short *) (s+4)); - instruction_address.offset = get_fs_word((unsigned short *) (s+6)); - instruction_address.selector = get_fs_word((unsigned short *) (s+8)); - operand_address.offset = get_fs_word((unsigned short *) (s+0x0a)); - operand_address.selector = get_fs_word((unsigned short *) (s+0x0c)); - RE_ENTRANT_CHECK_ON; - s += 0x0e; - if ( addr_modes.default_mode == VM86 ) - { - instruction_address.offset - += (instruction_address.selector & 0xf000) << 4; - operand_address.offset += (operand_address.selector & 0xf000) << 4; - } - } - else - { - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, s, 0x1c); - control_word = get_fs_word((unsigned short *) s); - partial_status = get_fs_word((unsigned short *) (s+4)); - tag_word = get_fs_word((unsigned short *) (s+8)); - instruction_address.offset = get_fs_long((unsigned long *) (s+0x0c)); - instruction_address.selector = get_fs_word((unsigned short *) (s+0x10)); - instruction_address.opcode = get_fs_word((unsigned short *) (s+0x12)); - operand_address.offset = get_fs_long((unsigned long *) (s+0x14)); - operand_address.selector = get_fs_long((unsigned long *) (s+0x18)); - RE_ENTRANT_CHECK_ON; - s += 0x1c; - } - -#ifdef PECULIAR_486 - control_word &= ~0xe080; -#endif PECULIAR_486 - - top = (partial_status >> SW_Top_Shift) & 7; - - if ( partial_status & ~control_word & CW_Exceptions ) - partial_status |= (SW_Summary | SW_Backward); - else - partial_status &= ~(SW_Summary | SW_Backward); - - for ( i = 0; i < 8; i++ ) - { - tag = tag_word & 3; - tag_word >>= 2; - - if ( tag == 3 ) - /* New tag is empty. Accept it */ - regs[i].tag = TW_Empty; - else if ( regs[i].tag == TW_Empty ) - { - /* Old tag is empty and new tag is not empty. New tag is determined - by old reg contents */ - if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias ) - { - if ( !(regs[i].sigl | regs[i].sigh) ) - regs[i].tag = TW_Zero; - else - regs[i].tag = TW_Valid; - } - else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias ) - { - if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) ) - regs[i].tag = TW_Infinity; - else - regs[i].tag = TW_NaN; - } - else - regs[i].tag = TW_Valid; - } - /* Else old tag is not empty and new tag is not empty. Old tag - remains correct */ - } - - return s; -} - - -void frstor(fpu_addr_modes addr_modes, char *data_address) -{ - int i, stnr; - unsigned char tag; - char *s = fldenv(addr_modes, data_address); - - for ( i = 0; i < 8; i++ ) - { - /* Load each register. */ - stnr = (i+top) & 7; - tag = regs[stnr].tag; /* Derived from the fldenv() loaded tag word. */ - reg_load_extended((long double *)(s+i*10), ®s[stnr]); - if ( tag == TW_Empty ) /* The loaded data over-rides all other cases. */ - regs[stnr].tag = tag; - } - -} - - -unsigned short tag_word(void) -{ - unsigned short word = 0; - unsigned char tag; - int i; - - for ( i = 7; i >= 0; i-- ) - { - switch ( tag = regs[i].tag ) - { - case TW_Valid: - if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) ) - tag = 2; - break; - case TW_Infinity: - case TW_NaN: - tag = 2; - break; - case TW_Empty: - tag = 3; - break; - /* TW_Zero already has the correct value */ - } - word <<= 2; - word |= tag; - } - return word; -} - - -char *fstenv(fpu_addr_modes addr_modes, char *d) -{ - if ( (addr_modes.default_mode == VM86) || - ((addr_modes.default_mode == PM16) - ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) ) - { - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,d,14); -#ifdef PECULIAR_486 - put_fs_long(control_word & ~0xe080, (unsigned short *) d); -#else - put_fs_word(control_word, (unsigned short *) d); -#endif PECULIAR_486 - put_fs_word(status_word(), (unsigned short *) (d+2)); - put_fs_word(tag_word(), (unsigned short *) (d+4)); - put_fs_word(instruction_address.offset, (unsigned short *) (d+6)); - put_fs_word(operand_address.offset, (unsigned short *) (d+0x0a)); - if ( addr_modes.default_mode == VM86 ) - { - put_fs_word((instruction_address.offset & 0xf0000) >> 4, - (unsigned short *) (d+8)); - put_fs_word((operand_address.offset & 0xf0000) >> 4, - (unsigned short *) (d+0x0c)); - } - else - { - put_fs_word(instruction_address.selector, (unsigned short *) (d+8)); - put_fs_word(operand_address.selector, (unsigned short *) (d+0x0c)); - } - RE_ENTRANT_CHECK_ON; - d += 0x0e; - } - else - { - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,d,28); -#ifdef PECULIAR_486 - /* An 80486 sets all the reserved bits to 1. */ - put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d); - put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4)); - put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8)); -#else - put_fs_word(control_word, (unsigned short *) d); - put_fs_word(status_word(), (unsigned short *) (d+4)); - put_fs_word(tag_word(), (unsigned short *) (d+8)); -#endif PECULIAR_486 - put_fs_long(instruction_address.offset, (unsigned long *) (d+0x0c)); - put_fs_word(instruction_address.selector, (unsigned short *) (d+0x10)); - put_fs_word(instruction_address.opcode, (unsigned short *) (d+0x12)); - put_fs_long(operand_address.offset, (unsigned long *) (d+0x14)); -#ifdef PECULIAR_486 - /* An 80486 sets all the reserved bits to 1. */ - put_fs_word(operand_address.selector, (unsigned short *) (d+0x18)); - put_fs_word(0xffff, (unsigned short *) (d+0x1a)); -#else - put_fs_long(operand_address.selector, (unsigned long *) (d+0x18)); -#endif PECULIAR_486 - RE_ENTRANT_CHECK_ON; - d += 0x1c; - } - - control_word |= CW_Exceptions; - partial_status &= ~(SW_Summary | SW_Backward); - - return d; -} - - -void fsave(fpu_addr_modes addr_modes, char *data_address) -{ - char *d; - int i; - - d = fstenv(addr_modes, data_address); - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,d,80); - RE_ENTRANT_CHECK_ON; - for ( i = 0; i < 8; i++ ) - write_to_extended(®s[(top + i) & 7], d + 10 * i); - - finit(); - -} - -/*===========================================================================*/ - -/* - A call to this function must be preceded by a call to - FPU_verify_area() to verify access to the 10 bytes at d - */ -static void write_to_extended(FPU_REG *rp, char *d) -{ - long e; - FPU_REG tmp; - - e = rp->exp - EXP_BIAS + EXTENDED_Ebias; - -#ifdef PARANOID - switch ( rp->tag ) - { - case TW_Zero: - if ( rp->sigh | rp->sigl | e ) - EXCEPTION(EX_INTERNAL | 0x160); - break; - case TW_Infinity: - case TW_NaN: - if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) ) - EXCEPTION(EX_INTERNAL | 0x161); - break; - default: - if (e > 0x7fff || e < -63) - EXCEPTION(EX_INTERNAL | 0x162); - } -#endif PARANOID - - /* - All numbers except denormals are stored internally in a - format which is compatible with the extended real number - format. - */ - if ( e > 0 ) - { - /* just copy the reg */ - RE_ENTRANT_CHECK_OFF; - put_fs_long(rp->sigl, (unsigned long *) d); - put_fs_long(rp->sigh, (unsigned long *) (d + 4)); - RE_ENTRANT_CHECK_ON; - } - else - { - /* - The number is a de-normal stored as a normal using our - extra exponent range, or is Zero. - Convert it back to a de-normal, or leave it as Zero. - */ - reg_move(rp, &tmp); - tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 63 */ - round_to_int(&tmp); - e = 0; - RE_ENTRANT_CHECK_OFF; - put_fs_long(tmp.sigl, (unsigned long *) d); - put_fs_long(tmp.sigh, (unsigned long *) (d + 4)); - RE_ENTRANT_CHECK_ON; - } - e |= rp->sign == SIGN_POS ? 0 : 0x8000; - RE_ENTRANT_CHECK_OFF; - put_fs_word(e, (unsigned short *) (d + 8)); - RE_ENTRANT_CHECK_ON; -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_mul.c linux/drivers/FPU-emu/reg_mul.c --- v1.1.76/linux/drivers/FPU-emu/reg_mul.c Fri Feb 25 14:42:46 1994 +++ linux/drivers/FPU-emu/reg_mul.c Thu Jan 1 02:00:00 1970 @@ -1,105 +0,0 @@ -/*---------------------------------------------------------------------------+ - | 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 billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | 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 */ -int reg_mul(FPU_REG const *a, FPU_REG const *b, - FPU_REG *dest, unsigned int control_w) -{ - char saved_sign = dest->sign; - char sign = (a->sign ^ b->sign); - - if (!(a->tag | b->tag)) - { - /* Both regs Valid, this should be the most common case. */ - dest->sign = sign; - if ( reg_u_mul(a, b, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - return 0; - } - 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 1; - } -#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); - /* The 80486 book says that the answer is +0, but a real - 80486 behaves this way. - IEEE-754 apparently says it should be this way. */ - dest->sign = sign; - return 0; - } - else - { - /* Must have infinities, NaNs, etc */ - if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) - { return real_2op_NaN(a, b, dest); } - else if (a->tag == TW_Infinity) - { - if (b->tag == TW_Zero) - { return arith_invalid(dest); } /* Zero*Infinity is invalid */ - else - { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(a, dest); - dest->sign = sign; - } - return 0; - } - else if (b->tag == TW_Infinity) - { - if (a->tag == TW_Zero) - { return arith_invalid(dest); } /* Zero*Infinity is invalid */ - else - { -#ifdef DENORM_OPERAND - if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(b, dest); - dest->sign = sign; - } - return 0; - } -#ifdef PARANOID - else - { - EXCEPTION(EX_INTERNAL|0x102); - return 1; - } -#endif PARANOID - } -} diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_norm.S linux/drivers/FPU-emu/reg_norm.S --- v1.1.76/linux/drivers/FPU-emu/reg_norm.S Tue Jan 11 11:10:49 1994 +++ linux/drivers/FPU-emu/reg_norm.S Thu Jan 1 02:00:00 1970 @@ -1,150 +0,0 @@ -/*---------------------------------------------------------------------------+ - | reg_norm.S | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | Normalize the value in a FPU_REG. | - | | - | Call from C as: | - | void normalize(FPU_REG *n) | - | | - | void normalize_nuo(FPU_REG *n) | - | | - +---------------------------------------------------------------------------*/ - -#include "fpu_asm.h" - - -.text - - .align 2,144 -.globl _normalize - -_normalize: - pushl %ebp - movl %esp,%ebp - pushl %ebx - - movl PARAM1,%ebx - -#ifdef PARANOID - cmpb TW_Valid,TAG(%ebx) - je L_ok - - pushl $0x220 - call _exception - addl $4,%esp - -L_ok: -#endif PARANOID - - 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 */ - - 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 - -#ifdef PARANOID - cmpb TW_Valid,TAG(%ebx) - je L_ok_nuo - - pushl $0x221 - call _exception - addl $4,%esp - -L_ok_nuo: -#endif PARANOID - - movl SIGH(%ebx),%edx - movl SIGL(%ebx),%eax - - orl %edx,%edx /* ms bits */ - js L_exit /* Already normalized */ - jnz L_nuo_shift_1 /* Shift left 1 - 31 bits */ - - orl %eax,%eax - jz L_zero /* The contents are zero */ - - 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 -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_round.S linux/drivers/FPU-emu/reg_round.S --- v1.1.76/linux/drivers/FPU-emu/reg_round.S Fri Aug 19 14:06:50 1994 +++ linux/drivers/FPU-emu/reg_round.S Thu Jan 1 02:00:00 1970 @@ -1,701 +0,0 @@ - .file "reg_round.S" -/*---------------------------------------------------------------------------+ - | reg_round.S | - | | - | Rounding/truncation/etc for FPU basic arithmetic functions. | - | | - | Copyright (C) 1993 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | This code has four possible entry points. | - | The following must be entered by a jmp instruction: | - | fpu_reg_round, fpu_reg_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) | - | | - | For correct "up" and "down" rounding, the argument must have the correct | - | sign. | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | Four entry points. | - | | - | Needed by both the fpu_reg_round and fpu_reg_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_reg_round_sqrt entry point: | - | %cx A control word in the same format as the FPU control word. | - | Otherwise, PARAM4 must give such a value. | - | | - | | - | 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" - -/* Flags for FPU_bits_lost */ -#define LOST_DOWN $1 -#define LOST_UP $2 - -/* Flags for FPU_denormal */ -#define DENORMAL $1 -#define UNMASKED_UNDERFLOW $2 - - -#ifndef NON_REENTRANT_FPU -/* Make the code re-entrant by putting - local storage on the stack: */ -#define FPU_bits_lost (%esp) -#define FPU_denormal 1(%esp) - -#else -/* Not re-entrant, so we can gain speed by putting - local storage in a static area: */ -.data - .align 2,0 -FPU_bits_lost: - .byte 0 -FPU_denormal: - .byte 0 -#endif NON_REENTRANT_FPU - - -.text - .align 2,144 -.globl fpu_reg_round -.globl fpu_reg_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_reg_round_sqrt - -fpu_reg_round: /* Normal entry point */ - movl PARAM4,%ecx - -fpu_reg_round_sqrt: /* Entry point from wm_sqrt.S */ - -#ifndef NON_REENTRANT_FPU - pushl %ebx /* adjust the stack pointer */ -#endif NON_REENTRANT_FPU - -#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 PECULIAR_486 -/* With the precision control bits set to 01 "(reserved)", a real 80486 - behaves as if the precision control bits were set to 11 "64 bits" */ - cmpl PR_RESERVED_BITS,%ecx - je LRound_To_64 -#ifdef PARANOID - jmp L_bugged_denorm_486 -#endif PARANOID -#else -#ifdef PARANOID - jmp L_bugged_denorm /* There is no bug, just a bad control word */ -#endif PARANOID -#endif PECULIAR_486 - - -/* 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_round24 -#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_round53 -#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_round64 -#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 - - /* Overflow, adjust the result (significand 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: - /* store the result */ - movb TW_Valid,TAG(%edi) - -xL_Store_significand: - movl %eax,SIGH(%edi) - movl %ebx,SIGL(%edi) - - xorl %eax,%eax /* No errors detected. */ - - cmpl EXP_OVER,EXP(%edi) - jge L_overflow - -fpu_reg_round_exit: -#ifndef NON_REENTRANT_FPU - popl %ebx /* adjust the stack pointer */ -#endif NON_REENTRANT_FPU - -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_UNDER+1,%ecx - subl EXP(%edi),%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 /* Save whether %edx is non-zero */ - 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: - 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 - -#ifdef PECULIAR_486 - /* - * This implements a special feature of 80486 behaviour. - * Underflow will be signalled even if the number is - * not a denormal after rounding. - * This difference occurs only for masked underflow, and not - * in the unmasked case. - * Actual 80486 behaviour differs from this in some circumstances. - */ - orl %eax,%eax /* ms bits */ - js LNormalise_shift_done /* Will be masked underflow */ -#endif PECULIAR_486 - - orl %eax,%eax /* ms bits */ - js xL_Normalised /* No longer a denormal */ - - 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 - -/* Reduce the exponent to EXP_UNDER */ - movl EXP_UNDER,EXP(%edi) - 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_reg_round_exit - - -xSignal_underflow: - /* The number may have been changed to a non-denormal */ - /* by the rounding operations. */ - cmpl EXP_UNDER,EXP(%edi) - jle xDo_unmasked_underflow - - jmp xL_Normalised - -xDo_unmasked_underflow: - /* Increase the exponent by the magic number */ - addl $(3*(1<<13)),EXP(%edi) - push %eax - pushl EX_Underflow - call EXCEPTION - popl %eax - popl %eax - jmp xL_Normalised - - -#ifdef PARANOID -#ifdef PECULIAR_486 -L_bugged_denorm_486: - pushl EX_INTERNAL|0x236 - call EXCEPTION - popl %ebx - jmp L_exception_exit -#else -L_bugged_denorm: - pushl EX_INTERNAL|0x230 - call EXCEPTION - popl %ebx - jmp L_exception_exit -#endif PECULIAR_486 - -L_bugged_round24: - pushl EX_INTERNAL|0x231 - call EXCEPTION - popl %ebx - jmp L_exception_exit - -L_bugged_round53: - pushl EX_INTERNAL|0x232 - call EXCEPTION - popl %ebx - jmp L_exception_exit - -L_bugged_round64: - pushl EX_INTERNAL|0x233 - call EXCEPTION - popl %ebx - jmp L_exception_exit - -L_norm_bugged: - pushl EX_INTERNAL|0x234 - call EXCEPTION - popl %ebx - jmp L_exception_exit - -L_entry_bugged: - pushl EX_INTERNAL|0x235 - call EXCEPTION - popl %ebx -L_exception_exit: - mov $1,%eax - jmp fpu_reg_round_exit -#endif PARANOID diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_u_add.S linux/drivers/FPU-emu/reg_u_add.S --- v1.1.76/linux/drivers/FPU-emu/reg_u_add.S Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/reg_u_add.S Thu Jan 1 02:00:00 1970 @@ -1,189 +0,0 @@ - .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. | - | | - | Copyright (C) 1992,1993 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | Call from C as: | - | void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | - | int control_w) | - | | - +---------------------------------------------------------------------------*/ - -/* - | 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 - 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 - - movl EXP(%esi),%ecx - subl EXP(%edi),%ecx /* exp1 - exp2 */ - 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_reg_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 -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_u_div.S linux/drivers/FPU-emu/reg_u_div.S --- v1.1.76/linux/drivers/FPU-emu/reg_u_div.S Thu Jun 2 10:28:28 1994 +++ linux/drivers/FPU-emu/reg_u_div.S Thu Jan 1 02:00:00 1970 @@ -1,477 +0,0 @@ - .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 billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | 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) */ - - -#ifndef NON_REENTRANT_FPU -/* - Local storage on the stack: - Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 - Overflow flag: ovfl_flag - */ -#define FPU_accum_3 -4(%ebp) -#define FPU_accum_2 -8(%ebp) -#define FPU_accum_1 -12(%ebp) -#define FPU_accum_0 -16(%ebp) -#define FPU_result_1 -20(%ebp) -#define FPU_result_2 -24(%ebp) -#define FPU_ovfl_flag -28(%ebp) - -#else -.data -/* - Local storage in a static area: - Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 - Overflow flag: ovfl_flag - */ - .align 2,0 -FPU_accum_3: - .long 0 -FPU_accum_2: - .long 0 -FPU_accum_1: - .long 0 -FPU_accum_0: - .long 0 -FPU_result_1: - .long 0 -FPU_result_2: - .long 0 -FPU_ovfl_flag: - .byte 0 -#endif NON_REENTRANT_FPU - - -.text - .align 2,144 - -.globl _reg_u_div - -.globl _divide_kernel - -_reg_u_div: - pushl %ebp - movl %esp,%ebp -#ifndef NON_REENTRANT_FPU - subl $28,%esp -#endif NON_REENTRANT_FPU - - 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 FPU_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,FPU_result_2 - - /* Work on the remainder of the first division */ - xorl %eax,%eax - divl %ecx - movl %eax,FPU_result_1 - - /* Work on the remainder of the 64 bit division */ - xorl %eax,%eax - divl %ecx - - testb $255,FPU_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 FPU_result_2 - rcrl FPU_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,FPU_accum_2 - movl SIGH(%esi),%eax - movl %eax,FPU_accum_3 - xorl %eax,%eax - movl %eax,FPU_accum_1 /* zero the extension */ - movl %eax,FPU_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,FPU_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 FPU_ovfl_flag /* Keep a record */ - - subl SIGL(%ebx),%eax - sbbl SIGH(%ebx),%edx /* Prevent the overflow */ - movl %eax,FPU_accum_2 - movl %edx,FPU_accum_3 - -LLess_than_1: -/* At this point, we have a dividend < divisor, with a record of - adjustment in FPU_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,FPU_result_2 /* Put the result in the answer */ - - mull SIGH(%ebx) /* mul by the ms dw of the denom */ - - subl %eax,FPU_accum_2 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_3 - - movl FPU_result_2,%eax /* Get the result back */ - mull SIGL(%ebx) /* now mul the ls dw of the denom */ - - subl %eax,FPU_accum_1 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_2 - sbbl $0,FPU_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 FPU_result_2 /* Correct the answer */ - - movl SIGL(%ebx),%eax - movl SIGH(%ebx),%edx - subl %eax,FPU_accum_1 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_2 - -#ifdef PARANOID - sbbl $0,FPU_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, FPU_accum_0 not used from now on */ -LDo_2nd_32_bits: - movl FPU_accum_2,%edx /* get the reduced num */ - movl FPU_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,FPU_accum_2 - movl %eax,FPU_accum_1 - - incl FPU_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,FPU_result_1 /* Put the result in the answer */ - - mull SIGH(%ebx) /* mul by the ms dw of the denom */ - - subl %eax,FPU_accum_1 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_2 - -#ifdef PARANOID - jc L_bugged_2 -#endif PARANOID - - movl FPU_result_1,%eax /* Get the result back */ - mull SIGL(%ebx) /* now mul the ls dw of the denom */ - - subl %eax,FPU_accum_0 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */ - sbbl $0,FPU_accum_2 - -#ifdef PARANOID - jc L_bugged_2 -#endif PARANOID - - jz LDo_3rd_32_bits - -#ifdef PARANOID - cmpl $1,FPU_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,FPU_accum_0 /* Subtract from the num local reg */ - sbbl %edx,FPU_accum_1 - sbbl $0,FPU_accum_2 - -#ifdef PARANOID - jc L_bugged_2 - jne L_bugged_2 -#endif PARANOID - - addl $1,FPU_result_1 /* Correct the answer */ - adcl $0,FPU_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 FPU_accum_1,%edx /* get the reduced num */ - movl FPU_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,FPU_accum_1 - movl %eax,FPU_accum_0 - - addl $1,FPU_result_1 /* Reflect the subtraction in the answer */ - adcl $0,FPU_result_2 - jne LRound_prep - jnc LRound_prep - - /* This is a tricky spot, there is an overflow of the answer */ - movb $255,FPU_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 FPU_accum_0,%ecx - movl FPU_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,FPU_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 FPU_result_2 - rcrl FPU_result_1 - rcrl %eax - -/* Round the result as required */ -LRound_precision: - decl EXP(%edi) /* binary point between 1st & 2nd bits */ - - movl %eax,%edx - movl FPU_result_1,%ebx - movl FPU_result_2,%eax - jmp fpu_reg_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 -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_u_mul.S linux/drivers/FPU-emu/reg_u_mul.S --- v1.1.76/linux/drivers/FPU-emu/reg_u_mul.S Thu Jun 2 10:28:28 1994 +++ linux/drivers/FPU-emu/reg_u_mul.S Thu Jan 1 02:00:00 1970 @@ -1,163 +0,0 @@ - .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 billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | 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" - - - -#ifndef NON_REENTRANT_FPU -/* Local storage on the stack: */ -#define FPU_accum_0 -4(%ebp) /* ms word */ -#define FPU_accum_1 -8(%ebp) - -#else -/* Local storage in a static area: */ -.data - .align 4,0 -FPU_accum_0: - .long 0 -FPU_accum_1: - .long 0 -#endif NON_REENTRANT_FPU - - -.text - .align 2,144 - -.globl _reg_u_mul -_reg_u_mul: - pushl %ebp - movl %esp,%ebp -#ifndef NON_REENTRANT_FPU - subl $8,%esp -#endif NON_REENTRANT_FPU - - 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,FPU_accum_0 - movl %edx,FPU_accum_1 - - movl SIGL(%esi),%eax - mull SIGH(%edi) - addl %eax,FPU_accum_1 - adcl %edx,%ebx -/* adcl $0,%ecx // overflow here is not possible */ - - movl SIGH(%esi),%eax - mull SIGL(%edi) - addl %eax,FPU_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,FPU_accum_0 - rcll $1,FPU_accum_1 - rcll $1,%ebx - rcll $1,%ecx - decl EXP(%edi) - -LResult_Normalised: - movl FPU_accum_0,%eax - movl FPU_accum_1,%edx - orl %eax,%eax - jz L_extent_zero - - orl $1,%edx - -L_extent_zero: - movl %ecx,%eax - jmp fpu_reg_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 -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/reg_u_sub.S linux/drivers/FPU-emu/reg_u_sub.S --- v1.1.76/linux/drivers/FPU-emu/reg_u_sub.S Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/reg_u_sub.S Thu Jan 1 02:00:00 1970 @@ -1,292 +0,0 @@ - .file "reg_u_sub.S" -/*---------------------------------------------------------------------------+ - | reg_u_sub.S | - | | - | Core floating point subtraction routine. | - | | - | Copyright (C) 1992,1993 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | Call from C as: | - | void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | - | int control_w) | - | | - +---------------------------------------------------------------------------*/ - -/* - | 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 - - movl EXP(%esi),%ecx - subl EXP(%edi),%ecx /* exp1 - exp2 */ - -#ifdef PARANOID - /* source 2 is always smaller than source 1 */ - js L_bugged_1 - - testl $0x80000000,SIGH(%edi) /* The args are assumed to be 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) - xchg %edx,%eax - jmp fpu_reg_round - -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 /* %eax contains zero */ - -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_reg_round /* Round the result */ - - -#ifdef PARANOID -L_bugged_1: - pushl EX_INTERNAL|0x206 - call EXCEPTION - pop %ebx - jmp L_error_exit - -L_bugged_2: - pushl EX_INTERNAL|0x209 - call EXCEPTION - pop %ebx - jmp L_error_exit - -L_bugged_3: - pushl EX_INTERNAL|0x210 - call EXCEPTION - pop %ebx - jmp L_error_exit - -L_bugged_4: - pushl EX_INTERNAL|0x211 - call EXCEPTION - pop %ebx - jmp L_error_exit - -L_bugged: - pushl EX_INTERNAL|0x212 - call EXCEPTION - pop %ebx - jmp L_error_exit -#endif PARANOID - - -L_error_exit: - movl $1,%eax -L_exit: - popl %ebx - popl %edi - popl %esi - leave - ret diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/round_Xsig.S linux/drivers/FPU-emu/round_Xsig.S --- v1.1.76/linux/drivers/FPU-emu/round_Xsig.S Mon Aug 1 08:19:15 1994 +++ linux/drivers/FPU-emu/round_Xsig.S Thu Jan 1 02:00:00 1970 @@ -1,148 +0,0 @@ -/*---------------------------------------------------------------------------+ - | round_Xsig.S | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | Normalize and round a 12 byte quantity. | - | Call from C as: | - | int round_Xsig(Xsig *n) | - | | - | Normalize a 12 byte quantity. | - | Call from C as: | - | int norm_Xsig(Xsig *n) | - | | - | Each function returns the size of the shift (nr of bits). | - | | - +---------------------------------------------------------------------------*/ - .file "round_Xsig.S" - -#include "fpu_asm.h" - - -.text - - .align 2,144 -.globl _round_Xsig - -_round_Xsig: - pushl %ebp - movl %esp,%ebp - pushl %ebx /* Reserve some space */ - pushl %ebx - pushl %esi - - movl PARAM1,%esi - - movl 8(%esi),%edx - movl 4(%esi),%ebx - movl (%esi),%eax - - movl $0,-4(%ebp) - - orl %edx,%edx /* ms bits */ - js L_round /* Already normalized */ - jnz L_shift_1 /* Shift left 1 - 31 bits */ - - movl %ebx,%edx - movl %eax,%ebx - xorl %eax,%eax - movl $-32,-4(%ebp) - -/* 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 - subl %ecx,-4(%ebp) - shld %cl,%ebx,%edx - shld %cl,%eax,%ebx - shl %cl,%eax - -L_round: - testl $0x80000000,%eax - jz L_exit - - addl $1,%ebx - adcl $0,%edx - jnz L_exit - - movl $0x80000000,%edx - incl -4(%ebp) - -L_exit: - movl %edx,8(%esi) - movl %ebx,4(%esi) - movl %eax,(%esi) - - movl -4(%ebp),%eax - - popl %esi - popl %ebx - leave - ret - - - - - .align 2,144 -.globl _norm_Xsig - -_norm_Xsig: - pushl %ebp - movl %esp,%ebp - pushl %ebx /* Reserve some space */ - pushl %ebx - pushl %esi - - movl PARAM1,%esi - - movl 8(%esi),%edx - movl 4(%esi),%ebx - movl (%esi),%eax - - movl $0,-4(%ebp) - - orl %edx,%edx /* ms bits */ - js L_n_exit /* Already normalized */ - jnz L_n_shift_1 /* Shift left 1 - 31 bits */ - - movl %ebx,%edx - movl %eax,%ebx - xorl %eax,%eax - movl $-32,-4(%ebp) - - orl %edx,%edx /* ms bits */ - js L_n_exit /* Normalized now */ - jnz L_n_shift_1 /* Shift left 1 - 31 bits */ - - movl %ebx,%edx - movl %eax,%ebx - xorl %eax,%eax - addl $-32,-4(%ebp) - jmp L_n_exit /* Might not be normalized, - but shift no more. */ - -/* We need to shift left by 1 - 31 bits */ -L_n_shift_1: - bsrl %edx,%ecx /* get the required shift in %ecx */ - subl $31,%ecx - negl %ecx - subl %ecx,-4(%ebp) - shld %cl,%ebx,%edx - shld %cl,%eax,%ebx - shl %cl,%eax - -L_n_exit: - movl %edx,8(%esi) - movl %ebx,4(%esi) - movl %eax,(%esi) - - movl -4(%ebp),%eax - - popl %esi - popl %ebx - leave - ret - diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/shr_Xsig.S linux/drivers/FPU-emu/shr_Xsig.S --- v1.1.76/linux/drivers/FPU-emu/shr_Xsig.S Mon Aug 1 08:19:16 1994 +++ linux/drivers/FPU-emu/shr_Xsig.S Thu Jan 1 02:00:00 1970 @@ -1,90 +0,0 @@ - .file "shr_Xsig.S" -/*---------------------------------------------------------------------------+ - | shr_Xsig.S | - | | - | 12 byte right shift function | - | | - | Copyright (C) 1992,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | Call from C as: | - | void shr_Xsig(Xsig *arg, unsigned nr) | - | | - | Extended shift right function. | - | Fastest for small shifts. | - | Shifts the 12 byte quantity pointed to by the first arg (arg) | - | right by the number of bits specified by the second arg (nr). | - | | - +---------------------------------------------------------------------------*/ - -#include "fpu_asm.h" - -.text - .align 2,144 - - .globl _shr_Xsig -_shr_Xsig: - 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),%eax /* lsl */ - movl 4(%esi),%ebx /* midl */ - movl 8(%esi),%edx /* msl */ - shrd %cl,%ebx,%eax - shrd %cl,%edx,%ebx - shr %cl,%edx - movl %eax,(%esi) - movl %ebx,4(%esi) - movl %edx,8(%esi) - popl %ebx - popl %esi - leave - ret - -L_more_than_31: - cmpl $64,%ecx - jnc L_more_than_63 - - subb $32,%cl - movl 4(%esi),%eax /* midl */ - movl 8(%esi),%edx /* msl */ - shrd %cl,%edx,%eax - shr %cl,%edx - movl %eax,(%esi) - movl %edx,4(%esi) - movl $0,8(%esi) - popl %esi - leave - ret - -L_more_than_63: - cmpl $96,%ecx - jnc L_more_than_95 - - subb $64,%cl - movl 8(%esi),%eax /* msl */ - shr %cl,%eax - xorl %edx,%edx - movl %eax,(%esi) - movl %edx,4(%esi) - movl %edx,8(%esi) - popl %esi - leave - ret - -L_more_than_95: - xorl %eax,%eax - movl %eax,(%esi) - movl %eax,4(%esi) - movl %eax,8(%esi) - popl %esi - leave - ret diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/status_w.h linux/drivers/FPU-emu/status_w.h --- v1.1.76/linux/drivers/FPU-emu/status_w.h Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/status_w.h Thu Jan 1 02:00:00 1970 @@ -1,65 +0,0 @@ -/*---------------------------------------------------------------------------+ - | status_w.h | - | | - | Copyright (C) 1992,1993 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - +---------------------------------------------------------------------------*/ - -#ifndef _STATUS_H_ -#define _STATUS_H_ - -#include "fpu_emu.h" /* for definition of PECULIAR_486 */ - -#ifdef __ASSEMBLER__ -#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 __ASSEMBLER__ - -#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 status_word() \ - ((partial_status & ~SW_Top & 0xffff) | ((top << SW_Top_Shift) & SW_Top)) -#define setcc(cc) ({ \ - partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \ - partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); }) - -#ifdef PECULIAR_486 - /* Default, this conveys no information, but an 80486 does it. */ - /* Clear the SW_C1 bit, "other bits undefined". */ -# define clear_C1() { partial_status &= ~SW_C1; } -# else -# define clear_C1() -#endif PECULIAR_486 - -#endif __ASSEMBLER__ - -#endif _STATUS_H_ diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/version.h linux/drivers/FPU-emu/version.h --- v1.1.76/linux/drivers/FPU-emu/version.h Mon Aug 1 08:19:16 1994 +++ linux/drivers/FPU-emu/version.h Thu Jan 1 02:00:00 1970 @@ -1,12 +0,0 @@ -/*---------------------------------------------------------------------------+ - | version.h | - | | - | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -#define FPU_VERSION "wm-FPU-emu version 1.20" diff -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/wm_shrx.S linux/drivers/FPU-emu/wm_shrx.S --- v1.1.76/linux/drivers/FPU-emu/wm_shrx.S Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/wm_shrx.S Thu Jan 1 02:00:00 1970 @@ -1,208 +0,0 @@ - .file "wm_shrx.S" -/*---------------------------------------------------------------------------+ - | wm_shrx.S | - | | - | 64 bit right shift functions | - | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | Call from C as: | - | unsigned shrx(void *arg1, unsigned arg2) | - | and | - | unsigned shrxs(void *arg1, unsigned arg2) | - | | - +---------------------------------------------------------------------------*/ - -#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 -u --recursive --new-file v1.1.76/linux/drivers/FPU-emu/wm_sqrt.S linux/drivers/FPU-emu/wm_sqrt.S --- v1.1.76/linux/drivers/FPU-emu/wm_sqrt.S Thu Jun 2 10:28:28 1994 +++ linux/drivers/FPU-emu/wm_sqrt.S Thu Jan 1 02:00:00 1970 @@ -1,474 +0,0 @@ - .file "wm_sqrt.S" -/*---------------------------------------------------------------------------+ - | wm_sqrt.S | - | | - | Fixed point arithmetic square root evaluation. | - | | - | Copyright (C) 1992,1993 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | - | | - | Call from C as: | - | void wm_sqrt(FPU_REG *n, unsigned int control_word) | - | | - +---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------+ - | 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" - - -#ifndef NON_REENTRANT_FPU -/* Local storage on the stack: */ -#define FPU_accum_3 -4(%ebp) /* ms word */ -#define FPU_accum_2 -8(%ebp) -#define FPU_accum_1 -12(%ebp) -#define FPU_accum_0 -16(%ebp) - -/* - * 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 - */ -#define FPU_fsqrt_arg_2 -20(%ebp) /* ms word */ -#define FPU_fsqrt_arg_1 -24(%ebp) -#define FPU_fsqrt_arg_0 -28(%ebp) /* ls word, at most the ms bit is set */ - -#else -/* Local storage in a static area: */ -.data - .align 4,0 -FPU_accum_3: - .long 0 /* ms word */ -FPU_accum_2: - .long 0 -FPU_accum_1: - .long 0 -FPU_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 - */ -FPU_fsqrt_arg_2: - .long 0 /* ms word */ -FPU_fsqrt_arg_1: - .long 0 -FPU_fsqrt_arg_0: - .long 0 /* ls word, at most the ms bit is set */ -#endif NON_REENTRANT_FPU - - -.text - .align 2,144 - -.globl _wm_sqrt -_wm_sqrt: - pushl %ebp - movl %esp,%ebp -#ifndef NON_REENTRANT_FPU - subl $28,%esp -#endif NON_REENTRANT_FPU - 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,FPU_fsqrt_arg_2 /* ms word of n */ - movl %ecx,FPU_fsqrt_arg_1 - movl %edx,FPU_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 FPU_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 FPU_fsqrt_arg_1,%ecx - subl %ecx,%eax - movl FPU_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,FPU_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,FPU_accum_1 - - movl %esi,%eax - mull %esi - movl %edx,FPU_accum_3 - movl %eax,FPU_accum_2 - - movl %edi,%eax - mull %esi - addl %eax,FPU_accum_1 - adcl %edx,FPU_accum_2 - adcl $0,FPU_accum_3 - -/* movl %esi,%eax */ -/* mull %edi */ - addl %eax,FPU_accum_1 - adcl %edx,FPU_accum_2 - adcl $0,FPU_accum_3 - -/* guess^2 now in FPU_accum_3:FPU_accum_2:FPU_accum_1 */ - - movl FPU_fsqrt_arg_0,%eax /* get normalized n */ - subl %eax,FPU_accum_1 - movl FPU_fsqrt_arg_1,%eax - sbbl %eax,FPU_accum_2 - movl FPU_fsqrt_arg_2,%eax /* ms word of normalized n */ - sbbl %eax,FPU_accum_3 - jnc sqrt_stage_3_positive - -/* Subtraction gives a negative result, - negate the result before division */ - notl FPU_accum_1 - notl FPU_accum_2 - notl FPU_accum_3 - addl $1,FPU_accum_1 - adcl $0,FPU_accum_2 - -#ifdef PARANOID - adcl $0,FPU_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 FPU_accum_2,%edx - movl FPU_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 FPU_accum_2,%edx - movl FPU_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_reg_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 diff -u --recursive --new-file v1.1.76/linux/drivers/Makefile linux/drivers/Makefile --- v1.1.76/linux/drivers/Makefile Wed Dec 1 14:44:16 1993 +++ linux/drivers/Makefile Sat Jan 7 12:57:43 1995 @@ -16,11 +16,7 @@ .c.o: $(CC) $(CFLAGS) -c $< -SUBDIRS = block char net - -ifdef CONFIG_MATH_EMULATION -SUBDIRS := $(SUBDIRS) FPU-emu -endif +SUBDIRS = block char net #streams ifdef CONFIG_SCSI SUBDIRS := $(SUBDIRS) scsi @@ -34,6 +30,9 @@ driversubdirs: dummy set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done + +modules: dummy + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i modules; done dep: set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done diff -u --recursive --new-file v1.1.76/linux/drivers/block/Makefile linux/drivers/block/Makefile --- v1.1.76/linux/drivers/block/Makefile Sun Jan 1 16:28:19 1995 +++ linux/drivers/block/Makefile Wed Jan 4 21:16:05 1995 @@ -79,6 +79,7 @@ dep: $(CPP) -M $(SRCS) > .depend +modules: dummy: # diff -u --recursive --new-file v1.1.76/linux/drivers/block/README.ide linux/drivers/block/README.ide --- v1.1.76/linux/drivers/block/README.ide Sun Jan 1 19:49:19 1995 +++ linux/drivers/block/README.ide Sat Jan 7 12:58:21 1995 @@ -9,7 +9,7 @@ - support for up to two IDE interfaces on one or two IRQs - support for any mix of up to four disk and/or cdrom drives - - support for reading IDE ATAPI cdrom drives (NEC, MITSUMI, VERSA) + - support for reading IDE ATAPI cdrom drives (NEC, MITSUMI, VERSA, SONY) - support for audio functions on FX400,NEC-260 cdrom drives (others?) - auto-detection of interfaces, drives, IRQs, and disk geometries - support for BIOSs which report "more than 16 heads" on disk drives @@ -47,15 +47,25 @@ where hdx can be any of {hda,hdb,hdc,hdd}, or simply hd, for the "next" drive in sequence. Only the first three parameters are required (cyls,heads,sects), -and wpcom is ignored for IDE drives. +and wpcom is ignored for IDE drives. For example: - Example: hdc=1050,32,64 hdd=cdrom + hdc=1050,32,64 hdd=cdrom If an irq number is given, it will apply to both drives on the same interface, either {hda,hdb} or {hdc,hdd}. The results of successful auto-probing may override the physical geometry/irq specified, though the "original" geometry is retained as the "logical" geometry for partitioning purposes (fdisk). +If the auto-probing during boot time confuses a drive (ie. the drive works +with hd.c but not with ide.c), then an command line option may be specified +for each drive for which you'd like the drive to skip the hardware +probe/identification sequence. For example: + + hdb=noprobe +or + hdc=768,16,32 + hdc=noprobe + Courtesy of Scott Snyder, the driver now supports ATAPI cdrom drives such as the NEC-260 and the new MITSUMI triple/quad speed drives. Such drives will be identified at boot time, as hda,hdb,hdc or hdd, @@ -95,7 +105,7 @@ addresses, giving a limit of 1024cyls for programs which use it. 2. The physical geometry fields of the disk partition table only - allows 10-bits for cylinder addresses, giving a similar limit of 1024 + allow 10-bits for cylinder addresses, giving a similar limit of 1024 cyls for operating systems that do not use the "sector count" fields instead of the physical Cyl/Head/Sect (CHS) geometry fields. @@ -106,7 +116,7 @@ a) Most folks use LILO to load linux. LILO uses the INT13 interface to the BIOS to load the kernel at boot time. Therefore, LILO can only load linux if the files it needs (usually just the kernel images) are - located below the magic 1024 cylinder "boundary". + located below the magic 1024 cylinder "boundary" (more on this later). b) Many folks also like to have bootable DOS partitions on their drive(s). DOS also uses the INT13 interface to the BIOS, not only @@ -127,17 +137,17 @@ a translated logical geometry into the BIOS/CMOS setup for the drive. Thus, if the drive has a geometry of 2100/16/63 (CHS), then the BIOS could present a "logical" geometry of 525/64/63 by "shifting" two bits from the -cylinder number into the head number field for purposes of the partition table, CMOS setup, and INT13 interfaces. Linux kernels 1.1.39 and higher detect and +cylinder number into the head number field for purposes of the partition table, +CMOS setup, and INT13 interfaces. Linux kernels 1.1.39 and higher detect and "handle" this translation automatically, making this a rather painless solution for the 1024 cyls problem. If for some reason Linux gets confused (unlikely), then use the kernel command line parameters to pass the *logical* geometry, as in: hda=525,64,63 If the BIOS does not support this form of drive translation, then several -options remain, listed below in order of increasing popularity: +options remain, listed below in inverse order of popularity: - - rewrite LILO to bypass the BIOS and talk directly to the drive - - boot from a floppy disk instead of the hard drive (takes 10 seconds) + - boot from a floppy disk instead of the hard drive (takes 10 seconds). - use a partition below the 1024 cyl boundary to hold the linux boot files (kernel images and /boot directory), and place the rest of linux anywhere else on the drive. These files can reside in a DOS @@ -145,9 +155,9 @@ If you cannot use drive translation, *and* your BIOS also restricts you to entering no more than 1024 cylinders in the geometry field in the CMOS setup, -then you'll have to set it to 1024. As of v3.5 of this driver, Linux will -automatically determine the *real* number of cylinders for fdisk to use, -allowing easy access to the full disk capacity. +then just set it to 1024. As of v3.5 of this driver, Linux automatically +determines the *real* number of cylinders for fdisk to use, allowing easy +access to the full disk capacity without having to fiddle around. Regardless of what you do, all DOS partitions *must* be contained entirely within the first 1024 logical cylinders. For a 1Gig WD disk drive, here's @@ -158,16 +168,29 @@ /dev/hda2 from cyl 993 to 1023 swap /dev/hda3 from cyl 1024 to 2100 linux -After installing slackware (or whatever), boot from floppy and mount the dos -partition under /dos, and then move the /boot directory to /dos/boot, -and move the /vmlinuz kernel file into /dos/boot. Then create a symlink -to put it back where the filesystem standard wants it: ln -s /dos/boot /boot -Finally, edit /etc/lilo.conf and change the reference to /vmlinuz to point -at /boot/vmlinuz and then re-run lilo. Have Fun. :) +To ensure that LILO can boot linux, the boot files (kernel and /boot/*) +must reside within the first 1024 cylinders of the drive. If your linux +root partition is *not* completely within the first 1024 cyls (quite common), +then you can use LILO to boot linux from files on your DOS partition +by doing the following after installing slackware (or whatever): + + 0. Boot from the "boot floppy" created during the installation + 1. Mount your DOS partition as /dos (and stick it in /etc/fstab) + 2. Move your kernel (/vmlinuz) to /dos/vmlinuz with: mv /vmlinuz /dos + 3. Edit /etc/lilo.conf to change /vmlinuz to /dos/vmlinuz + 4. Move /boot to /dos/boot with: cp -a /boot /dos ; rm -r /boot + 5. Create a symlink for LILO to use with: ln -s /dos/boot /boot + 6. Re-run LILO with: lilo If you "don't do DOS", then partition as you please, but remember to create a small partition to hold the /boot directory (and vmlinuz) as described above -such that it all lies within the first 1024 cylinders. +such that they stay within the first 1024 cylinders. + +Note that when creating partitions that span beyond cylinder 1024, +Linux fdisk will complain about "Partition X has different physical/logical +endings" and emit messages such as "This is larger than 1024, and may cause +problems with some software". Ignore them for linux partitions. The "some +software" refers to DOS, the BIOS, and LILO, as described previously. Western Digital now ships a "DiskManager 6.03" diskette with all of their big hard drives. Burn it! That idiotic piece of garbage isn't even universally diff -u --recursive --new-file v1.1.76/linux/drivers/block/README.sbpcd linux/drivers/block/README.sbpcd --- v1.1.76/linux/drivers/block/README.sbpcd Mon Dec 19 17:26:24 1994 +++ linux/drivers/block/README.sbpcd Wed Jan 4 08:12:46 1995 @@ -1,4 +1,4 @@ -This README belongs to release 2.9 or newer of the SoundBlaster Pro +This README belongs to release 3.0 or newer of the SoundBlaster Pro (Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and soon TEAC, too) CD-ROM driver for Linux. @@ -17,11 +17,20 @@ a soundcard). The quad-speed TEAC CD-55A drive uses the same interface types, but has a -totally different command and flow control scheme. It is not supported yet, -but I plan to add it. +totally different command and flow control scheme. Support is under +construction. -CreativeLabs has a new drive "CD-200". This drive is not supported yet, but -I plan to add it. +CreativeLabs has a new drive "CD-200". Support is under construction. +Detection should already work. + +Regarding CD200 and CD-55A support: + Please, don't mail me about it if you are not a competent BETA tester + (if you are: mail!; I do not have such drives). + Please, don't drop simple questions about the new drives in the + newsgroups. Full support needs more or less time. +If you are able to set the appropriate DBG-xxx switches, you can mail me +the "SBPCD:..." messages, regarding the new drives. But I mostly will +not answer (just use) it. This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives. For Aztech CDA-268 drives (and for some Wearnes, Okano and Orchid drives), diff -u --recursive --new-file v1.1.76/linux/drivers/block/blk.h linux/drivers/block/blk.h --- v1.1.76/linux/drivers/block/blk.h Sun Jan 1 16:28:19 1995 +++ linux/drivers/block/blk.h Thu Jan 5 13:47:08 1995 @@ -3,6 +3,7 @@ #include #include +#include /* * NR_REQUEST is the number of entries in the request-queue. diff -u --recursive --new-file v1.1.76/linux/drivers/block/cdu31a.c linux/drivers/block/cdu31a.c --- v1.1.76/linux/drivers/block/cdu31a.c Tue Nov 29 16:47:45 1994 +++ linux/drivers/block/cdu31a.c Sat Jan 7 12:57:55 1995 @@ -2863,7 +2863,7 @@ if (drive_found) { - snarf_region(sony_cd_base_io, 4); + register_iomem(sony_cd_base_io, 4,"cdu31a"); if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops)) { diff -u --recursive --new-file v1.1.76/linux/drivers/block/floppy.c linux/drivers/block/floppy.c --- v1.1.76/linux/drivers/block/floppy.c Wed Dec 7 07:21:24 1994 +++ linux/drivers/block/floppy.c Wed Jan 4 21:16:05 1995 @@ -2096,7 +2096,7 @@ floppy_track_buffer + (max_buffer_sectors << 10) || dma_buffer < floppy_track_buffer ){ DPRINT1("buffer overrun in copy buffer %d\n", - (floppy_track_buffer - dma_buffer) >>9); + (int) ((floppy_track_buffer - dma_buffer) >>9)); printk("sector_t=%d buffer_min=%d\n", sector_t, buffer_min); printk("current_count_sectors=%ld\n", @@ -2107,7 +2107,7 @@ printk("write\n"); break; } - if ( ((int)buffer) % 512 ) + if ( ((unsigned long)buffer) % 512 ) DPRINT1("%p buffer not aligned\n", buffer); #endif if ( CT(COMMAND) == FD_READ ) @@ -2328,7 +2328,7 @@ raw_cmd.length, current_count_sectors); if ( current_addr != CURRENT->buffer ) printk("addr=%d, length=%ld\n", - (current_addr - floppy_track_buffer ) >> 9, + (int) ((current_addr - floppy_track_buffer ) >> 9), current_count_sectors); printk("st=%d ast=%d mse=%d msi=%d\n", sector_t, aligned_sector_t, max_sector, max_size); diff -u --recursive --new-file v1.1.76/linux/drivers/block/genhd.c linux/drivers/block/genhd.c --- v1.1.76/linux/drivers/block/genhd.c Sun Jan 1 16:28:19 1995 +++ linux/drivers/block/genhd.c Thu Jan 5 13:55:40 1995 @@ -10,7 +10,6 @@ * in the early extended-partition checks and added DM partitions */ -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/block/hd.c linux/drivers/block/hd.c --- v1.1.76/linux/drivers/block/hd.c Sun Jan 1 16:28:19 1995 +++ linux/drivers/block/hd.c Thu Jan 5 13:55:40 1995 @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c --- v1.1.76/linux/drivers/block/ide-cd.c Sun Jan 1 16:28:19 1995 +++ linux/drivers/block/ide-cd.c Wed Jan 4 19:11:02 1995 @@ -870,7 +870,7 @@ } -/* modeflag: 0 = current, 1 = changable mask, 2 = default, 3 = saved */ +/* modeflag: 0 = current, 1 = changeable mask, 2 = default, 3 = saved */ static int cdrom_mode_sense (ide_dev_t *dev, int pageno, int modeflag, char *buf, int buflen) diff -u --recursive --new-file v1.1.76/linux/drivers/block/ide.c linux/drivers/block/ide.c --- v1.1.76/linux/drivers/block/ide.c Sun Jan 1 19:49:19 1995 +++ linux/drivers/block/ide.c Sat Jan 7 13:07:48 1995 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide.c Version 3.5 December 30, 1994 + * linux/drivers/block/ide.c Version 3.6 January 5, 1994 * * Copyright (C) 1994 Linus Torvalds & authors (see below) */ @@ -28,7 +28,7 @@ * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads", * | and general streamlining by Mark Lord (mlord@bnr.ca). * - * October, 1994 -- Complete line-by-line overhaul for linux 1.3.x, by: + * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by: * * Mark Lord (mlord@bnr.ca) (IDE Perf.Pkg) * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2") @@ -45,7 +45,7 @@ * Version 1.3 BETA dual i/f on shared irq tested & working! * Version 1.4 BETA added auto probing for irq(s) * Version 1.5 BETA added ALPHA (untested) support for IDE cd-roms, - * fixed hd.c coexistance bug, other minor stuff + * fixed hd.c coexistence bug, other minor stuff * Version 1.6 BETA fix link error when cd-rom not configured * Version 2.0 BETA lots of minor fixes; remove annoying messages; ... * Version 2.2 BETA fixed reset_drives; major overhaul of autoprobing @@ -82,9 +82,17 @@ * Version 3.4 BETA removed "444" debug message * (sent to Linus) * Version 3.5 correct the bios_cyl field if it's too small - * (to help fdisk with brain-dead BIOSs) + * (linux 1.1.76!) (to help fdisk with brain-dead BIOSs) + * Version 3.6 cosmetic corrections to comments and stuff + * reorganise probing code to make it understandable + * added halfway retry to probing for drive identification + * added "hdx=noprobe" command line option + * allow setting multmode even when identification fails * - * In progress: special 32-bit controller-type detection & support + * To do: + * - special 32-bit controller-type detection & support + * - figure out why two WD drives on one i/f sometimes don't identify + * - figure out how to support oddball "intelligent" caching cards */ #include @@ -104,7 +112,6 @@ #include #include #include -#include #include #include #include @@ -112,8 +119,9 @@ /***************************************************************************** * IDE driver configuration options (play with these as desired): */ +#define REALLY_SLOW_IO /* most systems can safely undef this */ +#include -#undef REALLY_SLOW_IO /* define if ide ports are very slow */ #undef REALLY_FAST_IO /* define if ide ports are perfect */ #undef INITIAL_MULT_COUNT /* define to override status quo */ @@ -123,7 +131,7 @@ #ifndef DISK_RECOVERY_TIME /* min. delay between IO for hardware */ #define DISK_RECOVERY_TIME 0 /* that needs it. */ #endif -#ifndef OK_TO_RESET_CONTROLLER /* needed for good error recovery */ +#ifndef OK_TO_RESET_CONTROLLER /* 1 needed for good error recovery */ #define OK_TO_RESET_CONTROLLER 1 /* 0 for use with AH2372A/B interface */ #endif #ifndef SUPPORT_TWO_INTERFACES /* 1 to support one/two interfaces */ @@ -141,6 +149,10 @@ #define PROBE_FOR_IRQS 1 /* 0 to force use of defaults below */ #define DEFAULT_IDE0_IRQ 14 /* in case irq-probe fails */ #define DEFAULT_IDE1_IRQ 15 /* in case irq-probe fails */ + +/* IDE_DRIVE_CMD is used to implement many features of the hdparm utility */ +#define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/ + /* * "No user-serviceable parts" beyond this point :) ****************************************************************************** @@ -150,12 +162,10 @@ * Need to change these elsewhere in the kernel (someday) */ #ifndef IDE0_TIMER -#define IDE0_TIMER HD_TIMER +#define IDE0_TIMER HD_TIMER #define IDE1_TIMER HD_TIMER2 #endif -#define IDE_DRIVE_CMD 99 /* some local magic */ - /* * Ensure that various configuration flags have compatible settings */ @@ -171,9 +181,9 @@ #define DEV_HWIF (dev->hwif) #else #undef OPTIMIZE_IRQS -#define OPTIMIZE_IRQS 0 +#define OPTIMIZE_IRQS 0 #undef SUPPORT_SHARING_IRQ -#define SUPPORT_SHARING_IRQ 0 +#define SUPPORT_SHARING_IRQ 0 #ifdef CONFIG_BLK_DEV_HD #define HWIF 1 #else @@ -224,11 +234,11 @@ /* * Timeouts for various operations: */ -#define WAIT_DRQ 3 /* 30msec - spec allows up to 20ms */ +#define WAIT_DRQ 3 /* 30msec - spec allows up to 20ms */ #define WAIT_READY 3 /* 30msec - should be instantaneous */ -#define WAIT_PIDENTIFY 50 /* 500msec - should be less than 3ms (?) */ -#define WAIT_WORSTCASE 3000 /* 30sec - worst case when spinning up */ -#define WAIT_CMD 1000 /* 10sec - maximum wait for an IRQ to happen */ +#define WAIT_PIDENTIFY 100 /* 1sec - should be less than 3ms (?) */ +#define WAIT_WORSTCASE 3000 /* 30sec - worst case when spinning up */ +#define WAIT_CMD 1000 /* 10sec - maximum wait for an IRQ to happen */ /* * Now for the data we need to maintain per-device: ide_dev_t @@ -265,9 +275,10 @@ byte unmask; /* pretty quick access to this also */ dev_type type : 1; /* disk or cdrom (or tape, floppy..) */ unsigned present : 1; /* drive is physically present */ + unsigned dont_probe : 1; /* from: hdx=noprobe */ unsigned keep_settings : 1; /* restore settings after drive reset */ unsigned busy : 1; /* mutex for ide_open, revalidate_.. */ - unsigned reserved0 : 4; /* unused */ + unsigned reserved0 : 3; /* unused */ special_t special; /* special action flags */ select_t select; /* basic drive/head select reg value */ byte mult_count, reserved1, reserved2; @@ -339,7 +350,7 @@ /* * One final include file, which references some of the data/defns from above */ -#define IDE_DRIVER /* "parameter" for blk.h */ +#define IDE_DRIVER /* "parameter" for blk.h */ #include "blk.h" /* @@ -376,7 +387,7 @@ * This is a macro rather than an inline to permit better gcc code. * Caller MUST do sti() before invoking WAIT_STAT() (for jiffies to work). * - * This route should get fixed to not hog the cpu during extra long waits.. + * This routine should get fixed to not hog the cpu during extra long waits.. * That could be done by busy-waiting for the first jiffy or two, and then * setting a timer to wake up at half second intervals thereafter, * until WAIT_WORSTCASE is achieved, before timing out. @@ -580,10 +591,12 @@ err =