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 = dump_status(DEV_HWIF, msg, stat); if ((rq = ide_cur_rq[DEV_HWIF]) == NULL || dev == NULL) return; +#ifdef IDE_DRIVE_CMD if (rq->cmd == IDE_DRIVE_CMD) { /* never retry an explicit DRIVE_CMD */ end_drive_cmd(dev, stat, err); return; } +#endif /* IDE_DRIVE_CMD */ if (dev->type == disk && (stat & ERR_STAT)) { /* err has different meaning on cdrom */ if (err & BBD_ERR) /* retries won't help this! */ @@ -874,8 +887,8 @@ if (dev->type == disk) ide_cmd(dev,WIN_RESTORE,dev->sect,&recal_intr); } else if (s->b.set_multmode) { - if (dev->id && dev->id->max_multsect && dev->type == disk) { - if (dev->mult_req > dev->id->max_multsect) + if (dev->type == disk) { + if (dev->id && dev->mult_req > dev->id->max_multsect) dev->mult_req = dev->id->max_multsect; if (dev->mult_req != dev->mult_count) ide_cmd(dev,WIN_SETMULT,dev->mult_req,&set_multmode_intr); @@ -1308,6 +1321,7 @@ return 0; } +#ifdef IDE_DRIVE_CMD /* * This function issues a specific IDE drive command onto the * tail of the request queue, and waits for it to be completed. @@ -1352,6 +1366,7 @@ restore_flags(flags); return rq.errors ? -EIO : 0; /* return -EIO if errors */ } +#endif /* IDE_DRIVE_CMD */ static int write_fs_long (unsigned long useraddr, long value) { @@ -1455,7 +1470,7 @@ if (!suser()) return -EACCES; if (MINOR(inode->i_rdev) & PARTN_MASK) return -EINVAL; - if ((dev->id == NULL) || (arg > dev->id->max_multsect)) + if ((dev->id != NULL) && (arg > dev->id->max_multsect)) return -EINVAL; save_flags(flags); cli(); @@ -1466,8 +1481,12 @@ dev->mult_req = arg; dev->special.b.set_multmode = 1; restore_flags(flags); +#ifdef IDE_DRIVE_CMD do_drive_cmd (inode->i_rdev, NULL); return (dev->mult_count == arg) ? 0 : -EIO; +#else + return 0; +#endif /* IDE_DRIVE_CMD */ #ifdef IDE_DRIVE_CMD case HDIO_DRIVE_CMD: @@ -1545,15 +1564,15 @@ { unsigned long lba_sects = id->lba_capacity; unsigned long chs_sects = id->cyls * id->heads * id->sectors; - unsigned long _15_percent = chs_sects / 10; + unsigned long _10_percent = chs_sects / 10; - /* perform a rough sanity check on lba_sects: within 15% is "okay" */ - if ((lba_sects - chs_sects) < _15_percent) + /* perform a rough sanity check on lba_sects: within 10% is "okay" */ + if ((lba_sects - chs_sects) < _10_percent) return lba_sects; /* some drives have the word order reversed */ lba_sects = (lba_sects << 16) | (lba_sects >> 16); - if ((lba_sects - chs_sects) < _15_percent) + if ((lba_sects - chs_sects) < _10_percent) return (id->lba_capacity = lba_sects); /* play it safe and assume lba capacity is the same as chs capacity */ @@ -1695,55 +1714,16 @@ while (timer > jiffies); } -/* - * This routine has the difficult job of finding a drive if it exists, - * without getting hung up if it doesn't exist, and without leaving any IRQs - * dangling to haunt us later. The last point actually occured in v2.3, and - * is the reason for the slightly complex exit sequence. If a drive is "known" - * to exist (from CMOS or kernel parameters), but does not respond right away, - * the probe will "hang in there" for the maximum wait time (about 30 seconds). - * Otherwise, it will exit much more quickly. - * - * Returns 1 if device present but not identified. Returns 0 otherwise. - */ -static int do_probe (ide_dev_t *dev, byte probe_cmd) + +static int try_to_identify (ide_dev_t *dev, byte cmd) { - unsigned int rc = 0; + int rc; unsigned long timer, timeout; #if PROBE_FOR_IRQS - unsigned int irqs = 0; - static byte okstat, irq_probed[2] = {0,0}; + int irqs = 0; + static byte irq_probed[2] = {0,0}; #endif /* PROBE_FOR_IRQS */ -#ifdef CONFIG_BLK_DEV_IDECD - if (dev->present) { - if ((dev->type == disk) ^ (probe_cmd == WIN_IDENTIFY)) - return 1; /* pointless to probe */ - } -#endif /* CONFIG_BLK_DEV_IDECD */ -#if DEBUG - printk("probing for %s: present=%d, type=%s, probetype=%s\n", - dev->name, dev->present, dev->type ? "cdrom":"disk", - (probe_cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI"); -#endif - OUT_BYTE(dev->select.all,HD_CURRENT); /* select target drive */ - delay_10ms(); /* wait for BUSY_STAT */ - if (IN_BYTE(HD_CURRENT,DEV_HWIF) != dev->select.all && !dev->present) - return 0; /* no i/f present: avoid killing ethernet cards */ - timeout = WAIT_WORSTCASE; - okstat = READY_STAT; -#ifdef CONFIG_BLK_DEV_IDECD - if (probe_cmd == WIN_PIDENTIFY) { - timeout = WAIT_PIDENTIFY; - okstat = 0; - } -#endif /* CONFIG_BLK_DEV_IDECD */ - if (!OK_STAT(GET_STAT(DEV_HWIF),okstat,BUSY_STAT) && !dev->present) { -#ifdef CONFIG_BLK_DEV_IDECD - if (probe_cmd == WIN_IDENTIFY) /* get rid of this line? */ -#endif /* CONFIG_BLK_DEV_IDECD */ - goto done_probe; /* no drive present */ - } OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */ #if PROBE_FOR_IRQS if (!irq_probed[DEV_HWIF]) { /* already probed for IRQ? */ @@ -1751,14 +1731,17 @@ OUT_BYTE(dev->ctl,HD_CMD); /* enable device irq */ } #endif /* PROBE_FOR_IRQS */ - OUT_BYTE(probe_cmd,HD_COMMAND); /* ask drive for ID */ + delay_10ms(); /* take a deep breath */ + OUT_BYTE(cmd,HD_COMMAND); /* ask drive for ID */ delay_10ms(); /* wait for BUSY_STAT */ - for (timer = jiffies + timeout; timer > jiffies;) { + timeout = (cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY; + for (timer = jiffies + (timeout / 2); timer > jiffies;) { if ((IN_BYTE(HD_ALTSTATUS,DEV_HWIF) & BUSY_STAT) == 0) { delay_10ms(); /* wait for IRQ & DATA_READY */ if (OK_STAT(GET_STAT(DEV_HWIF),DATA_READY,BAD_RW_STAT)){ cli(); /* some sys need this */ do_identify(dev); /* drive returned ID */ + rc = 0; } else rc = 1; /* drive refused ID */ #if PROBE_FOR_IRQS @@ -1771,18 +1754,63 @@ printk("%s: IRQ probe failed (%d)\n", dev->name, irqs); } #endif /* PROBE_FOR_IRQS */ - goto done_probe; + goto done_try; } } - /* dev->present = 0; */ /* it ain't there */ + rc = 2; /* it ain't responding */ #if PROBE_FOR_IRQS if (!irq_probed[DEV_HWIF]) - (void) probe_irq_off(irqs); /* stop probing */ + (void) probe_irq_off(irqs); /* end probing */ #endif /* PROBE_FOR_IRQS */ -done_probe: +done_try: OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */ - delay_10ms(); - (void) GET_STAT(DEV_HWIF); /* ensure drive irq is clear */ + return rc; +} + +/* + * This routine has the difficult job of finding a drive if it exists, + * without getting hung up if it doesn't exist, and without leaving any IRQs + * dangling to haunt us later. The last point actually occured in v2.3, and + * is the reason for the slightly complex exit sequence. If a drive is "known" + * to exist (from CMOS or kernel parameters), but does not respond right away, + * the probe will "hang in there" for the maximum wait time (about 30 seconds). + * Otherwise, it will exit much more quickly. + * + * Returns 1 if device present but not identified. Returns 0 otherwise. + */ +static int do_probe (ide_dev_t *dev, byte cmd) +{ + int rc; + +#ifdef CONFIG_BLK_DEV_IDECD + if (dev->present) { /* avoid waiting for inappropriate probes */ + if ((dev->type == disk) ^ (cmd == WIN_IDENTIFY)) + return 1; + } +#endif /* CONFIG_BLK_DEV_IDECD */ +#if DEBUG + printk("probing for %s: present=%d, type=%s, probetype=%s\n", + dev->name, dev->present, dev->type ? "cdrom":"disk", + (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI"); +#endif + OUT_BYTE(dev->select.all,HD_CURRENT); /* select target drive */ + delay_10ms(); /* wait for BUSY_STAT */ + if (IN_BYTE(HD_CURRENT,DEV_HWIF) != dev->select.all && !dev->present) + return 0; /* no i/f present: avoid killing ethernet cards */ + + if (OK_STAT(GET_STAT(DEV_HWIF),READY_STAT,BUSY_STAT) + || dev->present || cmd == WIN_PIDENTIFY) + { + if ((rc = try_to_identify(dev, cmd))) /* send cmd and wait */ + rc = try_to_identify(dev, cmd); /* try again */ + if (rc) + printk("%s: identification %s\n", dev->name, + (rc == 1) ? "refused" : "timed-out"); + delay_10ms(); + (void) GET_STAT(DEV_HWIF); /* ensure drive irq is clear */ + } else { + rc = 2; /* drive not present */ + } if (dev->select.b.drive == 1) { OUT_BYTE(0xa0,HD_CURRENT); /* exit with drive0 selected */ delay_10ms(); @@ -1793,29 +1821,32 @@ return rc; } -static byte probe_drive (ide_dev_t *dev) +static byte probe_for_drive (ide_dev_t *dev) { - if (do_probe(dev, WIN_IDENTIFY)) { + if (dev->dont_probe) /* skip probing? */ + return dev->present; + if (do_probe(dev, WIN_IDENTIFY) == 1) { /* 1 = drive aborted the cmd */ #ifdef CONFIG_BLK_DEV_IDECD - if (do_probe(dev, WIN_PIDENTIFY)) + (void) do_probe(dev, WIN_PIDENTIFY); #endif /* CONFIG_BLK_DEV_IDECD */ - if (dev->present) { - if (dev->type == disk) { - printk ("%s: non-IDE device, CHS=%d/%d/%d\n", - dev->name, dev->cyl, dev->head, dev->sect); - } + } + if (!dev->present) + return 0; /* drive not present */ + if (dev->id == NULL) { /* identification failed? */ + if (dev->type == disk) { + printk ("%s: non-IDE device, CHS=%d/%d/%d\n", + dev->name, dev->cyl, dev->head, dev->sect); + } #ifdef CONFIG_BLK_DEV_IDECD - else if (dev->type == cdrom) { - printk("%s: ATAPI cdrom (?)\n", dev->name); - } + else if (dev->type == cdrom) { + printk("%s: ATAPI cdrom (?)\n", dev->name); + } #endif /* CONFIG_BLK_DEV_IDECD */ - else { - dev->present = 0; /* nuke it */ - } + else { + dev->present = 0; /* nuke it */ + return 0; /* drive not present */ } } - if (!dev->present) - return 0; /* drive not found */ #ifdef CONFIG_BLK_DEV_IDECD if (dev->type == cdrom) cdrom_setup(dev); @@ -1827,10 +1858,10 @@ dev->present = 0; } } - return 1; /* drive found */ + return 1; /* drive present */ } -static void probe_hw_for_drives (byte hwif) +static void probe_for_drives (byte hwif) { ide_dev_t *devs = &ide_dev[HWIF][0]; /* for convenience */ @@ -1848,14 +1879,14 @@ sti(); /* needed for jiffies and irq probing */ /* second drive can only exist if first drive was present */ - if (probe_drive(&devs[0]) || devs[1].present) - (void) probe_drive(&devs[1]); + if (probe_for_drive(&devs[0]) || devs[1].present) + (void) probe_for_drive(&devs[1]); #if PROBE_FOR_IRQS (void) probe_irq_off(probe_irq_on()); /* clear dangling irqs */ #endif /* PROBE_FOR_IRQS */ if (devs[0].present || devs[1].present) { - snarf_region(IDE_PORT(HD_DATA,HWIF),8); - snarf_region(IDE_PORT(HD_CMD,HWIF),1); + register_iomem(IDE_PORT(HD_DATA,HWIF),8,"ide"); + register_iomem(IDE_PORT(HD_CMD,HWIF),1,"ide"); } restore_flags(flags); } @@ -1891,16 +1922,21 @@ dev = &ide_dev[hwif][drive]; if (dev->present) printk("(redefined) "); -#ifdef CONFIG_BLK_DEV_IDECD if (ints[0] == 0) { + if (!strcmp(str,"noprobe")) { + printk("noprobe\n"); + dev->dont_probe = 1; /* don't probe for this drive */ + return; + } +#ifdef CONFIG_BLK_DEV_IDECD if (!strcmp(str,"cdrom")) { printk("cdrom\n"); dev->present = 1; /* force autoprobe to find it */ dev->type = cdrom; return; } - } #endif /* CONFIG_BLK_DEV_IDECD */ + } if (ints[0] < 3 || ints[0] > 5) { printk("bad parms, expected: cyls,heads,sects[,wpcom[,irq]]\n"); } else { @@ -2106,7 +2142,7 @@ probe_cmos_for_drives (); #endif /* CONFIG_BLJ_DEV_HD */ probe_mem_start = (mem_start + 3uL) & ~3uL; - probe_hw_for_drives (hwif); + probe_for_drives (hwif); mem_start = probe_mem_start; } } diff -u --recursive --new-file v1.1.76/linux/drivers/block/mcd.c linux/drivers/block/mcd.c --- v1.1.76/linux/drivers/block/mcd.c Tue Nov 1 19:50:44 1994 +++ linux/drivers/block/mcd.c Sat Jan 7 12:57:56 1995 @@ -1168,7 +1168,7 @@ printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq); return mem_start; } - snarf_region(mcd_port, 4); + register_iomem(mcd_port, 4,"mcd"); outb(MCMD_CONFIG_DRIVE, MCDPORT(0)); outb(0x02,MCDPORT(0)); diff -u --recursive --new-file v1.1.76/linux/drivers/block/ramdisk.c linux/drivers/block/ramdisk.c --- v1.1.76/linux/drivers/block/ramdisk.c Thu Dec 29 19:58:41 1994 +++ linux/drivers/block/ramdisk.c Thu Jan 5 13:55:40 1995 @@ -8,7 +8,6 @@ */ -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/block/sbpcd.c linux/drivers/block/sbpcd.c --- v1.1.76/linux/drivers/block/sbpcd.c Tue Dec 27 09:54:43 1994 +++ linux/drivers/block/sbpcd.c Sat Jan 7 12:57:56 1995 @@ -7,9 +7,9 @@ * Also for the Longshine LCS-7260 drive. * Also for the IBM "External ISA CD-Rom" drive. * Not for the TEAC CD-55A drive (yet). - * Not for the CreativeLabs CDD-200 drive (yet). + * Not for the CreativeLabs CD200 drive (yet). * - * NOTE: This is release 2.9. + * NOTE: This is release 3.0. * It works with my SbPro & drive CR-521 V2.11 from 2/92 * and with the new CR-562-B V0.75 on a "naked" Panasonic * CI-101P interface. And vice versa. @@ -159,6 +159,16 @@ * only for the special purpose of obtaining the "right" volume * descriptor; accesses to the raw device should not get redirected). * + * 3.0 Just a "normal" increment, with some provisions to do it better. ;-) + * Introduced "#define READ_AUDIO" to specify the maximum number of + * audio frames to grab with one request. This defines a buffer size + * within kernel space; a value of 0 will reserve no such space and + * disable the CDROMREADAUDIO ioctl. A value of 75 enables the reading + * of a whole second with one command, but will use a buffer of more + * than 172 kB. + * Started CD200 support. Drive detection should work, but nothing + * more. + * * TODO * * disk change detection @@ -234,16 +244,18 @@ #include "blk.h" -#define VERSION "2.9 Eberhard Moenkeberg " +#define VERSION "3.0 Eberhard Moenkeberg " /* * still testing around... */ -#define MULTISESSION_BY_DRIVER 0 /* needs "#define MULTISESSION" in +#define MULTISESSION_BY_DRIVER 1 /* if set to 0 here, we need a counterpart in * linux/fs/isofs/inode.c - * if set to 0 here */ +#define READ_AUDIO 4 /* max. number of audio frames to read with one */ + /* request (allocates n* 2352 bytes kernel memory!) */ #define TEAC 0 /* if 1: enable TEAC CD-55A support (not usable yet) */ +#define CD200 1 /* if 1: enable CD200 support (not usable yet) */ #define JUKEBOX 1 /* tray control: eject tray if no disk is in */ #define EJECT 1 /* tray control: eject tray after last use */ #define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */ @@ -415,6 +427,7 @@ * (1<=0x16) return (-12); /* general failure */ DriveStruct[d].CD_changed=0xFF; if (sta==0x11) return (-15); /* invalid disk change (LCS: removed) */ - if (lcs_drive) + if (famL_drive) if (sta==0x12) return (-15); /* invalid disk change (inserted) */ return (-2); /* drive not ready */ } @@ -821,7 +833,7 @@ j=inb(CDi_status); if (!(j&s_not_data_ready)) return (j); if (!(j&s_not_result_ready)) return (j); - if (!new_drive) if (j&s_attention) return (j); + if (fam0L_drive) if (j&s_attention) return (j); } else for(timeout = jiffies + 1000, i=maxtim_data; timeout > jiffies; ) @@ -831,7 +843,7 @@ j=inb(CDi_status); if (!(j&s_not_data_ready)) return (j); if (!(j&s_not_result_ready)) return (j); - if (!new_drive) if (j&s_attention) return (j); + if (fam0L_drive) if (j&s_attention) return (j); } sbp_sleep(1); i = 1; @@ -917,7 +929,7 @@ /*==========================================================================*/ static int EvaluateStatus(int st) { - if (old_drive) + if (fam0_drive) { DriveStruct[d].status_byte=0; if (st&p_caddin_old) DriveStruct[d].status_byte |= p_door_closed|p_caddy_in; @@ -926,7 +938,7 @@ if (st&p_busy_old) DriveStruct[d].status_byte |= p_busy_new; if (st&p_disk_ok) DriveStruct[d].status_byte |= p_disk_ok; } - else if (lcs_drive) + else if (famL_drive) { DriveStruct[d].status_byte=0; if (st&p_caddin_old) DriveStruct[d].status_byte |= p_disk_ok|p_caddy_in; @@ -936,11 +948,14 @@ if (st&p_lcs_door_closed) DriveStruct[d].status_byte |= p_door_closed; if (st&p_lcs_door_locked) DriveStruct[d].status_byte |= p_door_locked; } - else /* new drive */ + else if (fam1_drive) { DriveStruct[d].status_byte=st; st=p_success_old; /* for new drives: fake "successful" bit of old drives */ } + else /* CD200, CD-55A */ + { + } return (st); } /*==========================================================================*/ @@ -999,9 +1014,12 @@ DPRINTF((DBG_STA,"SBPCD: giving xx_ReadStatus command\n")); SBPCD_CLI; - if (new_drive) OUT(CDo_command,0x05); - else OUT(CDo_command,0x81); - if (!old_drive) for (i=0;i<6;i++) OUT(CDo_command,0); + if (fam0L_drive) OUT(CDo_command,CMD0_STATUS); + else if (fam1_drive) OUT(CDo_command,CMD1_STATUS); + else /* CD200, CD-55A */ + { + } + if (!fam0_drive) for (i=0;i<6;i++) OUT(CDo_command,0); SBPCD_STI; } /*==========================================================================*/ @@ -1011,27 +1029,33 @@ clr_cmdbuf(); DPRINTF((DBG_ERR,"SBPCD: giving xx_ReadError command.\n")); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x82; + drvcmd[0]=CMD1_READ_ERR; response_count=8; flags_cmd_out=f_putcmd|f_ResponseStatus; } - else + else if (fam0L_drive) { - drvcmd[0]=0x82; + drvcmd[0]=CMD0_READ_ERR; response_count=6; - if (lcs_drive) + if (famL_drive) flags_cmd_out=f_putcmd; else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus; } + else /* CD200, CD-55A */ + { + } i=cmd_out(); DriveStruct[d].error_byte=0; DPRINTF((DBG_ERR,"SBPCD: xx_ReadError: cmd_out(82) returns %d (%02X)\n",i,i)); if (i<0) return (i); - if (old_drive) i=1; - else i=2; + if (fam0_drive) i=1; + else if (fam1L_drive) i=2; + else /* CD200, CD-55A */ + { + } DriveStruct[d].error_byte=infobuf[i]; DPRINTF((DBG_ERR,"SBPCD: xx_ReadError: infobuf[%d] is %d (%02X)\n",i,DriveStruct[d].error_byte,DriveStruct[d].error_byte)); i=sta2err(infobuf[i]); @@ -1056,9 +1080,9 @@ { if (cmd_type!=0) { - if (sbpro_type==1) OUT(CDo_sel_d_i,0x01); + if (sbpro_type==1) OUT(CDo_sel_i_d,1); DPRINTF((DBG_INF,"SBPCD: misleaded to try ResponseData.\n")); - if (sbpro_type==1) OUT(CDo_sel_d_i,0x00); + if (sbpro_type==1) OUT(CDo_sel_i_d,0); return (-22); } else i=ResponseInfo(); @@ -1108,8 +1132,9 @@ clr_cmdbuf(); if (f_blk_msf>1) return (-3); - if (old_drive) + if (fam0_drive) { + drvcmd[0]=CMD0_SEEK; /* same as CMD1_ and CMDL_ */ if (f_blk_msf==1) pos=msf2blk(pos); drvcmd[2]=(pos>>16)&0x00FF; drvcmd[3]=(pos>>8)&0x00FF; @@ -1117,18 +1142,21 @@ flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta | f_ResponseStatus | f_obey_p_check | f_bit1; } - else + else if (fam1L_drive) { + drvcmd[0]=CMD1_SEEK; /* same as CMD1_ and CMDL_ */ if (f_blk_msf==0) pos=blk2msf(pos); drvcmd[1]=(pos>>16)&0x00FF; drvcmd[2]=(pos>>8)&0x00FF; drvcmd[3]=pos&0x00FF; - if (lcs_drive) + if (famL_drive) flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; else flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; } - drvcmd[0]=0x01; + else /* CD200, CD-55A */ + { + } response_count=0; i=cmd_out(); return (i); @@ -1141,17 +1169,20 @@ DPRINTF((DBG_SPI,"SBPCD: SpinUp.\n")); DriveStruct[d].in_SpinUp = 1; clr_cmdbuf(); - if (!new_drive) + if (fam0L_drive) { - drvcmd[0]=0x05; + drvcmd[0]=CMD0_SPINUP; flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta| f_ResponseStatus|f_obey_p_check|f_bit1; } - else /* new_drive */ + else if (fam1_drive) { - drvcmd[0]=0x02; + drvcmd[0]=CMD1_SPINUP; flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; } + else /* CD200, CD-55A */ + { + } response_count=0; i=cmd_out(); DriveStruct[d].in_SpinUp = 0; @@ -1162,21 +1193,24 @@ { int i; - if (old_drive) return (-3); + if (fam0_drive) return (-3); clr_cmdbuf(); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x06; + drvcmd[0]=CMD1_SPINDOWN; flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; response_count=0; } - else /* lcs_drive */ + else if (famL_drive) { - drvcmd[0]=0x0D; + drvcmd[0]=CMDL_SPINDOWN; drvcmd[1]=1; flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; response_count=0; } + else /* CD200, CD-55A */ + { + } i=cmd_out(); return (i); } @@ -1185,9 +1219,9 @@ { int i; - if (!new_drive) return (-3); + if (fam0L_drive) return (-3); clr_cmdbuf(); - drvcmd[0]=0x09; + drvcmd[0]=CMD1_SETMODE; drvcmd[1]=0x03; drvcmd[2]=speed; drvcmd[3]=x1; @@ -1212,7 +1246,7 @@ volume1=value1=DriveStruct[d].vol_ctrl1; control0=value0=0; - if (((DriveStruct[d].drv_options&sax_a)!=0)&&(DriveStruct[d].drv_type>=drv_211)) + if (((DriveStruct[d].drv_options&audio_mono)!=0)&&(DriveStruct[d].drv_type>=drv_211)) { if ((volume0!=0)&&(volume1==0)) { @@ -1236,7 +1270,7 @@ volume1=0; } - if (new_drive) + if (fam1_drive) { control0=channel0+1; control1=channel1+1; @@ -1244,7 +1278,7 @@ value1=value0; if (volume0==0) control0=0; if (volume1==0) control1=0; - drvcmd[0]=0x09; + drvcmd[0]=CMD1_SETMODE; drvcmd[1]=0x05; drvcmd[3]=control0; drvcmd[4]=value0; @@ -1252,18 +1286,18 @@ drvcmd[6]=value1; flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; } - else if (lcs_drive) + else if (famL_drive) { if ((volume0==0)||(channel0!=0)) control0 |= 0x80; if ((volume1==0)||(channel1!=1)) control0 |= 0x40; if (volume0|volume1) value0=0x80; - drvcmd[0]=0x84; + drvcmd[0]=CMDL_SETMODE; drvcmd[1]=0x03; drvcmd[4]=control0; drvcmd[5]=value0; flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; } - else /* old_drive, different firmware levels */ + else if (fam0_drive) /* different firmware levels */ { if (DriveStruct[d].drv_type>=drv_300) { @@ -1323,13 +1357,15 @@ if (channel1!=1) control0 |= 0x10; } } - drvcmd[0]=0x84; + drvcmd[0]=CMD0_SETMODE; drvcmd[1]=0x83; drvcmd[4]=control0; drvcmd[5]=value0; flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; } - + else /* CD200, CD-55A */ + { + } response_count=0; i=cmd_out(); if (i>0) return (i); @@ -1353,16 +1389,19 @@ int i; DPRINTF((DBG_RES,"SBPCD: xy_DriveReset called.\n")); - if (!new_drive) OUT(CDo_reset,0x00); - else + if (fam0L_drive) OUT(CDo_reset,0x00); + else if (fam1_drive) { clr_cmdbuf(); - drvcmd[0]=0x0A; + drvcmd[0]=CMD1_RESET; flags_cmd_out=f_putcmd; response_count=0; i=cmd_out(); } - if (lcs_drive) sbp_sleep(500); /* wait 5 seconds */ + else /* CD200, CD-55A */ + { + } + if (famL_drive) sbp_sleep(500); /* wait 5 seconds */ else sbp_sleep(100); /* wait a second */ flush_status(); i=GetStatus(); @@ -1415,21 +1454,24 @@ int i; clr_cmdbuf(); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x0D; + drvcmd[0]=CMD1_PAU_RES; flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; } - else + else if (fam0L_drive) { - drvcmd[0]=0x8D; - if (lcs_drive) + drvcmd[0]=CMD0_PAU_RES; + if (famL_drive) flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus| f_obey_p_check|f_bit1; else flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus| f_obey_p_check; } + else /* CD200, CD-55A */ + { + } if (pau_res!=1) drvcmd[1]=0x80; response_count=0; i=cmd_out(); @@ -1440,24 +1482,27 @@ { int i; - if (old_drive) return (0); + if (fam0_drive) return (0); DPRINTF((DBG_LCK,"SBPCD: yy_LockDoor: %d (drive %d)\n", lock, d)); DPRINTF((DBG_LCS,"SBPCD: p_door_locked bit %d before\n", st_door_locked)); clr_cmdbuf(); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x0C; + drvcmd[0]=CMD1_LOCK_CTL; if (lock==1) drvcmd[1]=0x01; flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; response_count=0; } - else /* lcs_drive */ + else if (famL_drive) { - drvcmd[0]=0x0E; + drvcmd[0]=CMDL_LOCK_CTL; if (lock==1) drvcmd[1]=0x01; flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; response_count=0; } + else /* CD200, CD-55A */ + { + } i=cmd_out(); DPRINTF((DBG_LCS,"SBPCD: p_door_locked bit %d after\n", st_door_locked)); return (i); @@ -1467,22 +1512,25 @@ { int i; - if (old_drive) return (0); + if (fam0_drive) return (0); DPRINTF((DBG_LCK,"SBPCD: yy_CloseTray (drive %d)\n", d)); DPRINTF((DBG_LCS,"SBPCD: p_door_closed bit %d before\n", st_door_closed)); clr_cmdbuf(); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x07; + drvcmd[0]=CMD1_TRAY_CTL; flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check; } - else /* lcs_drive */ + else if (famL_drive) { - drvcmd[0]=0x0D; + drvcmd[0]=CMDL_TRAY_CTL; flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta| f_ResponseStatus|f_obey_p_check|f_bit1; } + else /* CD200, CD-55A */ + { + } response_count=0; i=cmd_out(); DPRINTF((DBG_LCS,"SBPCD: p_door_closed bit %d after\n", st_door_closed)); @@ -1497,26 +1545,29 @@ for (j=255;j>0;j--) { clr_cmdbuf(); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x87; + drvcmd[0]=CMD1_READSUBQ; flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; response_count=11; } - else + else if (fam0L_drive) { - drvcmd[0]=0x89; + drvcmd[0]=CMD0_READSUBQ; drvcmd[1]=0x02; - if (lcs_drive) + if (famL_drive) flags_cmd_out=f_putcmd; else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; response_count=13; } + else /* CD200, CD-55A */ + { + } i=cmd_out(); if (i<0) return (i); DPRINTF((DBG_SQ,"SBPCD: xx_ReadSubQ:")); - for (i=0;i<(new_drive?11:13);i++) + for (i=0;i<(fam1_drive?11:13);i++) { DPRINTF((DBG_SQ," %02X", infobuf[i])); } @@ -1533,10 +1584,10 @@ DriveStruct[d].SubQ_trk=byt2bcd(infobuf[2]); DriveStruct[d].SubQ_pnt_idx=byt2bcd(infobuf[3]); i=4; - if (!new_drive) i=5; + if (fam0L_drive) i=5; DriveStruct[d].SubQ_run_tot=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */ i=7; - if (!new_drive) i=9; + if (fam0L_drive) i=9; DriveStruct[d].SubQ_run_trk=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */ DriveStruct[d].SubQ_whatisthis=infobuf[i+3]; DriveStruct[d].diskstate_flags |= subq_bit; @@ -1549,31 +1600,38 @@ DriveStruct[d].diskstate_flags &= ~frame_size_bit; clr_cmdbuf(); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x84; + drvcmd[0]=CMD1_GETMODE; drvcmd[1]=0x00; flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; response_count=5; } - else + else if (fam0L_drive) { - drvcmd[0]=0x85; + drvcmd[0]=CMD0_GETMODE; drvcmd[1]=0x00; - if (lcs_drive) + if (famL_drive) flags_cmd_out=f_putcmd; else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; response_count=2; } + else /* CD200, CD-55A */ + { + } i=cmd_out(); if (i<0) return (i); i=0; - if (new_drive) DriveStruct[d].sense_byte=infobuf[i++]; + if (fam0L_drive) DriveStruct[d].sense_byte=0; + if (fam1_drive) DriveStruct[d].sense_byte=infobuf[i++]; + else /* CD200, CD-55A */ + { + } DriveStruct[d].frame_size=make16(infobuf[i],infobuf[i+1]); DPRINTF((DBG_XA,"SBPCD: xx_ModeSense: ")); - for (i=0;i<(new_drive?5:2);i++) + for (i=0;i<(fam1_drive?5:2);i++) { DPRINTF((DBG_XA,"%02X ", infobuf[i])); } @@ -1597,27 +1655,30 @@ DPRINTF((DBG_XA,"SBPCD: xx_ModeSelect: %02X %04X\n", DriveStruct[d].sense_byte, DriveStruct[d].frame_size)); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x09; + drvcmd[0]=CMD1_SETMODE; drvcmd[1]=0x00; drvcmd[2]=DriveStruct[d].sense_byte; drvcmd[3]=(DriveStruct[d].frame_size>>8)&0xFF; drvcmd[4]=DriveStruct[d].frame_size&0xFF; flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; } - else + else if (fam0L_drive) { - drvcmd[0]=0x84; + drvcmd[0]=CMD0_SETMODE; drvcmd[1]=0x00; drvcmd[2]=(DriveStruct[d].frame_size>>8)&0xFF; drvcmd[3]=DriveStruct[d].frame_size&0xFF; drvcmd[4]=0x00; - if(lcs_drive) + if(famL_drive) flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check; else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; } + else /* CD200, CD-55A */ + { + } response_count=0; i=cmd_out(); if (i<0) return (i); @@ -1634,26 +1695,29 @@ DriveStruct[d].diskstate_flags &= ~volume_bit; clr_cmdbuf(); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x84; + drvcmd[0]=CMD1_GETMODE; drvcmd[1]=0x05; response_count=5; flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; } - else + else if (fam0L_drive) { - drvcmd[0]=0x85; + drvcmd[0]=CMD0_GETMODE; drvcmd[1]=0x03; response_count=2; - if(lcs_drive) + if(famL_drive) flags_cmd_out=f_putcmd; else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; } + else /* CD200, CD-55A */ + { + } i=cmd_out(); if (i<0) return (i); - if (new_drive) + if (fam1_drive) { chan0=infobuf[1]&0x0F; vol0=infobuf[2]; @@ -1672,7 +1736,7 @@ chan0 >>= 1; chan1 >>= 1; } - else if (lcs_drive) + else if (famL_drive) { chan0=0; chan1=1; @@ -1681,7 +1745,7 @@ if ((switches&0x80)!=0) chan0=1; if ((switches&0x40)!=0) chan1=0; } - else /* old_drive, different firmware levels */ + else if (fam0_drive) /* different firmware levels */ { chan0=0; chan1=1; @@ -1698,30 +1762,39 @@ if ((switches&0x20)!=0) chan0=1; if ((switches&0x10)!=0) chan1=0; } - } - else + } + else { - vol0=infobuf[0]; - if ((vol0&0x01)!=0) chan0=1; - if ((vol1&0x01)==0) chan1=0; - vol0 &= 0xFC; - vol1 &= 0xFC; - if (vol0!=0) vol0 += 3; - if (vol1!=0) vol1 += 3; - } - } + vol0=infobuf[0]; + if ((vol0&0x01)!=0) chan0=1; + if ((vol1&0x01)==0) chan1=0; + vol0 &= 0xFC; + vol1 &= 0xFC; + if (vol0!=0) vol0 += 3; + if (vol1!=0) vol1 += 3; + } + } + } + else /* CD200, CD-55A */ + { } DriveStruct[d].vol_chan0=chan0; DriveStruct[d].vol_ctrl0=vol0; DriveStruct[d].vol_chan1=chan1; DriveStruct[d].vol_ctrl1=vol1; - if (!lcs_drive) + if (fam01_drive) { DriveStruct[d].vol_chan2=2; DriveStruct[d].vol_ctrl2=0xFF; DriveStruct[d].vol_chan3=3; DriveStruct[d].vol_ctrl3=0xFF; } + else if (famL_drive) + { + } + else /* CD200, CD-55A */ + { + } DriveStruct[d].diskstate_flags |= volume_bit; return (0); } @@ -1733,24 +1806,27 @@ DriveStruct[d].diskstate_flags &= ~cd_size_bit; clr_cmdbuf(); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x85; + drvcmd[0]=CMD1_CAPACITY; flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; } - else + else if (fam0L_drive) { - drvcmd[0]=0x88; - if(lcs_drive) + drvcmd[0]=CMD0_CAPACITY; + if(famL_drive) flags_cmd_out=f_putcmd; else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; } + else /* CD200, CD-55A */ + { + } response_count=5; i=cmd_out(); if (i<0) return (i); DriveStruct[d].CDsize_blk=make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])); - if (new_drive) DriveStruct[d].CDsize_blk=msf2blk(DriveStruct[d].CDsize_blk); + if (fam1_drive) DriveStruct[d].CDsize_blk=msf2blk(DriveStruct[d].CDsize_blk); DriveStruct[d].CDsize_frm = (DriveStruct[d].CDsize_blk * make16(infobuf[3],infobuf[4])) / CD_FRAMESIZE; DriveStruct[d].CDsize_blk += 151; DriveStruct[d].diskstate_flags |= cd_size_bit; @@ -1763,19 +1839,22 @@ DriveStruct[d].diskstate_flags &= ~toc_bit; clr_cmdbuf(); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x8B; + drvcmd[0]=CMD1_DISKINFO; flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; } - else + else if (fam0L_drive) { - drvcmd[0]=0x8B; - if(lcs_drive) + drvcmd[0]=CMD0_DISKINFO; + if(famL_drive) flags_cmd_out=f_putcmd; else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; } + else /* CD200, CD-55A */ + { + } response_count=6; i=cmd_out(); if (i<0) return (i); @@ -1795,20 +1874,23 @@ int i; clr_cmdbuf(); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x8C; + drvcmd[0]=CMD1_READTOC; flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; } - else + else if (fam0L_drive) { - drvcmd[0]=0x8C; + drvcmd[0]=CMD0_READTOC; drvcmd[1]=0x02; - if(lcs_drive) + if(famL_drive) flags_cmd_out=f_putcmd; else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; } + else /* CD200, CD-55A */ + { + } drvcmd[2]=num; response_count=8; i=cmd_out(); @@ -1817,7 +1899,7 @@ DriveStruct[d].TocEnt_ctl_adr=swap_nibbles(infobuf[1]); DriveStruct[d].TocEnt_number=infobuf[2]; DriveStruct[d].TocEnt_format=infobuf[3]; - if (new_drive) i=4; + if (fam1_drive) i=4; else i=5; DriveStruct[d].TocEnt_address=make32(make16(0,infobuf[i]), make16(infobuf[i+1],infobuf[i+2])); @@ -1833,12 +1915,15 @@ int i; clr_cmdbuf(); - drvcmd[0]=0x8E; + drvcmd[0]=CMD0_PACKET; drvcmd[1]=response_count; - if(lcs_drive) + if(famL_drive) flags_cmd_out=f_putcmd; - else + else if (fam01_drive) flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check; + else /* CD200, CD-55A */ + { + } i=cmd_out(); return (i); } @@ -1848,15 +1933,18 @@ int i; p++; - if (!new_drive) p[13]=0; + if (fam0L_drive) p[13]=0; for (i=0;i<7;i++) { - if (new_drive) DriveStruct[d].UPC_buf[i]=swap_nibbles(*p++); - else + if (fam1_drive) DriveStruct[d].UPC_buf[i]=swap_nibbles(*p++); + else if (fam0L_drive) { DriveStruct[d].UPC_buf[i]=((*p++)<<4)&0xFF; DriveStruct[d].UPC_buf[i] |= *p++; } + else /* CD200, CD-55A */ + { + } } DriveStruct[d].UPC_buf[6] &= 0xF0; return (0); @@ -1875,9 +1963,9 @@ { #endif TEST_UPC clr_cmdbuf(); - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x88; + drvcmd[0]=CMD1_READ_UPC; #if TEST_UPC drvcmd[1]=(block>>16)&0xFF; drvcmd[2]=(block>>8)&0xFF; @@ -1886,9 +1974,9 @@ response_count=8; flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; } - else + else if (fam0L_drive) { - drvcmd[0]=0x08; + drvcmd[0]=CMD0_READ_UPC; #if TEST_UPC drvcmd[2]=(block>>16)&0xFF; drvcmd[3]=(block>>8)&0xFF; @@ -1897,12 +1985,15 @@ response_count=0; flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; } + else /* CD200, CD-55A */ + { + } i=cmd_out(); if (i<0) return (i); - if (!new_drive) + if (fam0L_drive) { response_count=16; - if (lcs_drive) flags_cmd_out=f_putcmd; + if (famL_drive) flags_cmd_out=f_putcmd; i=xx_ReadPacket(); if (i<0) return (i); } @@ -1910,7 +2001,7 @@ checksum=0; #endif TEST_UPC DPRINTF((DBG_UPC,"SBPCD: UPC info: ")); - for (i=0;i<(new_drive?8:16);i++) + for (i=0;i<(fam1_drive?8:16);i++) { #if TEST_UPC checksum |= infobuf[i]; @@ -1923,7 +2014,7 @@ } #endif TEST_UPC DriveStruct[d].UPC_ctl_adr=0; - if (new_drive) i=0; + if (fam1_drive) i=0; else i=2; if ((infobuf[i]&0x80)!=0) { @@ -1950,9 +2041,10 @@ DriveStruct[d].f_multisession=0; clr_cmdbuf(); DriveStruct[d].lba_multi=0; - if (new_drive) + if (fam0_drive) return (0); + if (fam1_drive) { - drvcmd[0]=0x8D; + drvcmd[0]=CMD1_MULTISESS; response_count=6; flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; i=cmd_out(); @@ -1973,9 +2065,9 @@ DriveStruct[d].lba_multi)); } } - else if (lcs_drive) + else if (famL_drive) { - drvcmd[0]=0x8C; + drvcmd[0]=CMDL_MULTISESS; drvcmd[1]=3; drvcmd[2]=1; response_count=8; @@ -1999,6 +2091,9 @@ */ } } + else /* CD200, CD-55A */ + { + } return (0); } /*==========================================================================*/ @@ -2008,12 +2103,12 @@ { int i; - if (!new_drive) return (-ENOSYS); /* drive firmware lacks it */ + if (fam0L_drive) return (-ENOSYS); /* drive firmware lacks it */ #if 0 if (DriveStruct[d].audio_state!=audio_playing) return (-ENODATA); #endif clr_cmdbuf(); - drvcmd[0]=0x11; + drvcmd[0]=CMD1_SUBCHANINF; drvcmd[1]=(frame>>16)&0xFF; drvcmd[2]=(frame>>8)&0xFF; drvcmd[3]=frame&0xFF; @@ -2079,23 +2174,22 @@ static void teac_reset(int drv_id) { int i; -#define CMD_TEAC_RESET 0xc0 - OUT(CDo_sel_d_i,0); + OUT(CDo_sel_i_d,0); OUT(CDo_enable,drv_id); - OUT(CDo_command,CMD_TEAC_RESET); + OUT(CDo_command,CMDT_RESET); for (i=0;i<9;i++) OUT(CDo_command,0); DPRINTF((DBG_TEA,"SBPCD: TEAC soft reset.\n")); sbp_sleep(100); /* wait a second */ } /*==========================================================================*/ -static int look_for_drive(int drv_id) +static int look_for_TEAC_drive(int drv_id) { int i; if (sbpro_type!=1) teac_reset(drv_id); OUT(CDo_enable,drv_id); - OUT(CDo_sel_d_i,0); + OUT(CDo_sel_i_d,0); i=inb(CDi_status); if (i&s_not_result_ready) return (-1); /* drive not present */ i=inb(CDi_info); @@ -2111,7 +2205,7 @@ found=0; for (i=0;i<4;i++) { - j=look_for_drive(i); + j=look_for_TEAC_drive(i); if (j<1) continue; found++; DPRINTF((DBG_TEA,"SBPCD: TEAC drive (id=%d) found.\n",i)); @@ -2121,65 +2215,201 @@ /*==========================================================================*/ #endif TEAC /*==========================================================================*/ +/*==========================================================================*/ +#ifdef CD200 + +#if 0 + +static int xx_ReadVersion(int fam) +{ + int i; + + clr_cmdbuf(); + clear_response_buffer(13); + if (fam==1) + { + drvcmd[0]=CMD0_READ_VER; /* same as CMD1_ and CMDL_ */ + response_count=13; + i=do_cmd(f_putcmd|f_lopsta|f_getsta|f_ResponseStatus); + } + else if (fam==2) + { + drvcmd[0]=CMD2_READ_VER; + response_count=12; + i=do_cmd(f_putcmd); + } + else return (-1); + return (i); +} + +static int yy_ReadError(int fam) +{ + int i; + + clr_cmdbuf(); + response_count=9; + clr_respo_buf(9); + if (fam==1) + { + drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */ + i=do_cmd(f_putcmd|f_lopsta|f_getsta|f_ResponseStatus); + } + else if (fam==2) + { + drvcmd[0]=CMD2_READ_ERR; + i=do_cmd(f_putcmd); + } + else return (-1); + return (i); +} +#endif + +#endif CD200 +/*==========================================================================*/ static int check_version(void) { int i, j; DPRINTF((DBG_INI,"SBPCD: check_version entered.\n")); - /* clear any pending error state */ + DriveStruct[d].drv_type=0; + + /* check for CD200 and CD-55A first */ clr_cmdbuf(); - drvcmd[0]=0x82; + drvcmd[0]=CMD2_READ_ERR; response_count=9; flags_cmd_out=f_putcmd; i=cmd_out(); - if (i<0) DPRINTF((DBG_INI,"SBPCD: cmd_82 returns %d (ok anyway).\n",i)); - + if (i<0) DPRINTF((DBG_INI,"SBPCD: CMD2_READERR returns %d (ok anyway).\n",i)); /* read drive version */ clr_cmdbuf(); for (i=0;i<12;i++) infobuf[i]=0; - drvcmd[0]=0x83; + if (sbpro_type==1) OUT(CDo_sel_i_d,0); +#if 0 + OUT(CDo_reset,0); + sbp_sleep(600); + OUT(CDo_enable,d); +#endif + drvcmd[0]=CMD2_READ_VER; response_count=12; flags_cmd_out=f_putcmd; i=cmd_out(); - if (i<0) DPRINTF((DBG_INI,"SBPCD: cmd_83 returns %d\n",i)); - - DPRINTF((DBG_INI,"SBPCD: infobuf = \"")); - for (i=0;i<12;i++) DPRINTF((DBG_INI,"%c",infobuf[i])); - DPRINTF((DBG_INI,"\"\n")); - - DriveStruct[d].drv_type=0; - for (i=0;i<4;i++) if (infobuf[i]!=drive_family[i]) break; - if (i==4) + if (i<0) DPRINTF((DBG_INI,"SBPCD: CMD2_READ_VER returns %d\n",i)); + else { - DriveStruct[d].drive_model[0]=infobuf[i++]; - DriveStruct[d].drive_model[1]=infobuf[i++]; - DriveStruct[d].drive_model[2]='-'; - DriveStruct[d].drive_model[3]='x'; - DriveStruct[d].drive_model[4]=0; - DriveStruct[d].drv_type=drv_new; + for (i=0, j=0;i<12;i++) j+=infobuf[i]; + if (j) + { + DPRINTF((DBG_ID,"SBPCD: infobuf = \"")); + for (i=0;i<12;i++) DPRINTF((DBG_ID,"%02X ",infobuf[i])); + for (i=0;i<12;i++) DPRINTF((DBG_ID,"%c",infobuf[i])); + DPRINTF((DBG_ID,"\"\n")); + } } - if (!DriveStruct[d].drv_type) + + for (i=0;i<5;i++) if (infobuf[i]!=family2[i]) break; + if (i==5) + { + DriveStruct[d].drive_model[0]='C'; + DriveStruct[d].drive_model[1]='D'; + DriveStruct[d].drive_model[2]='2'; + DriveStruct[d].drive_model[3]='0'; + DriveStruct[d].drive_model[4]='0'; + DriveStruct[d].drive_model[5]=infobuf[i++]; + DriveStruct[d].drive_model[6]=infobuf[i++]; + DriveStruct[d].drive_model[7]=0; + DriveStruct[d].drv_type=drv_fam2; + } + else { - for (i=0;i<8;i++) if (infobuf[i]!=drive_vendor[i]) break; - if (i==8) + for (i=0;i<5;i++) if (infobuf[i]!=familyT[i]) break; + if (i==5) { - DriveStruct[d].drive_model[0]='2'; - DriveStruct[d].drive_model[1]='x'; + DriveStruct[d].drive_model[0]='C'; + DriveStruct[d].drive_model[1]='D'; DriveStruct[d].drive_model[2]='-'; - DriveStruct[d].drive_model[3]='x'; - DriveStruct[d].drive_model[4]=0; - DriveStruct[d].drv_type=drv_old; + DriveStruct[d].drive_model[3]='5'; + DriveStruct[d].drive_model[4]='5'; + DriveStruct[d].drive_model[5]='A'; + DriveStruct[d].drive_model[6]=infobuf[i++]; + DriveStruct[d].drive_model[7]=infobuf[i++]; + DriveStruct[d].drive_model[8]=0; + DriveStruct[d].drv_type=drv_famT; } } +#if 1 + if (DriveStruct[d].drv_type) + { + printk("\n\nSBPCD: new drive CD200 or CD-55A successfully detected.\n"); + printk("SBPCD: support is not fulfilled yet - drive gets ignored.\n"); + printk("SBPCD: just wait some days - don't mail, please.\n\n"); + DriveStruct[d].drv_type=0; + } +#endif if (!DriveStruct[d].drv_type) { - for (i=0;i<8;i++) if (infobuf[i]!=lcs_family[i]) break; - if (i==8) + /* check for CR-52x, CR-56x and LCS-7260 */ + /* clear any pending error state */ + clr_cmdbuf(); + drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */ + response_count=9; + flags_cmd_out=f_putcmd; + i=cmd_out(); + if (i<0) DPRINTF((DBG_INI,"SBPCD: CMD0_READERR returns %d (ok anyway).\n",i)); + /* read drive version */ + clr_cmdbuf(); + for (i=0;i<12;i++) infobuf[i]=0; + drvcmd[0]=CMD0_READ_VER; /* same as CMD1_ and CMDL_ */ + response_count=12; + flags_cmd_out=f_putcmd; + i=cmd_out(); + if (i<0) DPRINTF((DBG_INI,"SBPCD: CMD0_READ_VER returns %d\n",i)); + + for (i=0, j=0;i<12;i++) j+=infobuf[i]; + if (j) { - for (j=0;j<8;j++) - DriveStruct[d].drive_model[j]=infobuf[j]; - DriveStruct[d].drive_model[8]=0; - DriveStruct[d].drv_type=drv_lcs; + DPRINTF((DBG_ID,"SBPCD: infobuf = \"")); + for (i=0;i<12;i++) DPRINTF((DBG_ID,"%02X ",infobuf[i])); + for (i=0;i<12;i++) DPRINTF((DBG_ID,"%c",infobuf[i])); + DPRINTF((DBG_ID,"\"\n")); + } + + for (i=0;i<4;i++) if (infobuf[i]!=family1[i]) break; + if (i==4) + { + DriveStruct[d].drive_model[0]='C'; + DriveStruct[d].drive_model[1]='R'; + DriveStruct[d].drive_model[2]='-'; + DriveStruct[d].drive_model[3]='5'; + DriveStruct[d].drive_model[4]=infobuf[i++]; + DriveStruct[d].drive_model[5]=infobuf[i++]; + DriveStruct[d].drive_model[6]=0; + DriveStruct[d].drv_type=drv_fam1; + } + if (!DriveStruct[d].drv_type) + { + for (i=0;i<8;i++) if (infobuf[i]!=family0[i]) break; + if (i==8) + { + DriveStruct[d].drive_model[0]='C'; + DriveStruct[d].drive_model[1]='R'; + DriveStruct[d].drive_model[2]='-'; + DriveStruct[d].drive_model[3]='5'; + DriveStruct[d].drive_model[4]='2'; + DriveStruct[d].drive_model[5]='x'; + DriveStruct[d].drive_model[6]=0; + DriveStruct[d].drv_type=drv_fam0; + } + } + if (!DriveStruct[d].drv_type) + { + for (i=0;i<8;i++) if (infobuf[i]!=familyL[i]) break; + if (i==8) + { + for (j=0;j<8;j++) + DriveStruct[d].drive_model[j]=infobuf[j]; + DriveStruct[d].drive_model[8]=0; + DriveStruct[d].drv_type=drv_famL; + } } } if (!DriveStruct[d].drv_type) @@ -2188,7 +2418,7 @@ return (-1); } for (j=0;j<4;j++) DriveStruct[d].firmware_version[j]=infobuf[i+j]; - if (lcs_drive) + if (famL_drive) { DriveStruct[d].drv_type=drv_260; if ((DriveStruct[d].firmware_version[0]!='A') || @@ -2202,17 +2432,23 @@ j = (DriveStruct[d].firmware_version[0] & 0x0F) * 100 + (DriveStruct[d].firmware_version[2] & 0x0F) *10 + (DriveStruct[d].firmware_version[3] & 0x0F); - if (new_drive) + if (fam0_drive) + { + if (j<200) DriveStruct[d].drv_type=drv_199; + else if (j<201) DriveStruct[d].drv_type=drv_200; + else if (j<210) DriveStruct[d].drv_type=drv_201; + else if (j<211) DriveStruct[d].drv_type=drv_210; + else if (j<300) DriveStruct[d].drv_type=drv_211; + else if (j>=300) DriveStruct[d].drv_type=drv_300; + } + else if (fam1_drive) { if (j<100) DriveStruct[d].drv_type=drv_099; else DriveStruct[d].drv_type=drv_100; } - else if (j<200) DriveStruct[d].drv_type=drv_199; - else if (j<201) DriveStruct[d].drv_type=drv_200; - else if (j<210) DriveStruct[d].drv_type=drv_201; - else if (j<211) DriveStruct[d].drv_type=drv_210; - else if (j<300) DriveStruct[d].drv_type=drv_211; - else DriveStruct[d].drv_type=drv_300; + else /* CD200, CD-55A */ + { + } } DPRINTF((DBG_LCS,"SBPCD: drive type %02X\n",DriveStruct[d].drv_type)); DPRINTF((DBG_INI,"SBPCD: check_version done.\n")); @@ -2254,23 +2490,13 @@ { ndrives++; DriveStruct[d].drv_options=drv_pattern[j]; - if (!new_drive) + if (fam0L_drive) DriveStruct[d].drv_options&=~(speed_auto|speed_300|speed_150); - if (lcs_drive) - { - printk("%sDrive %d: %s (%.4s)\n", printk_header, - DriveStruct[d].drv_minor, - DriveStruct[d].drive_model, - DriveStruct[d].firmware_version); - } - else - { - printk("%sDrive %d: %s%.4s (%.4s)\n", printk_header, - DriveStruct[d].drv_minor, - drive_family, - DriveStruct[d].drive_model, - DriveStruct[d].firmware_version); - } + + printk("%sDrive %d: %.9s (%.4s)\n", printk_header, + DriveStruct[d].drv_minor, + DriveStruct[d].drive_model, + DriveStruct[d].firmware_version); printk_header=" - "; } else DriveStruct[d].drv_minor=-1; @@ -2350,7 +2576,7 @@ if (func2==tell_TocDescrip) return (-1); if (func2==tell_TocEntry) return (-1); if (func2==tell_subQ_info) return (-1); - if (new_drive) if (func2==tell_SubChanInfo) return (-1); + if (fam1_drive) if (func2==tell_SubChanInfo) return (-1); if (func2==tell_UPC) return (-1); #else return (0); @@ -2365,7 +2591,7 @@ if (func1==seek) return (-1); if (func1==audio_play) return (-1); if (func1!=ioctl_o) return (0); - if (new_drive) + if (fam1_drive) { if (func2==EjectDisk) return (-1); if (func2==CloseTray) return (-1); @@ -2383,13 +2609,13 @@ if (func2==tell_address) return (0); if (func2==tell_capabiliti) return (0); if (func2==tell_CD_changed) return (0); - if (!new_drive) if (func2==tell_SubChanInfo) return (0); + if (fam0L_drive) if (func2==tell_SubChanInfo) return (0); return (-1); } if (func1==ioctl_o) { if (func2==DriveReset) return (0); - if (!new_drive) + if (fam0L_drive) { if (func2==EjectDisk) return (0); if (func2==LockDoor) return (0); @@ -2507,7 +2733,7 @@ return (i); } #ifdef XA_TEST2 - if ((!new_drive) && (DriveStruct[d].xa_byte==0x20)) /* XA disk with old drive */ + if ((fam0L_drive) && (DriveStruct[d].xa_byte==0x20)) /* XA disk with old drive */ { xx_ModeSelect(CD_FRAMESIZE_XA); xx_ModeSense(); @@ -2525,12 +2751,15 @@ { int i; - if (!new_drive) + if (fam0L_drive) { i=inb(CDi_status); if (i&s_attention) GetStatus(); } - else GetStatus(); + else if (fam1_drive) GetStatus(); + else /* CD200, CD-55A */ + { + } if (DriveStruct[d].CD_changed==0xFF) { #if MANY_SESSION @@ -2605,9 +2834,9 @@ if (DriveStruct[d].audio_state==audio_playing) return (-EINVAL); clr_cmdbuf(); - if (lcs_drive) + if (famL_drive) { - drvcmd[0]=0x0A; + drvcmd[0]=CMDL_PLAY; i=msf2blk(pos_audio_start); n=msf2blk(pos_audio_end)+1-i; drvcmd[1]=(i>>16)&0x00FF; @@ -2621,18 +2850,21 @@ } else { - if (new_drive) + if (fam1_drive) { - drvcmd[0]=0x0E; + drvcmd[0]=CMD1_PLAY_MSF; flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check | f_wait_if_busy; } - else /* old_drive */ + else if (fam0_drive) { - drvcmd[0]=0x0B; + drvcmd[0]=CMD0_PLAY_MSF; flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta | f_ResponseStatus | f_obey_p_check | f_wait_if_busy; } + else /* CD200, CD-55A */ + { + } drvcmd[1]=(pos_audio_start>>16)&0x00FF; drvcmd[2]=(pos_audio_start>>8)&0x00FF; drvcmd[3]=pos_audio_start&0x00FF; @@ -2741,10 +2973,10 @@ switch (DriveStruct[d].audio_state) { case audio_playing: - if (lcs_drive) i=xx_ReadSubQ(); + if (famL_drive) i=xx_ReadSubQ(); else i=xx_Pause_Resume(1); if (i<0) return (-EIO); - if (lcs_drive) i=xx_Pause_Resume(1); + if (famL_drive) i=xx_Pause_Resume(1); else i=xx_ReadSubQ(); if (i<0) return (-EIO); DriveStruct[d].pos_audio_start=DriveStruct[d].SubQ_run_tot; @@ -2764,7 +2996,7 @@ /* been paused with a PAUSE command. */ /* It will resume playing from the location saved in SubQ_run_tot. */ if (DriveStruct[d].audio_state!=audio_pausing) return -EINVAL; - if (lcs_drive) + if (famL_drive) i=xx_PlayAudio(DriveStruct[d].pos_audio_start, DriveStruct[d].pos_audio_end); else i=xx_Pause_Resume(3); @@ -2880,7 +3112,7 @@ case CDROMEJECT: DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMEJECT entered.\n")); - if (old_drive) return (0); + if (fam0_drive) return (0); do i=yy_LockDoor(0); while (i!=0); DriveStruct[d].open_count=0; /* to get it locked next time again */ @@ -2894,7 +3126,7 @@ case CDROMEJECT_SW: DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMEJECT_SW entered.\n")); - if (old_drive) return (0); + if (fam0_drive) return (0); DriveStruct[d].f_eject=arg; return (0); @@ -2984,7 +3216,7 @@ DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADAUDIO requested.\n")); #if 0 - if (!new_drive) return (-EINVAL); + if (fam0L_drive) return (-EINVAL); #endif if (DriveStruct[d].aud_buf==NULL) return (-EINVAL); i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio)); @@ -3000,6 +3232,8 @@ else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */ block=read_audio.addr.lba; else return (-EINVAL); + i=yy_SetSpeed(speed_150,0,0); + if (i) DPRINTF((DBG_AUD,"SBPCD: read_audio: SetSpeed error %d\n", i)); DPRINTF((DBG_AUD,"SBPCD: read_audio: lba: %d, msf: %06X\n", block, blk2msf(block))); DPRINTF((DBG_AUD,"SBPCD: read_audio: before xx_ReadStatus.\n")); @@ -3027,25 +3261,28 @@ DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_status: ok.\n")); flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check; - if (!new_drive) + if (fam0L_drive) { flags_cmd_out |= f_lopsta | f_getsta | f_bit1; cmd_type=READ_M2; - drvcmd[0]=0x03; /* "read XA frames" command for old drives */ + drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */ drvcmd[1]=(block>>16)&0x000000ff; drvcmd[2]=(block>>8)&0x000000ff; drvcmd[3]=block&0x000000ff; drvcmd[4]=0; - drvcmd[5]=read_audio.nframes; /* # of frames */ + drvcmd[5]=read_audio.nframes; /* # of frames */ drvcmd[6]=0; } - else /* if new_drive */ + else if (fam1_drive) { - drvcmd[0]=0x10; /* "read frames" command for new drives */ + drvcmd[0]=CMD1_READ; /* "read frames", new drives */ lba2msf(block,&drvcmd[1]); /* msf-bin format required */ drvcmd[4]=0; drvcmd[5]=0; - drvcmd[6]=1; /* # of frames */ + drvcmd[6]=read_audio.nframes; /* # of frames */ + } + else /* CD200, CD-55A */ + { } DPRINTF((DBG_AUD,"SBPCD: read_audio: before giving \"read\" command.\n")); for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]); @@ -3061,7 +3298,7 @@ j=inb(CDi_status); if (!(j&s_not_data_ready)) break; if (!(j&s_not_result_ready)) break; - if (!new_drive) if (j&s_attention) break; + if (fam0L_drive) if (j&s_attention) break; } if (try != 0 || timeout <= jiffies) break; if (data_retrying == 0) data_waits++; @@ -3085,7 +3322,7 @@ DPRINTF((DBG_AUD,"SBPCD: read_audio: before reading data.\n")); error_flag=0; p = DriveStruct[d].aud_buf; - if (sbpro_type==1) OUT(CDo_sel_d_i,0x01); + if (sbpro_type==1) OUT(CDo_sel_i_d,1); #if 0 cli(); #endif @@ -3093,7 +3330,7 @@ #if 0 sti(); #endif - if (sbpro_type==1) OUT(CDo_sel_d_i,0x00); + if (sbpro_type==1) OUT(CDo_sel_i_d,0); data_retrying = 0; } DPRINTF((DBG_AUD,"SBPCD: read_audio: after reading data.\n")); @@ -3105,7 +3342,7 @@ #endif 0000 continue; } - if (!new_drive) + if (fam0L_drive) { i=maxtim_data; for (timeout=jiffies+900; timeout > jiffies; timeout--) @@ -3131,7 +3368,7 @@ } do { - if (!new_drive) xx_ReadStatus(); + if (fam0L_drive) xx_ReadStatus(); i=ResponseStatus(); /* builds status_byte, returns orig. status (old) or faked p_success_old (new) */ if (i<0) { DPRINTF((DBG_AUD, "SBPCD: read_audio: xx_ReadStatus error after read: %02X\n", @@ -3139,7 +3376,7 @@ continue; /* FIXME */ } } - while ((!new_drive)&&(!st_check)&&(!(i&p_success_old))); + while ((fam0L_drive)&&(!st_check)&&(!(i&p_success_old))); if (st_check) { i=xx_ReadError(); @@ -3187,7 +3424,7 @@ case CDROMMULTISESSION_SYS: /* tell start-of-last-session to kernel */ DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMMULTISESSION_SYS entered.\n")); if(!suser()) return -EACCES; - p_lba=arg; + p_lba=(unsigned int *) arg; if (DriveStruct[d].f_multisession) *p_lba=DriveStruct[d].lba_multi; else @@ -3286,7 +3523,7 @@ if (!st_spinning) xx_SpinUp(); #ifdef XA_TEST1 - if ((!new_drive) && (DriveStruct[d].xa_byte==0x20)) /* XA disk with old drive */ + if ((fam0L_drive) && (DriveStruct[d].xa_byte==0x20)) /* XA disk with old drive */ { xx_ModeSelect(CD_FRAMESIZE_XA); xx_ModeSense(); @@ -3339,7 +3576,7 @@ block=CURRENT->sector/4; #if MULTISESSION_BY_DRIVER - if (!old_drive) + if (!fam0_drive) { #if MANY_SESSION DPRINTF((DBG_MUL,"SBPCD: read MSF %08X\n", blk2msf(block))); @@ -3383,13 +3620,13 @@ f_ResponseStatus | f_obey_p_check; - if (!new_drive) + if (fam0L_drive) { flags_cmd_out |= f_lopsta | f_getsta | f_bit1; if (DriveStruct[d].xa_byte==0x20) { cmd_type=READ_M2; - drvcmd[0]=0x03; /* "read XA frames" command for old drives */ + drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */ drvcmd[1]=(block>>16)&0x000000ff; drvcmd[2]=(block>>8)&0x000000ff; drvcmd[3]=block&0x000000ff; @@ -3399,7 +3636,7 @@ } else { - drvcmd[0]=0x02; /* "read frames" command for old drives */ + drvcmd[0]=CMD0_READ; /* "read frames", old drives */ if (DriveStruct[d].drv_type>=drv_201) { @@ -3419,14 +3656,17 @@ drvcmd[6]=(DriveStruct[d].drv_type + + * serial.c: (receive_char): Added counter to prevent infinite loop + when a PCMCIA serial device is ejected. + Thu Dec 29 17:53:48 1994 * tty_io.c (check_tty_count): New procedure which checks diff -u --recursive --new-file v1.1.76/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v1.1.76/linux/drivers/char/Makefile Sun Jan 1 19:49:19 1995 +++ linux/drivers/char/Makefile Sat Jan 7 12:57:43 1995 @@ -73,15 +73,30 @@ SRCS := $(SRCS) mouse.c endif -all: char.a +MODULES := lp.o + +all: char.a modules char.a: $(OBJS) $(AR) rcs char.a $(OBJS) sync +ifdef MODULES + +modules: + $(MAKE) CFLAGS="$(CFLAGS) -DMODULE" $(MODULES) + (cd ../../modules;for i in $(MODULES); do ln -sf ../drivers/char/$$i .; done) + +else + +modules: + +endif + dep: $(CPP) -M $(SRCS) > .depend +modules: dummy: # diff -u --recursive --new-file v1.1.76/linux/drivers/char/console.c linux/drivers/char/console.c --- v1.1.76/linux/drivers/char/console.c Sun Jan 1 19:49:19 1995 +++ linux/drivers/char/console.c Thu Jan 5 09:00:34 1995 @@ -2152,8 +2152,6 @@ char *sptr, *buf = (char *)arg; int currcons, l, chcount; - if (!suser()) - return -EPERM; l = verify_area(VERIFY_READ, buf, 2); if (l) return l; diff -u --recursive --new-file v1.1.76/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c --- v1.1.76/linux/drivers/char/cyclades.c Tue Dec 27 09:54:44 1994 +++ linux/drivers/char/cyclades.c Thu Jan 5 13:55:40 1995 @@ -176,7 +176,6 @@ #include #include #include -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c --- v1.1.76/linux/drivers/char/keyboard.c Sun Jan 1 19:49:19 1995 +++ linux/drivers/char/keyboard.c Thu Jan 5 13:55:40 1995 @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/char/lp.c linux/drivers/char/lp.c --- v1.1.76/linux/drivers/char/lp.c Thu Dec 29 20:13:17 1994 +++ linux/drivers/char/lp.c Sat Jan 7 13:19:31 1995 @@ -5,6 +5,7 @@ * checking ought to be. * Copyright (C) 1993 by Nigel Gamble (added interrupt code) * Copyright (C) 1994 by Alan Cox (Modularised it) + * LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu */ #include @@ -38,6 +39,15 @@ #include #endif +/* Test if printer is ready (and optionally has no error conditions) */ +#define LP_READY(minor, status) \ + ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : (status & LP_PBUSY)) +#define LP_CAREFUL_READY(minor, status) \ + ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : 1) +#define _LP_CAREFUL_READY(status) \ + (status & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \ + (LP_PBUSY|LP_PSELECD|LP_PERRORP) + /* * All my debugging code assumes that you debug with only one printer at * a time. RWWH @@ -74,11 +84,7 @@ count ++; if(need_resched) schedule(); - if ((LP_F(minor) & LP_CAREFUL) && - (status & (LP_POUTPA|LP_PSELECD|LP_PERRORP)) != - (LP_PSELECD|LP_PERRORP)) - continue; - } while(!(status & LP_PBUSY) && count < LP_CHAR(minor)); + } while(!LP_READY(minor,status) && count < LP_CHAR(minor)); if (count == LP_CHAR(minor)) { return 0; @@ -113,9 +119,7 @@ || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY) || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)) { - if ((LP_F(minor) & LP_CAREFUL) && - (status & (LP_POUTPA|LP_PSELECD|LP_PERRORP)) != - (LP_PSELECD|LP_PERRORP)) + if (!LP_CAREFUL_READY(minor, status)) return 0; outb_p(lpchar, LP_B(minor)); /* must wait before taking strobe high, and after taking strobe @@ -172,15 +176,15 @@ int rc = total_bytes_written + bytes_written; status = LP_S(minor); if ((status & LP_POUTPA)) { - printk("lp%d out of paper\n", minor); + printk(KERN_INFO "lp%d out of paper\n", minor); if (LP_F(minor) & LP_ABORT) return rc?rc:-ENOSPC; } else if (!(status & LP_PSELECD)) { - printk("lp%d off-line\n", minor); + printk(KERN_INFO "lp%d off-line\n", minor); if (LP_F(minor) & LP_ABORT) return rc?rc:-EIO; } else if (!(status & LP_PERRORP)) { - printk("lp%d printer error\n", minor); + printk(KERN_ERR "lp%d printer error\n", minor); if (LP_F(minor) & LP_ABORT) return rc?rc:-EIO; } @@ -188,9 +192,7 @@ outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor))); status = LP_S(minor); if ((!(status & LP_PACK) || (status & LP_PBUSY)) - && (!(LP_F(minor) & LP_CAREFUL) || - (status & (LP_POUTPA|LP_PSELECD|LP_PERRORP)) - == (LP_PSELECD|LP_PERRORP))) { + && LP_CAREFUL_READY(minor, status)) { outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor))); sti(); continue; @@ -246,7 +248,7 @@ int status = LP_S(minor); if (status & LP_POUTPA) { - printk("lp%d out of paper\n", minor); + printk(KERN_INFO "lp%d out of paper\n", minor); if(LP_F(minor) & LP_ABORT) return temp-buf?temp-buf:-ENOSPC; current->state = TASK_INTERRUPTIBLE; @@ -254,7 +256,7 @@ schedule(); } else if (!(status & LP_PSELECD)) { - printk("lp%d off-line\n", minor); + printk(KERN_INFO "lp%d off-line\n", minor); if(LP_F(minor) & LP_ABORT) return temp-buf?temp-buf:-EIO; current->state = TASK_INTERRUPTIBLE; @@ -263,7 +265,7 @@ } else /* not offline or out of paper. on fire? */ if (!(status & LP_PERRORP)) { - printk("lp%d reported invalid error status (on fire, eh?)\n", minor); + printk(KERN_ERR "lp%d reported invalid error status (on fire, eh?)\n", minor); if(LP_F(minor) & LP_ABORT) return temp-buf?temp-buf:-EIO; current->state = TASK_INTERRUPTIBLE; @@ -326,13 +328,13 @@ if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) { int status = LP_S(minor); if (status & LP_POUTPA) { - printk("lp%d out of paper\n", minor); + printk(KERN_INFO "lp%d out of paper\n", minor); return -ENOSPC; } else if (!(status & LP_PSELECD)) { - printk("lp%d off-line\n", minor); + printk(KERN_INFO "lp%d off-line\n", minor); return -EIO; } else if (!(status & LP_PERRORP)) { - printk("lp%d printer error\n", minor); + printk(KERN_ERR "lp%d printer error\n", minor); return -EIO; } } @@ -492,6 +494,7 @@ int offset = 0; unsigned int testvalue = 0; int count = 0; + char buf[5]; if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) { printk("unable to get major %d for line printer\n", LP_MAJOR); @@ -510,7 +513,8 @@ LP_F(offset) |= LP_EXIST; lp_reset(offset); printk("lp%d at 0x%04x, ", offset,LP_B(offset)); - snarf_region(LP_B(offset), 3); + sprintf(buf,"lp%d",offset); + register_iomem(LP_B(offset), 3,buf); if (LP_IRQ(offset)) printk("using IRQ%d\n", LP_IRQ(offset)); else @@ -532,6 +536,7 @@ int offset = 0; unsigned int testvalue = 0; int count = 0; + char buf[5]; if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) { printk("unable to get major %d for line printer\n", LP_MAJOR); @@ -548,7 +553,8 @@ LP_F(offset) |= LP_EXIST; lp_reset(offset); printk("lp%d at 0x%04x, ", offset,LP_B(offset)); - snarf_region(LP_B(offset),3); + sprintf(buf,"lp%d",offset); + register_iomem(LP_B(offset),3,buf); if (LP_IRQ(offset)) printk("using IRQ%d\n", LP_IRQ(offset)); else diff -u --recursive --new-file v1.1.76/linux/drivers/char/serial.c linux/drivers/char/serial.c --- v1.1.76/linux/drivers/char/serial.c Thu Dec 22 06:53:15 1994 +++ linux/drivers/char/serial.c Sat Jan 7 12:57:56 1995 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -369,11 +370,15 @@ { struct tty_struct *tty = info->tty; unsigned char ch; + int ignored = 0; do { ch = serial_inp(info, UART_RX); - if (*status & info->ignore_status_mask) + if (*status & info->ignore_status_mask) { + if (++ignored > 100) + break; goto ignore_char; + } if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; tty->flip.count++; @@ -1462,6 +1467,7 @@ info->type = new_serial.type; info->close_delay = new_serial.close_delay; + release_region(info->port,8); if (change_port || change_irq) { /* * We need to shutdown the serial port at the old @@ -1472,6 +1478,9 @@ info->port = new_serial.port; info->hub6 = new_serial.hub6; } + if(info->type != PORT_UNKNOWN) + register_iomem(info->port,8,"serial(set)"); + check_and_exit: if (!info->port || !info->type) @@ -2344,6 +2353,7 @@ if ((status1 != 0xa5) || (status2 != 0x5a)) info->type = PORT_8250; } + register_iomem(info->port,8,"serial(auto)"); /* * Reset the UART. diff -u --recursive --new-file v1.1.76/linux/drivers/char/tpqic02.c linux/drivers/char/tpqic02.c --- v1.1.76/linux/drivers/char/tpqic02.c Tue Nov 1 19:50:44 1994 +++ linux/drivers/char/tpqic02.c Thu Jan 5 13:47:08 1995 @@ -212,6 +212,7 @@ #include #include #include +#include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v1.1.76/linux/drivers/char/tty_io.c Sun Jan 1 19:49:19 1995 +++ linux/drivers/char/tty_io.c Thu Jan 5 13:47:08 1995 @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -1442,6 +1443,8 @@ arg = get_fs_long((unsigned long *) arg); return tty_set_ldisc(tty, arg); case TIOCLINUX: + if (current->tty != tty && !suser()) + return -EPERM; retval = verify_area(VERIFY_READ, (void *) arg, 1); if (retval) return retval; diff -u --recursive --new-file v1.1.76/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c --- v1.1.76/linux/drivers/char/tty_ioctl.c Tue Nov 1 19:36:54 1994 +++ linux/drivers/char/tty_ioctl.c Thu Jan 5 13:55:40 1995 @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/char/vesa_blank.c linux/drivers/char/vesa_blank.c --- v1.1.76/linux/drivers/char/vesa_blank.c Sun Jan 1 19:49:19 1995 +++ linux/drivers/char/vesa_blank.c Wed Jan 4 19:11:02 1995 @@ -64,7 +64,7 @@ Patch (based on Linux Kernel revision 1.0) for handling the Power Saving feature of the new monitor generation. The code works on all these monitors (mine is a Smile 1506) and should run on *all* video adapter cards (change -some i/o-adresses), although tested only on two different VGA-cards: a +some i/o-addresses), although tested only on two different VGA-cards: a cheap Cirrus Logic (5428) and a miro Crystal 8S (S3-805). You can choose from two options: diff -u --recursive --new-file v1.1.76/linux/drivers/net/3c501.c linux/drivers/net/3c501.c --- v1.1.76/linux/drivers/net/3c501.c Tue Dec 6 12:37:33 1994 +++ linux/drivers/net/3c501.c Sat Jan 7 12:57:56 1995 @@ -14,6 +14,53 @@ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + + Fixed (again!) the missing interrupt locking on TX/RX shifting. + Alan Cox + + Some notes on this thing if you have to hack it. [Alan] + + 1] Some documentation is available from 3Com. Due to the boards age + standard responses when you ask for this will range from 'be serious' + to 'give it to a museum'. The documentation is incomplete and mostly + of historical interest anyway. + + 2] The basic system is a single buffer which can be used to receive or + transmit a packet. A third command mode exists when you are setting + things up. + + 3] If its transmitting its not receiving and vice versa. In fact the + time to get the board back into useful state after an operation is + quite large. + + 4] The driver works by keeping the board in receive mode waiting for a + packet to arrive. When one arrives it is copied out of the buffer + and delivered to the kernel. The card is reloaded and off we go. + + 5] When transmitting dev->tbusy is set and the card is reset (from + receive mode) [possibly losing a packet just received] to command + mode. A packet is loaded and transmit mode triggered. The interrupt + handler runs different code for transmit interrupts and can handle + returning to receive mode or retransmissions (yes you have to help + out with those too). + + Problems: + There are a wide variety of undocumented error returns from the card + and you basically have to kick the board and pray if they turn up. Most + only occur under extreme load or if you do something the board doesn't + like (eg touching a register at the wrong time). + + The driver is less efficient than it could be. It switches through + recieve mode even if more transmits are queued. If this worries you buy + a real ethernet card. + + The combination of slow receive restart and no real multicast + filter makes the board unusable with a kernel compiled for IP + multicasting in a real multicast environment. Thats down to the board, + but even with no multicast programs running a multicast IP kernel is + in group 224.0.0.1 and you will therefore be listening to all multicasts. + One nv conference running over that ethernet and you can give up. + */ static char *version = @@ -24,7 +71,6 @@ The 3c501 board. */ -#include #include #include #include @@ -183,7 +229,7 @@ return ENODEV; /* Grab the region so we can find the another board if autoIRQ fails. */ - snarf_region(ioaddr, EL1_IO_EXTENT); + register_iomem(ioaddr, EL1_IO_EXTENT,"3c501"); if (dev == NULL) dev = init_etherdev(0, sizeof(struct net_local), 0); @@ -218,9 +264,13 @@ if (autoirq) dev->irq = autoirq; - printk("%s: %s EtherLink at %#x, using %sIRQ %d, melting ethernet.\n", + printk("%s: %s EtherLink at %#x, using %sIRQ %d.\n", dev->name, mname, dev->base_addr, autoirq ? "auto":"assigned ", dev->irq); + +#ifdef CONFIG_IP_MULTICAST + printk("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n"); +#endif if (el_debug) printk("%s", version); @@ -272,6 +322,7 @@ { struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; + unsigned long flags; if (dev->tbusy) { if (jiffies - dev->trans_start < 20) { @@ -296,9 +347,17 @@ return 0; } + save_flags(flags); + /* Avoid incoming interrupts between us flipping tbusy and flipping + mode as the driver assumes tbusy is a faithful indicator of card + state */ + cli(); /* Avoid timer-based retransmission conflicts. */ if (set_bit(0, (void*)&dev->tbusy) != 0) + { + restore_flags(flags); printk("%s: Transmitter access conflict.\n", dev->name); + } else { int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN); unsigned char *buf = skb->data; @@ -306,14 +365,23 @@ lp->tx_pkt_start = gp_start; lp->collisions = 0; + /* + * Command mode with status cleared should [in theory] + * mean no more interrupts can be pending on the card. + */ outb(AX_SYS, AX_CMD); inb(RX_STATUS); inb(TX_STATUS); - outw(0x00, RX_BUF_CLR); /* Set rx packet area to 0. */ - outw(gp_start, GP_LOW); - outsb(DATAPORT,buf,skb->len); - outw(gp_start, GP_LOW); - outb(AX_XMIT, AX_CMD); /* Trigger xmit. */ + /* + * Turn interrupts back on while we spend a pleasant afternoon + * loading bytes into the board + */ + restore_flags(flags); + outw(0x00, RX_BUF_CLR); /* Set rx packet area to 0. */ + outw(gp_start, GP_LOW); /* aim - packet will be loaded into buffer start */ + outsb(DATAPORT,buf,skb->len); /* load buffer (usual thing each byte increments the pointer) */ + outw(gp_start, GP_LOW); /* the board reuses the same register */ + outb(AX_XMIT, AX_CMD); /* fire ... Trigger xmit. */ dev->trans_start = jiffies; } @@ -351,6 +419,11 @@ dev->interrupt = 1; if (dev->tbusy) { + + /* + * Board in transmit mode. + */ + int txsr = inb(TX_STATUS); if (el_debug > 6) @@ -358,12 +431,19 @@ inw(RX_LOW)); if ((axsr & 0x80) && (txsr & TX_READY) == 0) { + /* + * FIXME: is there a logic to whether to keep on trying or + * reset immediately ? + */ printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x" " gp=%03x rp=%03x.\n", dev->name, txsr, axsr, inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR)); dev->tbusy = 0; mark_bh(NET_BH); } else if (txsr & TX_16COLLISIONS) { + /* + * Timed out + */ if (el_debug) printk("%s: Transmit failed 16 times, ethernet jammed?\n", dev->name); @@ -372,6 +452,9 @@ } else if (txsr & TX_COLLISION) { /* Retrigger xmit. */ if (el_debug > 6) printk(" retransmitting after a collision.\n"); + /* + * Poor little chip can't reset its own start pointer + */ outb(AX_SYS, AX_CMD); outw(lp->tx_pkt_start, GP_LOW); outb(AX_XMIT, AX_CMD); @@ -379,26 +462,42 @@ dev->interrupt = 0; return; } else { + /* + * It worked.. we will now fall through and receive + */ lp->stats.tx_packets++; if (el_debug > 6) printk(" Tx succeeded %s\n", (txsr & TX_RDY) ? "." : "but tx is busy!"); + /* + * This is safe the interrupt is atomic WRT itself. + */ dev->tbusy = 0; - mark_bh(NET_BH); + mark_bh(NET_BH); /* In case more to transmit */ } } else { + + /* + * In receive mode. + */ + int rxsr = inb(RX_STATUS); if (el_debug > 5) printk(" rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS), inw(RX_LOW)); - /* Just reading rx_status fixes most errors. */ + /* + * Just reading rx_status fixes most errors. + */ if (rxsr & RX_MISSED) lp->stats.rx_missed_errors++; if (rxsr & RX_RUNT) { /* Handled to avoid board lock-up. */ lp->stats.rx_length_errors++; if (el_debug > 5) printk(" runt.\n"); } else if (rxsr & RX_GOOD) { + /* + * Receive worked. + */ el_receive(dev); } else { /* Nothing? Something is broken! */ if (el_debug > 2) @@ -410,6 +509,9 @@ printk(".\n"); } + /* + * Move into receive mode + */ outb(AX_RX, AX_CMD); outw(0x00, RX_BUF_CLR); inb(RX_STATUS); /* Be certain that interrupts are cleared. */ @@ -440,9 +542,17 @@ lp->stats.rx_over_errors++; return; } + + /* + * Command mode so we can empty the buffer + */ + outb(AX_SYS, AX_CMD); skb = alloc_skb(pkt_len, GFP_ATOMIC); + /* + * Start of frame + */ outw(0x00, GP_LOW); if (skb == NULL) { printk("%s: Memory squeeze, dropping packet.\n", dev->name); @@ -452,6 +562,12 @@ skb->len = pkt_len; skb->dev = dev; + /* + * The read incrememts through the bytes. The interrupt + * handler will fix the pointer when it returns to + * receive mode. + */ + insb(DATAPORT, skb->data, pkt_len); netif_rx(skb); @@ -544,10 +660,15 @@ 0, 0, 0, 0, 0x280, 5, 0, 0, 0, NULL, el1_probe }; + +int io=0x280; +int irq=5; int init_module(void) { + dev_3c501.irq=irq; + dev_3c501.base_addr=io; if (register_netdev(&dev_3c501) != 0) return -EIO; return 0; diff -u --recursive --new-file v1.1.76/linux/drivers/net/3c503.c linux/drivers/net/3c503.c --- v1.1.76/linux/drivers/net/3c503.c Sun Jan 1 16:22:34 1995 +++ linux/drivers/net/3c503.c Sat Jan 7 12:57:56 1995 @@ -25,7 +25,6 @@ static char *version = "3c503.c:v1.10 9/23/93 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; -#include #include #include #include @@ -173,7 +172,7 @@ return ENODEV; } - snarf_region(ioaddr, EL2_IO_EXTENT); + register_iomem(ioaddr, EL2_IO_EXTENT,"3c503"); if (dev == NULL) dev = init_etherdev(0, sizeof(struct ei_device), 0); diff -u --recursive --new-file v1.1.76/linux/drivers/net/3c505.c linux/drivers/net/3c505.c --- v1.1.76/linux/drivers/net/3c505.c Wed Aug 10 20:00:12 1994 +++ linux/drivers/net/3c505.c Thu Jan 5 13:55:40 1995 @@ -56,7 +56,6 @@ #define ELP_NEED_HARD_RESET 0 -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/net/3c507.c linux/drivers/net/3c507.c --- v1.1.76/linux/drivers/net/3c507.c Sun Oct 9 19:28:47 1994 +++ linux/drivers/net/3c507.c Sat Jan 7 12:57:56 1995 @@ -370,7 +370,7 @@ } /* We've committed to using the board, and can start filling in *dev. */ - snarf_region(ioaddr, EL16_IO_EXTENT); + register_iomem(ioaddr, EL16_IO_EXTENT,"3c507"); dev->base_addr = ioaddr; outb(0x01, ioaddr + MISC_CTRL); diff -u --recursive --new-file v1.1.76/linux/drivers/net/3c509.c linux/drivers/net/3c509.c --- v1.1.76/linux/drivers/net/3c509.c Tue Dec 6 12:37:33 1994 +++ linux/drivers/net/3c509.c Sat Jan 7 12:57:56 1995 @@ -224,7 +224,7 @@ dev->base_addr = ioaddr; dev->irq = irq; dev->if_port = if_port; - snarf_region(dev->base_addr, 16); + register_iomem(dev->base_addr, 16,"3c509"); { char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"}; diff -u --recursive --new-file v1.1.76/linux/drivers/net/8390.c linux/drivers/net/8390.c --- v1.1.76/linux/drivers/net/8390.c Tue Dec 6 12:37:33 1994 +++ linux/drivers/net/8390.c Thu Jan 5 13:55:40 1995 @@ -19,7 +19,6 @@ static char *version = "8390.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; -#include /* Braindamage remaining: @@ -30,7 +29,6 @@ The National Semiconductor LAN Databook, and the 3Com 3c503 databook. */ -#include #include #include #include @@ -139,6 +137,10 @@ return 1; } isr = inb(e8390_base+EN0_ISR); + if (dev->start == 0) { + printk("%s: xmit on stopped card\n", dev->name); + return 1; + } printk(KERN_DEBUG "%s: transmit timed out, TX status %#2x, ISR %#2x.\n", dev->name, txsr, isr); /* Does the 8390 thinks it has posted an interrupt? */ @@ -273,6 +275,11 @@ /* !!Assumption!! -- we stay in page 0. Don't break this. */ while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 && ++boguscount < 9) { + if (dev->start == 0) { + printk("%s: interrupt from stopped card\n", dev->name); + interrupts = 0; + break; + } if (interrupts & ENISR_RDC) { /* Ack meaningless DMA complete. */ outb_p(ENISR_RDC, e8390_base + EN0_ISR); diff -u --recursive --new-file v1.1.76/linux/drivers/net/MODULES linux/drivers/net/MODULES --- v1.1.76/linux/drivers/net/MODULES Mon Dec 12 21:01:09 1994 +++ linux/drivers/net/MODULES Sat Jan 7 12:57:44 1995 @@ -8,4 +8,7 @@ plip.o \ 8390.o \ slip.o \ - slhc.o + slhc.o \ + dummy.o \ + ewrk3.o \ + depca.o diff -u --recursive --new-file v1.1.76/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v1.1.76/linux/drivers/net/Makefile Mon Dec 5 00:07:40 1994 +++ linux/drivers/net/Makefile Wed Jan 4 21:16:05 1995 @@ -17,7 +17,7 @@ # The point of the makefile... -all: net.a modules +all: net.a Space.o: Space.c ../../include/linux/autoconf.h $(CC) $(CFLAGS) $(OPTS) -c $< diff -u --recursive --new-file v1.1.76/linux/drivers/net/ac3200.c linux/drivers/net/ac3200.c --- v1.1.76/linux/drivers/net/ac3200.c Wed Aug 10 19:50:28 1994 +++ linux/drivers/net/ac3200.c Thu Jan 5 13:55:40 1995 @@ -17,7 +17,6 @@ static char *version = "ac3200.c:v1.01 7/1/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/net/apricot.c linux/drivers/net/apricot.c --- v1.1.76/linux/drivers/net/apricot.c Wed Dec 7 07:21:24 1994 +++ linux/drivers/net/apricot.c Sat Jan 7 12:57:56 1995 @@ -21,7 +21,6 @@ static char *version = "apricot.c:v0.2 05/12/94\n"; -#include #include #include #include @@ -45,7 +44,7 @@ #ifndef HAVE_PORTRESERVE #define check_region(addr, size) 0 -#define snarf_region(addr, size) do ; while(0) +#define register_iomem(addr, size,name) do ; while(0) #endif #ifndef HAVE_ALLOC_SKB @@ -715,7 +714,7 @@ if(memcmp(eth_addr,"\x00\x00\x49",3)!= 0) return ENODEV; - snarf_region(ioaddr, APRICOT_TOTAL_SIZE); + register_iomem(ioaddr, APRICOT_TOTAL_SIZE,"apricot"); dev->base_addr = ioaddr; ether_setup(dev); diff -u --recursive --new-file v1.1.76/linux/drivers/net/at1700.c linux/drivers/net/at1700.c --- v1.1.76/linux/drivers/net/at1700.c Sun Nov 27 20:19:53 1994 +++ linux/drivers/net/at1700.c Sat Jan 7 12:57:56 1995 @@ -207,7 +207,7 @@ /* Grab the region so that we can find another board if the IRQ request fails. */ - snarf_region(ioaddr, AT1700_IO_EXTENT); + register_iomem(ioaddr, AT1700_IO_EXTENT,"at1700"); printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name, ioaddr, irq); diff -u --recursive --new-file v1.1.76/linux/drivers/net/atp.c linux/drivers/net/atp.c --- v1.1.76/linux/drivers/net/atp.c Sun Nov 27 20:19:53 1994 +++ linux/drivers/net/atp.c Sat Jan 7 12:57:56 1995 @@ -74,7 +74,6 @@ interpretations of the device registers. */ -#include /* Used only to override default values. */ #include #include #include @@ -114,7 +113,7 @@ #ifndef HAVE_PORTRESERVE #define check_region(ioaddr, size) 0 -#define snarf_region(ioaddr, size); do ; while (0) +#define register_iomem(ioaddr, size,name); do ; while (0) #endif /* use 0 for production, 1 for verification, >2 for debug */ diff -u --recursive --new-file v1.1.76/linux/drivers/net/auto_irq.c linux/drivers/net/auto_irq.c --- v1.1.76/linux/drivers/net/auto_irq.c Sun Oct 9 19:28:48 1994 +++ linux/drivers/net/auto_irq.c Thu Jan 5 13:55:40 1995 @@ -32,15 +32,12 @@ "auto_irq.c:v1.11 Donald Becker (becker@cesdis.gsfc.nasa.gov)"; #endif -/*#include */ -/*#include */ #include #include #include #include #include #include -/*#include */ struct device *irq2dev_map[16] = {0, 0, /* ... zeroed */}; diff -u --recursive --new-file v1.1.76/linux/drivers/net/de600.c linux/drivers/net/de600.c --- v1.1.76/linux/drivers/net/de600.c Tue Dec 6 12:37:33 1994 +++ linux/drivers/net/de600.c Thu Jan 5 13:55:40 1995 @@ -90,7 +90,6 @@ #endif unsigned int de600_debug = DE600_DEBUG; -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/net/de620.c linux/drivers/net/de620.c --- v1.1.76/linux/drivers/net/de620.c Tue Dec 6 12:37:33 1994 +++ linux/drivers/net/de620.c Thu Jan 5 13:55:40 1995 @@ -101,7 +101,6 @@ */ #endif -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/net/depca.c linux/drivers/net/depca.c --- v1.1.76/linux/drivers/net/depca.c Mon Dec 12 20:06:54 1994 +++ linux/drivers/net/depca.c Sat Jan 7 12:57:56 1995 @@ -137,6 +137,11 @@ To unload a module, turn off the associated interface 'ifconfig eth?? down' then 'rmmod depca'. + + [Alan Cox: Changed to split off the module values as ints for insmod + + you can now do insmod depca.c irq=7 io=0x200 ] + TO DO: ------ @@ -172,7 +177,6 @@ static char *version = "depca.c:v0.381 12/12/94 davies@wanton.lkg.dec.com\n"; #include -#include #include #include #include @@ -476,7 +480,7 @@ } #ifdef HAVE_PORTRESERVE - snarf_region(ioaddr, DEPCA_TOTAL_SIZE); + register_iomem(ioaddr, DEPCA_TOTAL_SIZE,"depca"); #endif /* @@ -578,7 +582,7 @@ memset(lp->tx_ring, 0, sizeof(struct depca_tx_head)*j); /* This should never happen. */ - if ((int)(lp->rx_ring) & 0x07) { + if ((long)(lp->rx_ring) & 0x07) { printk("\n **ERROR** DEPCA Rx and Tx descriptor rings not on a quadword boundary.\n"); return -ENXIO; } @@ -1546,12 +1550,22 @@ static struct device thisDepca = { " ", /* device name inserted by /linux/drivers/net/net_init.c */ 0, 0, 0, 0, - 0x200, 7, /* I/O address, IRQ <--- EDIT THIS LINE FOR YOUR CONFIGURATION */ + 0x200, 7, /* I/O address, IRQ */ 0, 0, 0, NULL, depca_probe }; + +/* + * This is a tweak to keep the insmod program happy. It can only + * set int values with var=value so we split these out. + */ + +int irq=7; /* EDIT THESE LINE FOR YOUR CONFIGURATION */ +int io=0x200; /* Or use the irq= io= options to insmod */ int init_module(void) { + thisDepca.irq=irq; + thisDepca.base_addr=io; if (register_netdev(&thisDepca) != 0) return -EIO; return 0; diff -u --recursive --new-file v1.1.76/linux/drivers/net/dummy.c linux/drivers/net/dummy.c --- v1.1.76/linux/drivers/net/dummy.c Tue Dec 20 09:58:46 1994 +++ linux/drivers/net/dummy.c Sat Jan 7 12:57:44 1995 @@ -31,7 +31,6 @@ /* To have statistics (just packets sent) define this */ #undef DUMMY_STATS -#include #include #include #include @@ -52,13 +51,33 @@ #include #include +#ifdef MODULE +#include +#include +#endif + static int dummy_xmit(struct sk_buff *skb, struct device *dev); #ifdef DUMMY_STATS static struct enet_statistics *dummy_get_stats(struct device *dev); #endif -int -dummy_init(struct device *dev) +#ifdef MODULE +static int dummy_open(struct device *dev) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int dummy_close(struct device *dev) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +#endif + + +int dummy_init(struct device *dev) { /* I commented this out as bootup is noisy enough anyway and this driver seems pretty reliable 8) 8) 8) */ @@ -72,6 +91,10 @@ memset(dev->priv, 0, sizeof(struct enet_statistics)); dev->get_stats = dummy_get_stats; #endif +#ifdef MODULE + dev->open = &dummy_open; + dev->stop = &dummy_close; +#endif /* Fill in the fields of the device structure with ethernet-generic values. */ ether_setup(dev); @@ -108,3 +131,45 @@ return stats; } #endif + +#ifdef MODULE +char kernel_version[] = UTS_RELEASE; + +static int dummy_probe(struct device *dev) +{ + dummy_init(dev); + return 0; +} + +static struct device dev_dummy = { + "dummy0\0 ", + 0, 0, 0, 0, + 0x0, 0, + 0, 0, 0, NULL, dummy_probe }; + +int init_module(void) +{ + /* Find a name for this unit */ + int ct= 1; + + while(dev_get(dev_dummy.name)!=NULL && ct<100) + { + sprintf(dev_dummy.name,"dummy%d",ct); + ct++; + } + + if (register_netdev(&dev_dummy) != 0) + return -EIO; + return 0; +} + +void cleanup_module(void) +{ + if (MOD_IN_USE) + printk("dummy: device busy, remove delayed\n"); + else + { + unregister_netdev(&dev_dummy); + } +} +#endif /* MODULE */ diff -u --recursive --new-file v1.1.76/linux/drivers/net/e2100.c linux/drivers/net/e2100.c --- v1.1.76/linux/drivers/net/e2100.c Wed Aug 10 20:00:12 1994 +++ linux/drivers/net/e2100.c Sat Jan 7 12:57:56 1995 @@ -34,7 +34,6 @@ static char *version = "e2100.c:v1.01 7/21/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; -#include #include #include #include @@ -148,7 +147,7 @@ return ENODEV; /* Grab the region so we can find a different board if IRQ select fails. */ - snarf_region(ioaddr, E21_IO_EXTENT); + register_iomem(ioaddr, E21_IO_EXTENT,"e2100"); /* Read the station address PROM. */ for (i = 0; i < 6; i++) diff -u --recursive --new-file v1.1.76/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c --- v1.1.76/linux/drivers/net/eexpress.c Thu Dec 22 09:04:09 1994 +++ linux/drivers/net/eexpress.c Sat Jan 7 12:57:56 1995 @@ -19,6 +19,7 @@ The statistics need to be updated correctly. Modularized by Pauline Middelink + Changed to support io= irq= by Alan Cox */ static char *version = @@ -362,7 +363,7 @@ } /* We've committed to using the board, and can start filling in *dev. */ - snarf_region(ioaddr, 16); + register_iomem(ioaddr, 16,"eexpress"); dev->base_addr = ioaddr; for (i = 0; i < 6; i++) { @@ -1003,10 +1004,16 @@ char kernel_version[] = UTS_RELEASE; static struct device dev_eexpress = { " " /*"eexpress"*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, express_probe }; + + +int irq=0; +int io=0; int init_module(void) { + dev_eexpress.base_addr=io; + dev_eexpress.irq=irq; if (register_netdev(&dev_eexpress) != 0) return -EIO; return 0; diff -u --recursive --new-file v1.1.76/linux/drivers/net/ewrk3.c linux/drivers/net/ewrk3.c --- v1.1.76/linux/drivers/net/ewrk3.c Mon Dec 12 22:09:05 1994 +++ linux/drivers/net/ewrk3.c Sat Jan 7 12:57:56 1995 @@ -78,6 +78,8 @@ 6) run the net startup bits for your new eth?? interface manually (usually /etc/rc.inet[12] at boot time). 7) enjoy! + + [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y] Note that autoprobing is not allowed in loadable modules - the system is already up and running and you're messing with interrupts. @@ -126,7 +128,6 @@ static char *version = "ewrk3.c:v0.31 12/5/94 davies@wanton.lkg.dec.com\n"; #include -#include #include #include #include @@ -351,7 +352,7 @@ if (!check_region(base_addr, EWRK3_IOP_INC)) { if (((mem_chkd >> ((base_addr - EWRK3_IO_BASE)/ EWRK3_IOP_INC))&0x01)==1) { if (DevicePresent(base_addr) == 0) { /* Is EWRK3 really here? */ - snarf_region(base_addr, EWRK3_IOP_INC); /* Register I/O region */ + register_iomem(base_addr, EWRK3_IOP_INC,"ewrk3"); /* Register I/O region */ status = ewrk3_hw_init(dev, base_addr); } else { printk("ewrk3_probe(): No device found\n"); @@ -1343,7 +1344,7 @@ ** Device found. Mark its (I/O) location for future reference. Only 24 ** EtherWORKS devices can exist between 0x100 and 0x3e0. */ - snarf_region(iobase, EWRK3_IOP_INC); + register_iomem(iobase, EWRK3_IOP_INC,"ewrk3"); if (num_ewrk3s > 0) { /* only gets here in autoprobe */ dev = alloc_device(dev, iobase); } else { @@ -1386,7 +1387,7 @@ ** EtherWORKS devices can exist in EISA space.... */ mem_chkd |= (0x01 << (i + 24)); - snarf_region(iobase, EWRK3_IOP_INC); + register_iomem(iobase, EWRK3_IOP_INC,"ewrk3"); if (num_ewrk3s > 0) { /* only gets here in autoprobe */ dev = alloc_device(dev, iobase); } else { @@ -1824,12 +1825,18 @@ static struct device thisEthwrk = { " ", /* device name inserted by /linux/drivers/net/net_init.c */ 0, 0, 0, 0, - 0x300, 5, /* I/O address, IRQ <--- EDIT THIS LINE FOR YOUR CONFIGURATION */ + 0x300, 5, /* I/O address, IRQ */ 0, 0, 0, NULL, ewrk3_probe }; + +int io=0x300; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */ +int irq=5; /* or use the insmod io= irq= options */ + int init_module(void) { + thisEthwrk.base_addr=io; + thisEthwrk.irq=irq; if (register_netdev(&thisEthwrk) != 0) return -EIO; return 0; diff -u --recursive --new-file v1.1.76/linux/drivers/net/hp-plus.c linux/drivers/net/hp-plus.c --- v1.1.76/linux/drivers/net/hp-plus.c Thu Dec 29 19:58:42 1994 +++ linux/drivers/net/hp-plus.c Sat Jan 7 12:57:56 1995 @@ -21,7 +21,6 @@ static char *version = "hp-plus.c:v1.10 9/24/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; -#include #include /* Important -- this inlines word moves. */ #include #include @@ -179,7 +178,7 @@ } /* Grab the region so we can find another board if something fails. */ - snarf_region(ioaddr, HP_IO_EXTENT); + register_iomem(ioaddr, HP_IO_EXTENT,"hp-plus"); /* Read the IRQ line. */ outw(HW_Page, ioaddr + HP_PAGING); diff -u --recursive --new-file v1.1.76/linux/drivers/net/hp.c linux/drivers/net/hp.c --- v1.1.76/linux/drivers/net/hp.c Tue Nov 22 15:40:12 1994 +++ linux/drivers/net/hp.c Sat Jan 7 12:57:56 1995 @@ -21,7 +21,6 @@ static char *version = "hp.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; -#include #include #include #include @@ -126,7 +125,7 @@ dev = init_etherdev(0, sizeof(struct ei_device), 0); /* Grab the region so we can find another board if something fails. */ - snarf_region(ioaddr, HP_IO_EXTENT); + register_iomem(ioaddr, HP_IO_EXTENT,"hp"); printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr); diff -u --recursive --new-file v1.1.76/linux/drivers/net/lance.c linux/drivers/net/lance.c --- v1.1.76/linux/drivers/net/lance.c Wed Nov 30 10:19:12 1994 +++ linux/drivers/net/lance.c Sat Jan 7 12:57:56 1995 @@ -365,7 +365,7 @@ printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); dev->base_addr = ioaddr; - snarf_region(ioaddr, LANCE_TOTAL_SIZE); + register_iomem(ioaddr, LANCE_TOTAL_SIZE,"lance"); /* Make certain the data structures used by the LANCE are aligned. */ dev->priv = (void *)(((int)dev->priv + 7) & ~7); diff -u --recursive --new-file v1.1.76/linux/drivers/net/loopback.c linux/drivers/net/loopback.c --- v1.1.76/linux/drivers/net/loopback.c Sun Oct 9 19:28:49 1994 +++ linux/drivers/net/loopback.c Wed Jan 4 21:16:05 1995 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -70,20 +71,11 @@ dev->tbusy = 0; -#if 1 - __asm__("cmpl $0,_intr_count\n\t" - "jne 1f\n\t" - "movl _bh_active,%%eax\n\t" - "testl _bh_mask,%%eax\n\t" - "je 1f\n\t" - "incl _intr_count\n\t" - "call _do_bottom_half\n\t" - "decl _intr_count\n" - "1:" - : - : - : "ax", "dx", "cx"); -#endif + if (!intr_count && (bh_active & bh_mask)) { + start_bh_atomic(); + do_bottom_half(); + end_bh_atomic(); + } return(0); } diff -u --recursive --new-file v1.1.76/linux/drivers/net/ne.c linux/drivers/net/ne.c --- v1.1.76/linux/drivers/net/ne.c Sun Nov 27 13:14:21 1994 +++ linux/drivers/net/ne.c Sat Jan 7 12:57:56 1995 @@ -23,7 +23,6 @@ static char *version = "ne.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; -#include #include #include #include @@ -260,7 +259,7 @@ dev->base_addr = ioaddr; - snarf_region(ioaddr, NE_IO_EXTENT); + register_iomem(ioaddr, NE_IO_EXTENT,"ne2000"); for(i = 0; i < ETHER_ADDR_LEN; i++) dev->dev_addr[i] = SA_prom[i]; diff -u --recursive --new-file v1.1.76/linux/drivers/net/ni52.c linux/drivers/net/ni52.c --- v1.1.76/linux/drivers/net/ni52.c Fri Aug 26 09:37:38 1994 +++ linux/drivers/net/ni52.c Sat Jan 7 12:57:56 1995 @@ -44,7 +44,6 @@ * 30.Sep.93: Added nop-chain .. driver now runs with only one Xmit-Buff, too (MH) */ -#include #include #include #include @@ -182,7 +181,7 @@ #ifndef HAVE_PORTRESERVE #define check_region(ioaddr, size) 0 -#define snarf_region(ioaddr, size); do ; while (0) +#define register_iomem(ioaddr, size,name); do ; while (0) #endif #define NI52_TOTAL_SIZE 16 @@ -389,7 +388,7 @@ printk("%s: Ni52 found at %#3x, ",dev->name,dev->base_addr); - snarf_region(ioaddr,NI52_TOTAL_SIZE); + register_iomem(ioaddr,NI52_TOTAL_SIZE,"ni52"); dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL); /* warning: we don't free it on errors */ diff -u --recursive --new-file v1.1.76/linux/drivers/net/ni65.c linux/drivers/net/ni65.c --- v1.1.76/linux/drivers/net/ni65.c Fri Aug 26 09:37:38 1994 +++ linux/drivers/net/ni65.c Sat Jan 7 12:57:56 1995 @@ -30,7 +30,6 @@ * known BUGS: 16MB limit */ -#include #include #include #include @@ -55,7 +54,7 @@ #ifndef HAVE_PORTRESERVE #define check_region(ioaddr, size) 0 -#define snarf_region(ioaddr, size); do ; while (0) +#define register_iomem(ioaddr, size,name); do ; while (0) #endif #ifndef NET_DEBUG @@ -244,7 +243,7 @@ irq2dev_map[dev->irq] = dev; /* Grab the region so we can find another board if autoIRQ fails. */ - snarf_region(ioaddr,NI65_TOTAL_SIZE); + register_iomem(ioaddr,NI65_TOTAL_SIZE,"ni65"); p = dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL); memset((char *) dev->priv,0,sizeof(struct priv)); diff -u --recursive --new-file v1.1.76/linux/drivers/net/plip.c linux/drivers/net/plip.c --- v1.1.76/linux/drivers/net/plip.c Fri Dec 16 13:56:43 1994 +++ linux/drivers/net/plip.c Sat Jan 7 12:57:56 1995 @@ -34,8 +34,6 @@ #endif "PLIP $Revision: 1.7 $ gniibe@mri.co.jp\n"; -#include - /* Sources: Ideas and protocols came from Russ Nelson's @@ -227,7 +225,7 @@ } } - snarf_region(PAR_DATA(dev), 3); + register_iomem(PAR_DATA(dev), 3,"plip"); /* Fill in the generic fields of the device structure. */ ether_setup(dev); diff -u --recursive --new-file v1.1.76/linux/drivers/net/sk_g16.c linux/drivers/net/sk_g16.c --- v1.1.76/linux/drivers/net/sk_g16.c Sun Nov 27 20:19:53 1994 +++ linux/drivers/net/sk_g16.c Sat Jan 7 12:57:57 1995 @@ -56,7 +56,6 @@ * - (Try to make it slightly faster.) */ -#include #include #include #include @@ -330,7 +329,7 @@ #ifndef HAVE_PORTRESERVE #define check_region(ioaddr, size) 0 -#define snarf_region(ioaddr, size); do ; while (0) +#define register_iomem(ioaddr, size,name); do ; while (0) #endif @@ -783,7 +782,7 @@ dev->dev_addr[5]); /* Grab the I/O Port region */ - snarf_region(ioaddr, ETHERCARD_TOTAL_SIZE); + register_iomem(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16"); /* Initialize device structure */ diff -u --recursive --new-file v1.1.76/linux/drivers/net/skeleton.c linux/drivers/net/skeleton.c --- v1.1.76/linux/drivers/net/skeleton.c Sun Nov 27 20:19:53 1994 +++ linux/drivers/net/skeleton.c Sat Jan 7 12:57:57 1995 @@ -247,7 +247,7 @@ #endif /* jumpered DMA */ /* Grab the region so we can find another board if autoIRQ fails. */ - snarf_region(ioaddr, NETCARD_IO_EXTENT); + register_iomem(ioaddr, NETCARD_IO_EXTENT,"skeleton"); /* Initialize the device structure. */ if (dev->priv == NULL) diff -u --recursive --new-file v1.1.76/linux/drivers/net/slip.c linux/drivers/net/slip.c --- v1.1.76/linux/drivers/net/slip.c Tue Dec 27 09:54:43 1994 +++ linux/drivers/net/slip.c Sat Jan 7 12:57:44 1995 @@ -3,7 +3,7 @@ * devices like TTY. It interfaces between a raw TTY, and the * kernel's INET protocol layers (via DDI). * - * Version: @(#)slip.c 0.8.2 11/29/94 + * Version: @(#)slip.c 0.8.3 12/24/94 * * Authors: Laurence Culhane, * Fred N. van Kempen, @@ -32,6 +32,9 @@ * ifconfig sl? up & down now works correctly. * Modularization. * Alan Cox : Oops - fix AX.25 buffer lengths + * Dmitry Gorodchanin : Even more cleanups. Preserve CSLIP + * statistics. Include CSLIP code only + * if it really needed. * * * @@ -49,6 +52,7 @@ */ #define CONFIG_SLIP_COMPRESSED #endif +/* Undef this, if you don't need 6bit encapsulation code in the driver */ #define CONFIG_SLIP_MODE_SLIP6 #include @@ -80,9 +84,9 @@ #ifdef MODULE -#define SLIP_VERSION "0.8.1-NET3.014-NEWTTY-MODULAR" +#define SLIP_VERSION "0.8.3-NET3.019-NEWTTY-MODULAR" #else -#define SLIP_VERSION "0.8.1-NET3.014-NEWTTY" +#define SLIP_VERSION "0.8.3-NET3.019-NEWTTY" #endif @@ -97,19 +101,6 @@ static void slip_unesc6(struct slip *sl, unsigned char c); #endif -/* Initialize a SLIP control block for use. */ -static void -sl_initialize(struct slip *sl, struct device *dev) -{ - memset(sl, 0, sizeof (struct slip)); - sl->magic = SLIP_MAGIC; - sl->dev = dev; - sl->mode = SL_MODE_DEFAULT; - dev->type = ARPHRD_SLIP + sl->mode; - if (dev->type == 260) { /* KISS */ - dev->type = ARPHRD_AX25; - } -} /* Find a free SLIP channel, and link in this `tty' line. */ static inline struct slip * @@ -145,7 +136,7 @@ { struct device *dev = sl->dev; unsigned char *xbuff, *rbuff, *oxbuff, *orbuff; -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP unsigned char *cbuff, *ocbuff; #endif int len; @@ -163,11 +154,11 @@ xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP cbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); #endif -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP if (xbuff == NULL || rbuff == NULL || cbuff == NULL) { #else if (xbuff == NULL || rbuff == NULL) { @@ -181,7 +172,7 @@ if (rbuff != NULL) { kfree(rbuff); } -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP if (cbuff != NULL) { kfree(cbuff); } @@ -195,7 +186,7 @@ sl->xbuff = xbuff; orbuff = sl->rbuff; sl->rbuff = rbuff; -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP ocbuff = sl->cbuff; sl->cbuff = cbuff; #endif @@ -233,7 +224,7 @@ if (orbuff != NULL) { kfree(orbuff); } -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP if (ocbuff != NULL) { kfree(ocbuff); } @@ -268,7 +259,7 @@ int count; count = sl->rcount; -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP if (sl->mode & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) { unsigned char c; if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) { @@ -290,6 +281,7 @@ if (!(sl->mode & SL_MODE_CSLIP)) { /* turn on header compression */ sl->mode |= SL_MODE_CSLIP; + sl->mode &= ~SL_MODE_ADAPTIVE; printk("%s: header compression turned on\n", sl->dev->name); } sl->rbuff[0] &= 0x4f; @@ -298,7 +290,7 @@ } } } -#endif /* CONFIG INET */ +#endif /* SL_INCLUDE_CSLIP */ skb = alloc_skb(count, GFP_ATOMIC); if (skb == NULL) { @@ -338,7 +330,7 @@ } p = icp; -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP if (sl->mode & SL_MODE_CSLIP) { len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1); } @@ -505,7 +497,7 @@ unsigned long len; if (sl->tty == NULL) { - return -ENXIO; + return -ENODEV; } /* @@ -532,7 +524,7 @@ if (sl->xbuff == NULL) { goto noxbuff; } -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP sl->cbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL); if (sl->cbuff == NULL) { goto nocbuff; @@ -556,20 +548,19 @@ sl->xbits = 0; #endif sl->flags &= (1 << SLF_INUSE); /* Clear ESCAPE & ERROR flags */ - sl->mode = SL_MODE_DEFAULT; /* Needed because address '0' is special */ if (dev->pa_addr == 0) { dev->pa_addr=ntohl(0xC0A80001); } dev->tbusy = 0; - dev->flags |= IFF_UP; +/* dev->flags |= IFF_UP; */ dev->start = 1; return 0; /* Cleanup */ -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP noslcomp: kfree(sl->cbuff); nocbuff: @@ -593,20 +584,34 @@ } sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); dev->tbusy = 1; - dev->flags &= ~IFF_UP; + dev->start = 0; + +/* dev->flags &= ~IFF_UP; */ /* Free all SLIP frame buffers. */ - kfree(sl->rbuff); + if (sl->rbuff) { + kfree(sl->rbuff); + } sl->rbuff = NULL; - kfree(sl->xbuff); + if (sl->xbuff) { + kfree(sl->xbuff); + } sl->xbuff = NULL; -#ifdef CONFIG_INET - kfree(sl->cbuff); +#ifdef SL_INCLUDE_CSLIP + /* Save CSLIP statistics */ + if (sl->slcomp) { + sl->rx_compressed += sl->slcomp->sls_i_compressed; + sl->rx_dropped += sl->slcomp->sls_i_tossed; + sl->tx_compressed += sl->slcomp->sls_o_compressed; + sl->tx_misses += sl->slcomp->sls_o_misses; + } + if (sl->cbuff) { + kfree(sl->cbuff); + } sl->cbuff = NULL; slhc_free(sl->slcomp); sl->slcomp = NULL; #endif - dev->start = 0; return 0; } @@ -671,6 +676,7 @@ slip_open(struct tty_struct *tty) { struct slip *sl = (struct slip *) tty->disc_data; + int err; /* First make sure we're not already connected. */ if (sl && sl->magic == SLIP_MAGIC) { @@ -691,9 +697,19 @@ tty->ldisc.flush_buffer(tty); } + /* Restore default settings */ + sl->mode = SL_MODE_DEFAULT; + sl->dev->type = ARPHRD_SLIP + sl->mode; +#ifdef CONFIG_AX25 + if (sl->dev->type == 260) { /* KISS */ + sl->dev->type = ARPHRD_AX25; + } +#endif /* Perform the low-level SLIP initialization. */ - (void) sl_open(sl->dev); - + if ((err = sl_open(sl->dev))) { + return err; + } + #ifdef MODULE MOD_INC_USE_COUNT; #endif @@ -720,7 +736,7 @@ } (void) dev_close(sl->dev); - + tty->disc_data = 0; sl->tty = NULL; sl_free(sl); @@ -735,31 +751,29 @@ { static struct enet_statistics stats; struct slip *sl = &sl_ctrl[dev->base_addr]; -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP struct slcompress *comp; #endif - if (! sl) { - return NULL; - } - memset(&stats, 0, sizeof(struct enet_statistics)); - stats.rx_packets = sl->rx_packets; - stats.tx_packets = sl->tx_packets; - stats.rx_dropped = sl->rx_dropped; - stats.tx_dropped = sl->tx_dropped; - stats.tx_errors = sl->tx_errors; - stats.rx_errors = sl->rx_errors; + stats.rx_packets = sl->rx_packets; + stats.tx_packets = sl->tx_packets; + stats.rx_dropped = sl->rx_dropped; + stats.tx_dropped = sl->tx_dropped; + stats.tx_errors = sl->tx_errors; + stats.rx_errors = sl->rx_errors; stats.rx_over_errors = sl->rx_over_errors; - -#ifdef CONFIG_INET +#ifdef SL_INCLUDE_CSLIP + stats.rx_fifo_errors = sl->rx_compressed; + stats.tx_fifo_errors = sl->tx_compressed; + stats.collisions = sl->tx_misses; comp = sl->slcomp; if (comp) { - stats.rx_fifo_errors = comp->sls_i_compressed; - stats.rx_dropped += comp->sls_i_tossed; - stats.tx_fifo_errors = comp->sls_o_compressed; - stats.collisions = comp->sls_o_misses; + stats.rx_fifo_errors += comp->sls_i_compressed; + stats.rx_dropped += comp->sls_i_tossed; + stats.tx_fifo_errors += comp->sls_o_compressed; + stats.collisions += comp->sls_o_misses; } #endif /* CONFIG_INET */ return (&stats); @@ -920,10 +934,7 @@ } #endif /* CONFIG_SLIP_MODE_SLIP6 */ - - #ifdef CONFIG_AX25 - int sl_set_mac_address(struct device *dev, void *addr) { @@ -945,7 +956,6 @@ memcpy(dev->dev_addr, addr, 7); return 0; } - #endif /* CONFIG_AX25 */ @@ -985,10 +995,16 @@ return -err; } tmp = get_fs_long((long *)arg); -#ifndef CONFIG_INET - if (tmp & SL_MODE_CSLIP) { +#ifndef SL_INCLUDE_CSLIP + if (tmp & (SL_MODE_CSLIP|SL_MODE_ADAPTIVE)) { return -EINVAL; } +#else + if ((tmp & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) == + (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) { + /* return -EINVAL; */ + tmp &= ~SL_MODE_ADAPTIVE; + } #endif #ifndef CONFIG_SLIP_MODE_SLIP6 if (tmp & SL_MODE_SLIP6) { @@ -1010,9 +1026,11 @@ #endif sl->mode = tmp; sl->dev->type = ARPHRD_SLIP+sl->mode; +#ifdef CONFIG_AX25 if (sl->dev->type == 260) { sl->dev->type = ARPHRD_AX25; } +#endif return 0; case SIOCSIFHWADDR: @@ -1055,7 +1073,7 @@ "" #endif ); -#if defined(CONFIG_INET) && !defined(MODULE) +#if defined(SL_INCLUDE_CSLIP) && !defined(MODULE) printk("CSLIP: code copyright 1989 Regents of the University of California\n"); #endif #ifdef CONFIG_AX25 @@ -1081,8 +1099,11 @@ } /* Set up the "SLIP Control Block". (And clear statistics) */ - sl_initialize(sl, dev); - + + memset(sl, 0, sizeof (struct slip)); + sl->magic = SLIP_MAGIC; + sl->dev = dev; + /* Finish setting up the DEVICE info. */ dev->mtu = SL_MTU; dev->hard_start_xmit = sl_xmit; @@ -1098,8 +1119,11 @@ #endif dev->hard_header_len = 0; dev->addr_len = 0; - dev->type = 0; + dev->type = ARPHRD_SLIP + SL_MODE_DEFAULT; #ifdef CONFIG_AX25 + if (sl->dev->type == 260) { + sl->dev->type = ARPHRD_AX25; + } memcpy(dev->broadcast, ax25_bcast, 7); /* Only activated in AX.25 mode */ memcpy(dev->dev_addr, ax25_test, 7); /* "" "" "" "" */ #endif diff -u --recursive --new-file v1.1.76/linux/drivers/net/slip.h linux/drivers/net/slip.h --- v1.1.76/linux/drivers/net/slip.h Tue Dec 27 09:54:43 1994 +++ linux/drivers/net/slip.h Sat Jan 7 13:05:14 1995 @@ -10,13 +10,26 @@ * Alan Cox : Added slip mtu field. * Matt Dillon : Printable slip (borrowed from net2e) * Alan Cox : Added SL_SLIP_LOTS - * Dmitry Gorodchanin : A lot of changes in the 'struct slip' + * Dmitry Gorodchanin : A lot of changes in the 'struct slip' + * Dmitry Gorodchanin : Added CSLIP statistics. * * Author: Fred N. van Kempen, */ #ifndef _LINUX_SLIP_H #define _LINUX_SLIP_H +#include + +#if defined(CONFIG_INET) && defined(CONFIG_SLIP_COMPRESSED) +# define SL_INCLUDE_CSLIP +#endif + +#ifdef SL_INCLUDE_CSLIP +# define SL_MODE_DEFAULT SL_MODE_ADAPTIVE +#else +# define SL_MODE_DEFAULT SL_MODE_SLIP +#endif + /* SLIP configuration. */ #ifndef SL_SLIP_LOTS #define SL_NRUNIT 4 /* number of SLIP channels */ @@ -38,7 +51,7 @@ /* Various fields. */ struct tty_struct *tty; /* ptr to TTY structure */ struct device *dev; /* easy for intr handling */ -#if defined(CONFIG_INET) +#ifdef SL_INCLUDE_CSLIP struct slcompress *slcomp; /* for header compression */ unsigned char *cbuff; /* compression buffer */ #endif @@ -81,19 +94,6 @@ #define SL_MODE_ADAPTIVE 8 }; -#ifdef CONFIG_INET -#ifdef CONFIG_SLIP_ADAPTIVE -#define SL_MODE_DEFAULT SL_MODE_ADAPTIVE -#else /* !CONFIG_SLIP_ADAPTIVE */ -#ifdef CONFIG_SLIP_COMPRESSED -#define SL_MODE_DEFAULT (SL_MODE_CSLIP | SL_MODE_ADAPTIVE) -#else /* !CONFIG_SLIP_COMPRESSED */ -#define SL_MODE_DEFAULT SL_MODE_SLIP -#endif /* CONFIG_SLIP_COMPRESSED */ -#endif /* CONFIG_SLIP_ADAPTIVE */ -#else /* !CONFIG_INET */ -#define SL_MODE_DEFAULT SL_MODE_SLIP -#endif /* CONFIG_INET */ #define SLIP_MAGIC 0x5302 diff -u --recursive --new-file v1.1.76/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c --- v1.1.76/linux/drivers/net/smc-ultra.c Wed Dec 7 09:15:34 1994 +++ linux/drivers/net/smc-ultra.c Sat Jan 7 12:57:57 1995 @@ -19,7 +19,6 @@ static char *version = "smc-ultra.c:v1.11 11/21/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; -#include #include #include #include @@ -148,7 +147,7 @@ /* OK, were are certain this is going to work. Setup the device. */ - snarf_region(ioaddr, 32); + register_iomem(ioaddr, 32,"smc-ultra"); /* The 8390 isn't at the base address, so fake the offset */ dev->base_addr = ioaddr+ULTRA_NIC_OFFSET; diff -u --recursive --new-file v1.1.76/linux/drivers/net/wd.c linux/drivers/net/wd.c --- v1.1.76/linux/drivers/net/wd.c Thu Dec 22 13:37:13 1994 +++ linux/drivers/net/wd.c Sat Jan 7 12:57:57 1995 @@ -20,7 +20,6 @@ static char *version = "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; -#include #include #include #include @@ -236,7 +235,7 @@ } /* OK, were are certain this is going to work. Setup the device. */ - snarf_region(ioaddr, WD_IO_EXTENT); + register_iomem(ioaddr, WD_IO_EXTENT,"wd"); ethdev_init(dev); ei_status.name = model_name; diff -u --recursive --new-file v1.1.76/linux/drivers/net/znet.c linux/drivers/net/znet.c --- v1.1.76/linux/drivers/net/znet.c Tue Dec 6 16:44:09 1994 +++ linux/drivers/net/znet.c Thu Jan 5 13:55:40 1995 @@ -62,7 +62,6 @@ functionality from the serial subsystem. */ -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c --- v1.1.76/linux/drivers/scsi/53c7,8xx.c Sat Dec 3 12:05:09 1994 +++ linux/drivers/scsi/53c7,8xx.c Wed Jan 4 21:16:05 1995 @@ -207,6 +207,23 @@ static int scan_scsis_buf_busy = 0; static char scan_scsis_buf[512]; + +/* + * Spl-levels are evil. We shouldn't emulate braindamage. + * Linus + */ +static int splx (int new_level) +{ + register int old_level, tmp; + save_flags(tmp); + old_level = (tmp & 0x200) ? 7 : 0; + if (new_level) + sti(); + else + cli(); + return old_level; +} + /* * TODO : * diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v1.1.76/linux/drivers/scsi/Makefile Sun Dec 4 19:16:28 1994 +++ linux/drivers/scsi/Makefile Wed Jan 4 21:16:05 1995 @@ -6,6 +6,13 @@ # unless its something special (ie not a .c file). # +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) -c -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< + AHA152X = -DDEBUG_AHA152X -DAUTOCONF ifeq (${CFLAGS},) diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c --- v1.1.76/linux/drivers/scsi/aha152x.c Tue Nov 29 10:07:13 1994 +++ linux/drivers/scsi/aha152x.c Sat Jan 7 12:57:57 1995 @@ -722,7 +722,7 @@ can_disconnect ? "enabled" : "disabled", can_doparity ? "enabled" : "disabled"); - snarf_region(port_base, TEST-SCSISEQ); /* Register */ + register_iomem(port_base, TEST-SCSISEQ,"aha152x"); /* Register */ /* not expecting any interrupts */ SETPORT(SIMODE0, 0); diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c --- v1.1.76/linux/drivers/scsi/aha1542.c Sun Jan 1 15:44:03 1995 +++ linux/drivers/scsi/aha1542.c Sat Jan 7 12:57:57 1995 @@ -1053,7 +1053,7 @@ aha1542_command(0, cmd, buffer, 512); } #endif - snarf_region(bases[indx], 4); /* Register the IO ports that we use */ + register_iomem(bases[indx], 4,"aha1542"); /* Register the IO ports that we use */ count++; continue; unregister: diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/aha1740.c linux/drivers/scsi/aha1740.c --- v1.1.76/linux/drivers/scsi/aha1740.c Sun Jan 1 15:44:03 1995 +++ linux/drivers/scsi/aha1740.c Sat Jan 7 12:57:57 1995 @@ -471,7 +471,7 @@ printk("Unable to allocate IRQ for adaptec controller.\n"); return 0; } - snarf_region(base, 0x5c); /* Reserve the space that we need to use */ + register_iomem(base, 0x5c,"aha1740"); /* Reserve the space that we need to use */ return 1; } diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/buslogic.c linux/drivers/scsi/buslogic.c --- v1.1.76/linux/drivers/scsi/buslogic.c Sun Jan 1 15:44:03 1995 +++ linux/drivers/scsi/buslogic.c Sat Jan 7 12:57:57 1995 @@ -82,6 +82,7 @@ #include #include #include +#include #include #include @@ -1335,8 +1336,8 @@ } #endif - snarf_region(bases[indx], 4); /* Register the IO ports that - we use */ + register_iomem(bases[indx], 4,"buslogic"); + /* Register the IO ports that we use */ count++; continue; unregister: diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c --- v1.1.76/linux/drivers/scsi/eata.c Tue Dec 20 10:23:14 1994 +++ linux/drivers/scsi/eata.c Sat Jan 7 12:57:57 1995 @@ -385,7 +385,7 @@ sh[j]->cmd_per_lun = MAX_CMD_PER_LUN; /* Register the I/O space that we use */ - snarf_region(sh[j]->io_port, REG_REGION); + register_iomem(sh[j]->io_port, REG_REGION,"eata"); memset(HD(j), 0, sizeof(struct hostdata)); HD(j)->subversion = subversion; diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c --- v1.1.76/linux/drivers/scsi/fdomain.c Mon Dec 12 21:47:12 1994 +++ linux/drivers/scsi/fdomain.c Sat Jan 7 12:57:57 1995 @@ -704,7 +704,7 @@ /* Log I/O ports with kernel */ - snarf_region( port_base, 0x10 ); + register_iomem( port_base, 0x10 ,"fdomain"); if ((bios_major == 3 && bios_minor >= 2) || bios_major < 0) { adapter_mask = 0x80; diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/in2000.c linux/drivers/scsi/in2000.c --- v1.1.76/linux/drivers/scsi/in2000.c Sun Jan 1 15:44:03 1995 +++ linux/drivers/scsi/in2000.c Sat Jan 7 12:57:57 1995 @@ -630,7 +630,7 @@ shpnt->io_port = base; shpnt->n_io_port = 12; shpnt->irq = irq_level; - snarf_region(base, 12); /* Prevent other drivers from using this space */ + register_iomem(base, 12,"in2000"); /* Prevent other drivers from using this space */ return 1; } diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/qlogic.c linux/drivers/scsi/qlogic.c --- v1.1.76/linux/drivers/scsi/qlogic.c Tue Dec 13 10:12:54 1994 +++ linux/drivers/scsi/qlogic.c Sat Jan 7 12:57:57 1995 @@ -71,7 +71,6 @@ /*----------------------------------------------------------------*/ -#include #include "../block/blk.h" /* to get disk capacity */ #include #include @@ -528,7 +527,7 @@ host->can_queue = 1; sti(); #endif - snarf_region( qbase , 0x10 ); + register_iomem( qbase , 0x10 ,"qlogic"); hreg = scsi_register( host , 0 ); /* no host data */ hreg->io_port = qbase; diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/scsi_module.c linux/drivers/scsi/scsi_module.c --- v1.1.76/linux/drivers/scsi/scsi_module.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/scsi_module.c Thu Jan 5 09:24:45 1995 @@ -0,0 +1,49 @@ +/* + * scsi_module.c Copyright (1994, 1995) Eric Youngdale. + * + * Support for loading low-level scsi drivers using the linux kernel loadable + * module interface. + * + * To use, the host adapter should first define and initialize the variable + * driver_template (datatype Scsi_Host_Template), and then include this file. + * This should also be wrapped in a #ifdef MODULE/#endif. + * + * The low -level driver must also define a release function which will + * free any irq assignments, release any dma channels, release any I/O + * address space that might be reserved, and otherwise clean up after itself. + * The idea is that the same driver should be able to be reloaded without + * any difficulty. This makes debugging new drivers easier, as you should + * be able to load the driver, test it, unload, modify and reload. + * + * One *very* important caveat. If the driver may need to do DMA on the + * ISA bus, you must have unchecked_isa_dma set in the device template, + * even if this might be changed during the detect routine. This is + * because the shpnt structure will be allocated in a special way so that + * it will be below the appropriate DMA limit - thus if your driver uses + * the hostdata field of shpnt, and the board must be able to access this + * via DMA, the shpnt structure must be in a DMA accessible region of + * memory. This comment would be relevant for something like the buslogic + * driver where there are many boards, only some of which do DMA onto the + * ISA bus. There is no convenient way of specifying whether the host + * needs to be in a ISA DMA accessible region of memory when you call + * scsi_register. + */ + +#include +#include "../../tools/version.h" + +char kernel_version[] = UTS_RELEASE; + +int init_module(void) { + driver_template.usage_count = &mod_use_count_; + scsi_register_module(MODULE_SCSI_HA, &driver_template); + return (driver_template.present == 0); +} + +void cleanup_module( void) { + if (MOD_IN_USE) { + printk(KERN_INFO __FILE__ ": module is in use, remove rejected\n"); + } + scsi_unregister_module(MODULE_SCSI_HA, &driver_template); +} + diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c --- v1.1.76/linux/drivers/scsi/sd.c Sun Jan 1 15:44:03 1995 +++ linux/drivers/scsi/sd.c Wed Jan 4 21:16:05 1995 @@ -498,9 +498,9 @@ if (contiguous && SCpnt->request.bh && - ((int) SCpnt->request.bh->b_data) + (SCpnt->request.nr_sectors << 9) - 1 > + ((long) SCpnt->request.bh->b_data) + (SCpnt->request.nr_sectors << 9) - 1 > ISA_DMA_THRESHOLD && SCpnt->host->unchecked_isa_dma) { - if(((int) SCpnt->request.bh->b_data) > ISA_DMA_THRESHOLD) + if(((long) SCpnt->request.bh->b_data) > ISA_DMA_THRESHOLD) bounce_buffer = (char *) scsi_malloc(bounce_size); if(!bounce_buffer) contiguous = 0; }; @@ -558,7 +558,7 @@ if(!bhp || !CONTIGUOUS_BUFFERS(bhp,bh) || !CLUSTERABLE_DEVICE(SCpnt) || (SCpnt->host->unchecked_isa_dma && - ((unsigned int) bh->b_data-1) == ISA_DMA_THRESHOLD)) { + ((unsigned long) bh->b_data-1) == ISA_DMA_THRESHOLD)) { if (count < SCpnt->host->sg_tablesize) count++; else break; }; @@ -597,7 +597,7 @@ sgpnt[count].length += bh->b_size; counted += bh->b_size >> 9; - if (((int) sgpnt[count].address) + sgpnt[count].length - 1 > + if (((long) sgpnt[count].address) + sgpnt[count].length - 1 > ISA_DMA_THRESHOLD && (SCpnt->host->unchecked_isa_dma) && !sgpnt[count].alt_address) { sgpnt[count].alt_address = sgpnt[count].address; @@ -640,7 +640,7 @@ if(bhp && CONTIGUOUS_BUFFERS(bh,bhp) && CLUSTERABLE_DEVICE(SCpnt)) { char * tmp; - if (((int) sgpnt[count].address) + sgpnt[count].length + + if (((long) sgpnt[count].address) + sgpnt[count].length + bhp->b_size - 1 > ISA_DMA_THRESHOLD && (SCpnt->host->unchecked_isa_dma) && !sgpnt[count].alt_address) continue; @@ -696,7 +696,7 @@ /* Now handle the possibility of DMA to addresses > 16Mb */ if(SCpnt->use_sg == 0){ - if (((int) buff) + (this_count << 9) - 1 > ISA_DMA_THRESHOLD && + if (((long) buff) + (this_count << 9) - 1 > ISA_DMA_THRESHOLD && (SCpnt->host->unchecked_isa_dma)) { if(bounce_buffer) buff = bounce_buffer; diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c --- v1.1.76/linux/drivers/scsi/seagate.c Wed Nov 30 17:17:39 1994 +++ linux/drivers/scsi/seagate.c Thu Jan 5 13:47:08 1995 @@ -52,6 +52,8 @@ #include #include #include +#include + #include "../block/blk.h" #include "scsi.h" #include "hosts.h" diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c --- v1.1.76/linux/drivers/scsi/sg.c Tue Dec 6 12:24:26 1994 +++ linux/drivers/scsi/sg.c Thu Jan 5 09:24:45 1995 @@ -116,6 +116,8 @@ } if (!scsi_generics[dev].users) scsi_generics[dev].timeout=SG_DEFAULT_TIMEOUT; + if (scsi_generics[dev].device->host->hostt->usage_count) + (*scsi_generics[dev].device->host->hostt->usage_count)++; scsi_generics[dev].users++; return 0; } @@ -124,6 +126,8 @@ { int dev=MINOR(inode->i_rdev); scsi_generics[dev].users--; + if (scsi_generics[dev].device->host->hostt->usage_count) + (*scsi_generics[dev].device->host->hostt->usage_count)--; scsi_generics[dev].exclude=0; wake_up(&scsi_generics[dev].generic_wait); } @@ -230,7 +234,11 @@ if ((i=verify_area(VERIFY_READ,buf,count))) return i; - if (countpending) @@ -283,6 +291,18 @@ size=COMMAND_SIZE(opcode); if (opcode >= 0xc0 && device->header.twelve_byte) size = 12; SCpnt->cmd_len = size; + /* + * Verify that the user has actually passed enough bytes for this command. + */ + if (count<(sizeof(struct sg_header) + size)) + { + device->pending=0; + wake_up(&device->write_wait); + sg_free(device->buff,device->buff_len); + device->buff = NULL; + return -EIO; + } + memcpy_fromfs(cmnd,buf,size); buf+=size; memcpy_fromfs(device->buff,buf,device->header.pack_len-size-sizeof(struct sg_header)); @@ -291,7 +311,8 @@ printk("do cmd\n"); #endif scsi_do_cmd (SCpnt,(void *) cmnd, - (void *) device->buff,amt,sg_command_done,device->timeout,SG_DEFAULT_RETRIES); + (void *) device->buff,device->header.pack_len-size-sizeof(struct sg_header), + sg_command_done,device->timeout,SG_DEFAULT_RETRIES); #ifdef DEBUG printk("done cmd\n"); #endif diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c --- v1.1.76/linux/drivers/scsi/sr.c Sun Jan 1 15:44:03 1995 +++ linux/drivers/scsi/sr.c Wed Jan 4 08:30:23 1995 @@ -278,114 +278,120 @@ * Much of this has do be done with vendor-specific SCSI-commands. * So I have to complete it step by step. Useful information is welcome. * - * Actually works: (should work ;-) + * Actually works: * - NEC: Detection and support of multisession CD's. Special handling * for XA-disks is not necessary. * * - TOSHIBA: setting density is done here now, mounting PhotoCD's should * work now without running the program "set_density" - * multisession-CD's are supported too. + * People reported, that it is nessesary to eject and reinsert + * the CD after the set-density call to get this working for + * old drives. + * And some very new drives don't need this call any more... + * Multisession CD's are supported too. * - * Gerd Knorr (mailto:kraxel@cs.tu-berlin.de, - * http://www.cs.tu-berlin.de/~kraxel/) + * Dec 1994: completely rewritten, uses kernel_scsi_ioctl() now + * + * kraxel@cs.tu-berlin.de (Gerd Knorr) */ -static void sr_photocd_done(Scsi_Cmnd *SCpnt) -{ - SCpnt->request.dev = 0xfffe; -} - static void sr_photocd(struct inode *inode) { unsigned long sector,min,sec,frame; - Scsi_Cmnd *SCpnt; - unsigned char scsi_cmd[10]; - unsigned char *buffer; + unsigned char buf[40]; int rc; + if (!suser()) { + /* I'm not the superuser, so SCSI_IOCTL_SEND_COMMAND is'nt allowed for me. + * That's why mpcd_sector will be initialized with zero, becauce I'm not + * able to get the right value. Nessesary only if access_count is 1, else + * no disk change happend since the last call of this function and we can + * keep the old value. + */ + if (1 == scsi_CDs[MINOR(inode->i_rdev)].device->access_count) + scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = 0; + return; + } + switch(scsi_CDs[MINOR(inode->i_rdev)].device->manufacturer) { case SCSI_MAN_NEC: +#ifdef DEBUG printk("sr_photocd: use NEC code\n"); - SCpnt = allocate_device(NULL, scsi_CDs[MINOR(inode->i_rdev)].device,1); - memset(scsi_cmd,0,10); - scsi_cmd[0] = 0xde; - scsi_cmd[1] = ((scsi_CDs[MINOR(inode->i_rdev)].device->lun) << 5) | 0x03; - scsi_cmd[2] = 0xb0; - buffer = (unsigned char*) scsi_malloc(512); - scsi_do_cmd(SCpnt, scsi_cmd, buffer, 0x16, - sr_photocd_done, SR_TIMEOUT, MAX_RETRIES); - while (SCpnt->request.dev != 0xfffe); - rc = SCpnt->result; - if (driver_byte(rc) != 0) { - printk("sr_photocd: oops, CD-ROM reports an error.\n"); - sector = 0; } - else { - min = (unsigned long)buffer[15]/16*10 + (unsigned long)buffer[15]%16; - sec = (unsigned long)buffer[16]/16*10 + (unsigned long)buffer[16]%16; - frame = (unsigned long)buffer[17]/16*10 + (unsigned long)buffer[17]%16; +#endif + memset(buf,0,40); + *((unsigned long*)buf) = 0; + *((unsigned long*)buf+1) = 0x16; + buf[8+0] = 0xde; + buf[8+1] = 0x03; + buf[8+2] = 0xb0; + rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device, + SCSI_IOCTL_SEND_COMMAND, buf); + if (rc != 0) { + printk("sr_photocd: ioctl error: %i\n",rc); + sector = 0; + } else { + min = (unsigned long)buf[8+15]/16*10 + (unsigned long)buf[8+15]%16; + sec = (unsigned long)buf[8+16]/16*10 + (unsigned long)buf[8+16]%16; + frame = (unsigned long)buf[8+17]/16*10 + (unsigned long)buf[8+17]%16; sector = min*60*75 + sec*75 + frame; +#ifdef DEBUG if (sector) { - printk("sr_photocd: multisession PhotoCD detected\n"); + printk("sr_photocd: multisession CD detected. start: %lu\n",sector); } +#endif } - scsi_free(buffer,512); - SCpnt->request.dev = -1; break; case SCSI_MAN_TOSHIBA: +#ifdef DEBUG printk("sr_photocd: use TOSHIBA code\n"); - +#endif + /* first I do a set_density-call (for reading XA-sectors) ... */ - SCpnt = allocate_device(NULL, scsi_CDs[MINOR(inode->i_rdev)].device,1); - memset(scsi_cmd,0,10); - scsi_cmd[0] = 0x15; - scsi_cmd[1] = ((scsi_CDs[MINOR(inode->i_rdev)].device->lun) << 5)|(1 << 4); - scsi_cmd[4] = 12; - buffer = (unsigned char*) scsi_malloc(512); - memset(buffer,0,512); - buffer[ 3] = 0x08; - buffer[ 4] = 0x83; - buffer[10] = 0x08; - scsi_do_cmd(SCpnt, scsi_cmd, buffer, 12, - sr_photocd_done, SR_TIMEOUT, MAX_RETRIES); - while (SCpnt->request.dev != 0xfffe); - rc = SCpnt->result; - if (driver_byte(rc) != 0) { - printk("sr_photocd: oops, CD-ROM reports an error.\n"); } - scsi_free(buffer,512); - SCpnt->request.dev = -1; + memset(buf,0,40); + *((unsigned long*)buf) = 12; + *((unsigned long*)buf+1) = 12; + buf[8+0] = 0x15; + buf[8+1] = (1 << 4); + buf[8+2] = 12; + buf[14+ 3] = 0x08; + buf[14+ 4] = 0x83; + buf[14+10] = 0x08; + rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device, + SCSI_IOCTL_SEND_COMMAND, buf); + if (rc != 0) { + printk("sr_photocd: ioctl error: %i\n",rc); + } /* ... and then I ask, if there is a multisession-Disk */ - SCpnt = allocate_device(NULL, scsi_CDs[MINOR(inode->i_rdev)].device,1); - memset(scsi_cmd,0,10); - scsi_cmd[0] = 0xc7; - scsi_cmd[1] = ((scsi_CDs[MINOR(inode->i_rdev)].device->lun) << 5) | 3; - buffer = (unsigned char*) scsi_malloc(512); - memset(buffer,0,512); - scsi_do_cmd(SCpnt, scsi_cmd, buffer, 4, - sr_photocd_done, SR_TIMEOUT, MAX_RETRIES); - while (SCpnt->request.dev != 0xfffe); - rc = SCpnt->result; - if (driver_byte(rc) != 0) { - printk("sr_photocd: oops, CD-ROM reports an error.\n"); - sector = 0; } - else { - min = (unsigned long)buffer[1]/16*10 + (unsigned long)buffer[1]%16; - sec = (unsigned long)buffer[2]/16*10 + (unsigned long)buffer[2]%16; - frame = (unsigned long)buffer[3]/16*10 + (unsigned long)buffer[3]%16; + memset(buf,0,40); + *((unsigned long*)buf) = 0; + *((unsigned long*)buf+1) = 4; + buf[8+0] = 0xc7; + buf[8+1] = 3; + if (rc != 0) { + printk("sr_photocd: ioctl error: %i\n",rc); + sector = 0; + } else { + min = (unsigned long)buf[8+1]/16*10 + (unsigned long)buf[8+1]%16; + sec = (unsigned long)buf[8+2]/16*10 + (unsigned long)buf[8+2]%16; + frame = (unsigned long)buf[8+3]/16*10 + (unsigned long)buf[8+3]%16; sector = min*60*75 + sec*75 + frame; if (sector) { sector -= CD_BLOCK_OFFSET; - printk("sr_photocd: multisession PhotoCD detected: %lu\n",sector); +#ifdef DEBUG + printk("sr_photocd: multisession CD detected: start: %lu\n",sector); +#endif } } - scsi_free(buffer,512); - SCpnt->request.dev = -1; break; + case SCSI_MAN_UNKNOWN: default: - printk("sr_photocd: there is no special photocd-code for this drive\n"); +#ifdef DEBUG + printk("sr_photocd: unknown drive, no special multisession code\n"); +#endif sector = 0; break; } @@ -414,7 +420,7 @@ if(scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size) get_sectorsize(MINOR(inode->i_rdev)); -#if 0 /* don't use for now - it doesn't seem to work for everybody */ +#if 1 /* don't use for now - it doesn't seem to work for everybody */ sr_photocd(inode); #endif @@ -566,10 +572,15 @@ and we have 2048 byte sectors. This code should work for buffers that are any multiple of 512 bytes long. */ - /* this is for support of multisession-CD's */ +#if 1 + /* Here we redirect the volume descriptor block of the CD-ROM. + * Nessesary for multisession CD's, until the isofs-rotines + * handle this via the CDROMMULTISESSION_SYS call + */ if (block >= 64 && block < 68) { block += scsi_CDs[dev].mpcd_sector*4; } - +#endif + SCpnt->use_sg = 0; if (SCpnt->host->sg_tablesize > 0 && diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c --- v1.1.76/linux/drivers/scsi/sr_ioctl.c Tue Jul 26 21:25:33 1994 +++ linux/drivers/scsi/sr_ioctl.c Wed Jan 4 08:30:23 1995 @@ -384,7 +384,52 @@ return -EINVAL; case CDROMREADMODE1: return -EINVAL; + + /* block-copy from ../block/sbpcd.c with some adjustments... */ + case CDROMMULTISESSION: /* tell start-of-last-session to user */ + { + struct cdrom_multisession ms_info; + long lba; + + err = verify_area(VERIFY_READ, (void *) arg, + sizeof(struct cdrom_multisession)); + if (err) return (err); + + memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession)); + if (ms_info.addr_format==CDROM_MSF) { /* MSF-bin requested */ + lba = scsi_CDs[target].mpcd_sector+CD_BLOCK_OFFSET; + ms_info.addr.msf.minute = lba / (CD_SECS*CD_FRAMES); + lba %= CD_SECS*CD_FRAMES; + ms_info.addr.msf.second = lba / CD_FRAMES; + ms_info.addr.msf.frame = lba % CD_FRAMES; + } else if (ms_info.addr_format==CDROM_LBA) /* lba requested */ + ms_info.addr.lba=scsi_CDs[target].mpcd_sector; + else return (-EINVAL); + + if (scsi_CDs[target].mpcd_sector) + ms_info.xa_flag=1; /* valid redirection address */ + else + ms_info.xa_flag=0; /* invalid redirection address */ + + err=verify_area(VERIFY_WRITE,(void *) arg, + sizeof(struct cdrom_multisession)); + if (err) return (err); + + memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession)); + return (0); + } + + case CDROMMULTISESSION_SYS: /* tell start-of-last-session to kernel */ + { + long *p_lba; + + if(!suser()) return -EACCES; + p_lba =(long*)arg; + *p_lba=scsi_CDs[target].mpcd_sector; + return (0); + } + case BLKRASET: if(!suser()) return -EACCES; if(!inode->i_rdev) return -EINVAL; diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c --- v1.1.76/linux/drivers/scsi/u14-34f.c Tue Dec 20 10:23:14 1994 +++ linux/drivers/scsi/u14-34f.c Sat Jan 7 12:57:57 1995 @@ -352,7 +352,7 @@ if (sh[j]->base == 0) outb(CMD_ENA_INTR, sh[j]->io_port + REG_SYS_MASK); /* Register the I/O space that we use */ - snarf_region(sh[j]->io_port, REG_REGION); + register_iomem(sh[j]->io_port, REG_REGION,"u14-34f"); memset(HD(j), 0, sizeof(struct hostdata)); HD(j)->heads = mapping_table[config_2.mapping_mode].heads; diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/ultrastor.c linux/drivers/scsi/ultrastor.c --- v1.1.76/linux/drivers/scsi/ultrastor.c Thu Dec 29 09:24:08 1994 +++ linux/drivers/scsi/ultrastor.c Sat Jan 7 12:57:57 1995 @@ -438,7 +438,8 @@ /* All above tests passed, must be the right thing. Get some useful info. */ - snarf_region(config.port_address, 0x0c); /* Register the I/O space that we use */ + register_iomem(config.port_address, 0x0c,"ultrastor"); + /* Register the I/O space that we use */ *(char *)&config_1 = inb(CONFIG(config.port_address + 0)); *(char *)&config_2 = inb(CONFIG(config.port_address + 1)); diff -u --recursive --new-file v1.1.76/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c --- v1.1.76/linux/drivers/scsi/wd7000.c Tue Nov 29 10:07:11 1994 +++ linux/drivers/scsi/wd7000.c Sat Jan 7 12:57:57 1995 @@ -606,7 +606,7 @@ save_flags(flags); cli(); while (busy) { /* someone else is allocating */ - sti(); + sti(); /* Yes this is really needed here */ now = jiffies; while (jiffies == now) /* wait a jiffy */; cli(); } @@ -615,7 +615,7 @@ while (freescbs < needed) { timeout = jiffies + WAITnexttimeout; do { - sti(); + sti(); /* Yes this is really needed here */ now = jiffies; while (jiffies == now) /* wait a jiffy */; cli(); } while (freescbs < needed && jiffies <= timeout); @@ -1162,7 +1162,7 @@ printk("using IO %xh IRQ %d DMA %d.\n", host->iobase, host->irq, host->dma); - snarf_region(host->iobase, 4); /* Register our ports */ + register_iomem(host->iobase, 4,"wd7000"); /* Register our ports */ /* * For boards before rev 6.0, scatter/gather isn't supported. */ diff -u --recursive --new-file v1.1.76/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c --- v1.1.76/linux/drivers/sound/dmabuf.c Tue Aug 23 09:48:52 1994 +++ linux/drivers/sound/dmabuf.c Wed Jan 4 19:17:57 1995 @@ -38,7 +38,7 @@ DEFINE_WAIT_QUEUES (dev_sleeper[MAX_AUDIO_DEV], dev_sleep_flag[MAX_AUDIO_DEV]); static struct dma_buffparms dmaps[MAX_AUDIO_DEV] = -{0}; /* +{{0}}; /* * Primitive way to allocate * such a large array. * Needs dynamic run-time allocation. diff -u --recursive --new-file v1.1.76/linux/fs/Makefile linux/fs/Makefile --- v1.1.76/linux/fs/Makefile Sat Dec 3 12:01:25 1994 +++ linux/fs/Makefile Thu Jan 5 09:24:45 1995 @@ -11,44 +11,56 @@ ifdef CONFIG_MINIX_FS FS_SUBDIRS := $(FS_SUBDIRS) minix +else +MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) minix endif + ifdef CONFIG_EXT_FS FS_SUBDIRS := $(FS_SUBDIRS) ext endif + ifdef CONFIG_EXT2_FS FS_SUBDIRS := $(FS_SUBDIRS) ext2 endif + ifdef CONFIG_MSDOS_FS FS_SUBDIRS := $(FS_SUBDIRS) msdos else MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) msdos endif + ifdef CONFIG_PROC_FS FS_SUBDIRS := $(FS_SUBDIRS) proc endif + ifdef CONFIG_ISO9660_FS FS_SUBDIRS := $(FS_SUBDIRS) isofs else MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) isofs endif + ifdef CONFIG_NFS_FS FS_SUBDIRS := $(FS_SUBDIRS) nfs endif + ifdef CONFIG_XIA_FS FS_SUBDIRS := $(FS_SUBDIRS) xiafs else MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) xiafs endif + ifdef CONFIG_UMSDOS_FS FS_SUBDIRS := $(FS_SUBDIRS) umsdos else MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) umsdos endif + ifdef CONFIG_SYSV_FS FS_SUBDIRS := $(FS_SUBDIRS) sysv else MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) sysv endif + ifdef CONFIG_HPFS_FS FS_SUBDIRS := $(FS_SUBDIRS) hpfs endif @@ -70,7 +82,7 @@ block_dev.o stat.o exec.o pipe.o namei.o fcntl.o ioctl.o \ select.o fifo.o locks.o filesystems.o dcache.o $(BINFMTS) -all: fs.o filesystems.a modules modules_fs +all: fs.o filesystems.a fs.o: $(OBJS) $(LD) -r -o fs.o $(OBJS) @@ -81,32 +93,8 @@ test ! -d $$i || \ { $(MAKE) -C $$i; $(AR) rcs filesystems.a $$i/$$i.o; }; done -ifdef MODULES - -modules: - $(MAKE) CFLAGS="$(CFLAGS) -DMODULE" $(MODULES) - (cd ../modules;for i in $(MODULES); do ln -sf ../fs/$$i .; done) - -else - modules: - -endif - -ifdef MODULE_FS_SUBDIRS - -modules_fs: - set -e; for i in $(MODULE_FS_SUBDIRS); do \ - test ! -d $$i || \ - { $(MAKE) -C $$i; }; done - (cd ../modules; \ - for i in $(MODULE_FS_SUBDIRS); do ln -sf ../fs/$$i/$$i.o .; done) - -else - -modules_fs: - -endif + set -e; for i in $(MODULE_FS_SUBDIRS); do $(MAKE) -C $$i modules; done depend dep: $(CPP) -M *.c > .depend diff -u --recursive --new-file v1.1.76/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c --- v1.1.76/linux/fs/binfmt_elf.c Mon Dec 12 21:49:38 1994 +++ linux/fs/binfmt_elf.c Wed Jan 4 21:16:05 1995 @@ -58,19 +58,19 @@ be in memory */ -static void padzero(int elf_bss){ - unsigned int fpnt, nbyte; +static void padzero(unsigned long elf_bss) +{ + unsigned long 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++); - }; - }; + nbyte = elf_bss & (PAGE_SIZE-1); + if (nbyte) { + nbyte = PAGE_SIZE - nbyte; + verify_area(VERIFY_WRITE, (void *) elf_bss, nbyte); + fpnt = elf_bss; + do { + put_fs_byte(0, fpnt++); + } while (--nbyte); + } } unsigned long * create_elf_tables(char * p,int argc,int envc,struct elfhdr * exec, unsigned int load_addr, int ibcs) @@ -501,7 +501,7 @@ /* 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); + bprm->p += setup_arg_pages(0, bprm->page); current->mm->start_stack = bprm->p; /* Now we do a little grungy work by mmaping the ELF image into @@ -637,8 +637,7 @@ 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 */ + start_thread(regs, elf_entry, bprm->p); if (current->flags & PF_PTRACED) send_sig(SIGTRAP, current, 0); #ifndef CONFIG_BINFMT_ELF diff -u --recursive --new-file v1.1.76/linux/fs/buffer.c linux/fs/buffer.c --- v1.1.76/linux/fs/buffer.c Mon Dec 19 17:34:22 1994 +++ linux/fs/buffer.c Thu Jan 5 13:55:40 1995 @@ -16,7 +16,6 @@ * invalidate changed floppy-disk-caches. */ -#include #include #include #include @@ -1451,7 +1450,7 @@ } while (tmp != bh); tmp = bh; - while((unsigned int) tmp->b_data & (PAGE_SIZE - 1)) + while((unsigned long) tmp->b_data & (PAGE_SIZE - 1)) tmp = tmp->b_this_page; /* This is the buffer at the head of the page */ @@ -1740,7 +1739,7 @@ * the tuning parameters. We would want to verify each parameter, however, * to make sure that it is reasonable. */ -asmlinkage int sys_bdflush(int func, int data) +asmlinkage int sys_bdflush(int func, long data) { int i, error; int ndirty; diff -u --recursive --new-file v1.1.76/linux/fs/exec.c linux/fs/exec.c --- v1.1.76/linux/fs/exec.c Wed Nov 30 17:17:39 1994 +++ linux/fs/exec.c Thu Jan 5 09:24:45 1995 @@ -43,6 +43,8 @@ #include #include +#include + asmlinkage int sys_exit(int exit_code); asmlinkage int sys_brk(unsigned long); @@ -50,6 +52,8 @@ static int load_aout_library(int fd); static int aout_core_dump(long signr, struct pt_regs * regs); +extern void dump_thread(struct pt_regs *, struct user *); + /* * Here are the actual binaries that will be accepted: * add more with "register_binfmt()".. @@ -164,8 +168,7 @@ unsigned short fs; int has_dumped = 0; char corefile[6+sizeof(current->comm)]; - int i; - register int dump_start, dump_size; + unsigned long dump_start, dump_size; struct user dump; if (!current->dumpable) @@ -206,44 +209,22 @@ if (!file.f_op->write) goto close_coredump; has_dumped = 1; -/* 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; + strncpy(dump.u_comm, current->comm, sizeof(current->comm)); + dump.u_ar0 = (struct pt_regs *)(((unsigned long)(&dump.regs)) - ((unsigned long)(&dump))); + dump.signal = signr; + dump_thread(regs, &dump); + /* If the size of the dump file exceeds the rlimit, then see what would happen if we wrote the stack, but not the data area. */ if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE > current->rlim[RLIMIT_CORE].rlim_cur) dump.u_dsize = 0; + /* Make sure we have enough room to write the stack and data areas. */ if ((dump.u_ssize+1) * PAGE_SIZE > current->rlim[RLIMIT_CORE].rlim_cur) dump.u_ssize = 0; - strncpy(dump.u_comm, current->comm, sizeof(current->comm)); - dump.u_ar0 = (struct pt_regs *)(((int)(&dump.regs)) -((int)(&dump))); - dump.signal = signr; - 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; - } + set_fs(KERNEL_DS); /* struct user */ DUMP_WRITE(&dump,sizeof(dump)); @@ -450,7 +431,7 @@ return p; } -unsigned long change_ldt(unsigned long text_size,unsigned long * page) +unsigned long setup_arg_pages(unsigned long text_size,unsigned long * page) { unsigned long code_limit,data_limit,code_base,data_base; int i; @@ -551,20 +532,7 @@ mpnt = mpnt1; } - /* Flush the old ldt stuff... */ - if (current->ldt) { - free_page((unsigned long) current->ldt); - current->ldt = NULL; - for (i=1 ; idebugreg[i] = 0; + flush_thread(); if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || !permission(bprm->inode,MAY_READ)) @@ -598,8 +566,6 @@ int retval; int sh_bang = 0; - if (regs->cs != USER_CS) - return -EINVAL; bprm.p = PAGE_SIZE*MAX_ARG_PAGES-4; for (i=0 ; imm->start_brk, current->mm->brk); - p += change_ldt(ex.a_text,bprm->page); + p += setup_arg_pages(ex.a_text,bprm->page); p -= MAX_ARG_PAGES*PAGE_SIZE; p = (unsigned long)create_tables((char *)p, bprm->argc, bprm->envc, current->personality != PER_LINUX); current->mm->start_stack = p; - regs->eip = ex.a_entry; /* eip, magic happens :-) */ - regs->esp = p; /* stack pointer */ + start_thread(regs, ex.a_entry, p); if (current->flags & PF_PTRACED) send_sig(SIGTRAP, current, 0); return 0; diff -u --recursive --new-file v1.1.76/linux/fs/isofs/Makefile linux/fs/isofs/Makefile --- v1.1.76/linux/fs/isofs/Makefile Wed Nov 30 22:03:47 1994 +++ linux/fs/isofs/Makefile Wed Jan 4 21:16:05 1995 @@ -23,6 +23,7 @@ isofs.o: $(OBJS) $(LD) -r -o isofs.o $(OBJS) +modules: isofs.o dep: $(CPP) -M *.c > .depend diff -u --recursive --new-file v1.1.76/linux/fs/isofs/inode.c linux/fs/isofs/inode.c --- v1.1.76/linux/fs/isofs/inode.c Tue Dec 27 10:05:46 1994 +++ linux/fs/isofs/inode.c Thu Jan 5 09:24:45 1995 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,7 @@ char cruft; unsigned char conversion; unsigned int blocksize; + mode_t mode; gid_t gid; uid_t uid; }; @@ -76,6 +78,7 @@ popt->cruft = 'n'; popt->conversion = 'a'; popt->blocksize = 1024; + popt->mode = S_IRUGO; popt->gid = 0; popt->uid = 0; if (!options) return 1; @@ -108,6 +111,7 @@ } else if (value && (!strcmp(this_char,"block") || + !strcmp(this_char,"mode") || !strcmp(this_char,"uid") || !strcmp(this_char,"gid"))) { char * vpnt = value; @@ -130,6 +134,9 @@ case 'g': popt->gid = ivalue; break; + case 'm': + popt->mode = ivalue; + break; } } else return 0; @@ -140,11 +147,15 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, int silent) { - struct buffer_head *bh; + struct buffer_head *bh=NULL; int iso_blknum; unsigned int blocksize_bits; int high_sierra; int dev=s->s_dev; + int i; + unsigned int vol_desc_start; + struct inode inode_fake; + extern struct file_operations * get_blkfops(unsigned int); struct iso_volume_descriptor *vdp; struct hs_volume_descriptor *hdp; @@ -184,7 +195,25 @@ s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */ - for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) { + /* + * look if the driver can tell the multi session redirection value + * + */ + vol_desc_start=0; + inode_fake.i_rdev=dev; + i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake, + NULL, + CDROMMULTISESSION_SYS, + (unsigned long) &vol_desc_start); +#if 0 + printk("isofs.inode: CDROMMULTISESSION_SYS rc=%d\n",i); + printk("isofs.inode: vol_desc_start = %d\n", vol_desc_start); +#endif 0 + if (i!=0) vol_desc_start=0; + for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100; iso_blknum++) { +#if 0 + printk("isofs.inode: iso_blknum=%d\n", iso_blknum); +#endif 0 if (!(bh = bread(dev, iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits), opt.blocksize))) { s->s_dev=0; printk("isofs_read_super: bread failed, dev 0x%x iso_blknum %d\n", @@ -298,6 +327,11 @@ s->u.isofs_sb.s_cruft = opt.cruft; s->u.isofs_sb.s_uid = opt.uid; s->u.isofs_sb.s_gid = opt.gid; + /* + * It would be incredibly stupid to allow people to mark every file on the disk + * as suid, so we merely allow them to set the default permissions. + */ + s->u.isofs_sb.s_mode = opt.mode & 0777; s->s_blocksize = opt.blocksize; s->s_blocksize_bits = blocksize_bits; s->s_mounted = iget(s, isonum_733 (rootp->extent) << s -> u.isofs_sb.s_log_zone_size); @@ -391,9 +425,6 @@ raw_inode = ((struct iso_directory_record *) pnt); } - inode->i_mode = S_IRUGO; /* Everybody gets to read the file. */ - inode->i_nlink = 1; - if (raw_inode->flags[-high_sierra] & 2) { inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR; inode->i_nlink = 1; /* Set to 1. We know there are 2, but @@ -402,7 +433,7 @@ easier to give 1 which tells find to do it the hard way. */ } else { - inode->i_mode = S_IRUGO; /* Everybody gets to read the file. */ + inode->i_mode = inode->i_sb->u.isofs_sb.s_mode; /* Everybody gets to read the file. */ inode->i_nlink = 1; inode->i_mode |= S_IFREG; /* If there are no periods in the name, then set the execute permission bit */ diff -u --recursive --new-file v1.1.76/linux/fs/isofs/rock.c linux/fs/isofs/rock.c --- v1.1.76/linux/fs/isofs/rock.c Sun Nov 20 19:26:07 1994 +++ linux/fs/isofs/rock.c Thu Jan 5 13:55:40 1995 @@ -5,7 +5,6 @@ * * Rock Ridge Extensions to iso9660 */ -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/fs/minix/Makefile linux/fs/minix/Makefile --- v1.1.76/linux/fs/minix/Makefile Wed Dec 1 14:44:15 1993 +++ linux/fs/minix/Makefile Thu Jan 5 09:24:45 1995 @@ -7,6 +7,10 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... +ifndef CONFIG_MINIX_FS +CFLAGS := $(CFLAGS) -DMODULE +endif + .c.s: $(CC) $(CFLAGS) -S $< .c.o: diff -u --recursive --new-file v1.1.76/linux/fs/minix/inode.c linux/fs/minix/inode.c --- v1.1.76/linux/fs/minix/inode.c Wed Nov 2 12:47:08 1994 +++ linux/fs/minix/inode.c Sat Jan 7 13:02:02 1995 @@ -4,6 +4,14 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#ifdef MODULE +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include #include #include @@ -63,6 +71,7 @@ brelse(sb->u.minix_sb.s_zmap[i]); brelse (sb->u.minix_sb.s_sbh); unlock_super(sb); + MOD_DEC_USE_COUNT; return; } @@ -207,6 +216,7 @@ else if (s->u.minix_sb.s_mount_state & MINIX_ERROR_FS) printk ("MINIX-fs: mounting file system with errors, " "running fsck is recommended.\n"); + MOD_INC_USE_COUNT; return s; } @@ -511,3 +521,30 @@ brelse (bh); return err; } + +#ifdef MODULE + +char kernel_version[] = UTS_RELEASE; + +static struct file_system_type minix_fs_type = { + minix_read_super, "minix", 1, NULL +}; + +int init_module(void) +{ + register_filesystem(&minix_fs_type); + return 0; +} + +void cleanup_module(void) +{ + if (MOD_IN_USE) + printk("ne: device busy, remove delayed\n"); + else + { + unregister_filesystem(&minix_fs_type); + } +} + +#endif + diff -u --recursive --new-file v1.1.76/linux/fs/msdos/Makefile linux/fs/msdos/Makefile --- v1.1.76/linux/fs/msdos/Makefile Tue Dec 27 08:37:13 1994 +++ linux/fs/msdos/Makefile Wed Jan 4 21:16:05 1995 @@ -23,6 +23,7 @@ msdos.o: $(OBJS) $(LD) -r -o msdos.o $(OBJS) +modules: msdos.o dep: $(CPP) -M *.c > .depend diff -u --recursive --new-file v1.1.76/linux/fs/nfs/inode.c linux/fs/nfs/inode.c --- v1.1.76/linux/fs/nfs/inode.c Sun Oct 23 19:35:37 1994 +++ linux/fs/nfs/inode.c Sat Jan 7 12:57:54 1995 @@ -4,11 +4,22 @@ * Copyright (C) 1992 Rick Sladkey * * nfs inode and superblock handling functions + * + * Modularised by Alan Cox , while hacking some + * experimental NFS changes. Modularisation taken straight from SYS5 fs. */ #include #include +#ifdef MODULE +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include #include #include @@ -48,6 +59,7 @@ lock_super(sb); sb->s_dev = 0; unlock_super(sb); + MOD_DEC_USE_COUNT; } /* @@ -123,6 +135,7 @@ printk("nfs_read_super: get root inode failed\n"); return NULL; } + MOD_INC_USE_COUNT; return sb; } @@ -238,3 +251,37 @@ inode->i_dirt = 0; return error; } + +#ifdef MODULE + +/* Every kernel module contains stuff like this. */ + +char kernel_version[] = UTS_RELEASE; + +static struct file_system_type nfs_fs_type = { + nfs_read_super, "nfs", 1, NULL +}; + +int init_module(void) +{ + int i; + + register_filesystem(&nfs_fs_type); + + return 0; +} + +void cleanup_module(void) +{ + int i; + + if (MOD_IN_USE) + { + printk("NFS cannot be removed, currently in use\n"); + return; + } + + unregister_filesystem(&nfs_fs_type); +} + +#endif diff -u --recursive --new-file v1.1.76/linux/fs/nfs/proc.c linux/fs/nfs/proc.c --- v1.1.76/linux/fs/nfs/proc.c Fri Nov 4 16:18:50 1994 +++ linux/fs/nfs/proc.c Thu Jan 5 13:55:40 1995 @@ -33,7 +33,6 @@ #define NFS_PROC_DEBUG #endif -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/fs/nfs/sock.c linux/fs/nfs/sock.c --- v1.1.76/linux/fs/nfs/sock.c Fri Oct 7 09:12:32 1994 +++ linux/fs/nfs/sock.c Thu Jan 5 13:55:40 1995 @@ -18,7 +18,6 @@ * */ -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/fs/proc/array.c linux/fs/proc/array.c --- v1.1.76/linux/fs/proc/array.c Mon Jan 2 15:09:31 1995 +++ linux/fs/proc/array.c Thu Jan 5 13:47:08 1995 @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff -u --recursive --new-file v1.1.76/linux/fs/proc/inode.c linux/fs/proc/inode.c --- v1.1.76/linux/fs/proc/inode.c Mon Dec 12 22:38:19 1994 +++ linux/fs/proc/inode.c Thu Jan 5 13:47:08 1995 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff -u --recursive --new-file v1.1.76/linux/fs/proc/net.c linux/fs/proc/net.c --- v1.1.76/linux/fs/proc/net.c Fri Dec 2 13:05:09 1994 +++ linux/fs/proc/net.c Thu Jan 5 13:47:08 1995 @@ -30,6 +30,7 @@ #include #include #include +#include /* forward references */ static int proc_readnet(struct inode * inode, struct file * file, diff -u --recursive --new-file v1.1.76/linux/fs/sysv/Makefile linux/fs/sysv/Makefile --- v1.1.76/linux/fs/sysv/Makefile Sat Dec 3 12:01:33 1994 +++ linux/fs/sysv/Makefile Wed Jan 4 21:16:05 1995 @@ -24,6 +24,7 @@ sysv.o: $(OBJS) $(LD) -r -o sysv.o $(OBJS) +modules: sysv.o dep: $(CPP) -M *.c > .depend diff -u --recursive --new-file v1.1.76/linux/fs/sysv/inode.c linux/fs/sysv/inode.c --- v1.1.76/linux/fs/sysv/inode.c Tue Dec 6 12:39:35 1994 +++ linux/fs/sysv/inode.c Sat Jan 7 12:48:34 1995 @@ -228,6 +228,7 @@ sb->sv_sb_flc_blocks = &sbd->s_free[0]; sb->sv_sb_total_free_blocks = &sbd->s_tfree; sb->sv_sb_time = &sbd->s_time; + sb->sv_sb_state = &sbd->s_state; sb->sv_block_base = 0; sb->sv_firstinodezone = 2; sb->sv_firstdatazone = sbd->s_isize; @@ -285,6 +286,7 @@ sb->sv_sb_flc_blocks = &sbd->s_free[0]; sb->sv_sb_total_free_blocks = &sbd->s_tfree; sb->sv_sb_time = &sbd->s_time; + sb->sv_sb_state = &sbd->s_state; sb->sv_block_base = 0; sb->sv_firstinodezone = 2; sb->sv_firstdatazone = sbd->s_isize; @@ -332,6 +334,7 @@ sb->sv_sb_flc_blocks = &sbd->s_free[0]; sb->sv_sb_total_free_blocks = &sbd->s_tfree; sb->sv_sb_time = &sbd->s_time; + sb->sv_sb_state = &sbd->s_state; sb->sv_block_base = 0; sb->sv_firstinodezone = 2; sb->sv_firstdatazone = sbd->s_isize; @@ -502,8 +505,19 @@ lock_super(sb); if (sb->sv_bh1->b_dirt || sb->sv_bh2->b_dirt) { /* If we are going to write out the super block, - then attach current time stamp. */ + then attach current time stamp. + But if the filesystem was marked clean, keep it clean. */ unsigned long time = CURRENT_TIME; + unsigned long old_time = *sb->sv_sb_time; + if (sb->sv_convert) + old_time = from_coh_ulong(old_time); + switch (sb->sv_type) { + case FSTYPE_SYSV4: + if (*sb->sv_sb_state == 0x7c269d38 - old_time) + *sb->sv_sb_state = 0x7c269d38 - time; + default: + break; + } if (sb->sv_convert) time = to_coh_ulong(time); *sb->sv_sb_time = time; diff -u --recursive --new-file v1.1.76/linux/fs/umsdos/Makefile linux/fs/umsdos/Makefile --- v1.1.76/linux/fs/umsdos/Makefile Tue Nov 1 19:36:55 1994 +++ linux/fs/umsdos/Makefile Wed Jan 4 21:16:05 1995 @@ -24,6 +24,8 @@ umsdos.o: $(OBJS) $(LD) -r -o umsdos.o $(OBJS) +modules: umsdos.o + clean: rm -f core *.o *.a *.s diff -u --recursive --new-file v1.1.76/linux/fs/xiafs/Makefile linux/fs/xiafs/Makefile --- v1.1.76/linux/fs/xiafs/Makefile Sat Dec 3 12:01:43 1994 +++ linux/fs/xiafs/Makefile Wed Jan 4 21:16:05 1995 @@ -24,6 +24,8 @@ xiafs.o: $(OBJS) $(LD) -r -o xiafs.o $(OBJS) +modules: xiafs.o + dep: $(CPP) -M *.c > .depend diff -u --recursive --new-file v1.1.76/linux/ibcs/Makefile linux/ibcs/Makefile --- v1.1.76/linux/ibcs/Makefile Sun Nov 27 20:19:53 1994 +++ linux/ibcs/Makefile Thu Jan 1 02:00:00 1970 @@ -1,37 +0,0 @@ -# -# 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/ibcs/binfmt_coff.c linux/ibcs/binfmt_coff.c --- v1.1.76/linux/ibcs/binfmt_coff.c Tue Nov 22 15:40:12 1994 +++ linux/ibcs/binfmt_coff.c Thu Jan 1 02:00:00 1970 @@ -1,784 +0,0 @@ -/* - * 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/ibcs/binfmt_elf.c linux/ibcs/binfmt_elf.c --- v1.1.76/linux/ibcs/binfmt_elf.c Wed Nov 30 17:17:39 1994 +++ linux/ibcs/binfmt_elf.c Thu Jan 1 02:00:00 1970 @@ -1,655 +0,0 @@ -/* - * 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/ibcs/emulate.c linux/ibcs/emulate.c --- v1.1.76/linux/ibcs/emulate.c Wed May 25 11:26:10 1994 +++ linux/ibcs/emulate.c Thu Jan 1 02:00:00 1970 @@ -1,10 +0,0 @@ -/* - * 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/include/asm-alpha/byteorder.h linux/include/asm-alpha/byteorder.h --- v1.1.76/linux/include/asm-alpha/byteorder.h Thu Jan 1 02:00:00 1970 +++ linux/include/asm-alpha/byteorder.h Wed Jan 4 21:16:05 1995 @@ -0,0 +1,82 @@ +#ifndef _ALPHA_BYTEORDER_H +#define _ALPHA_BYTEORDER_H + +#undef ntohl +#undef ntohs +#undef htonl +#undef htons + +extern unsigned long int ntohl(unsigned long int); +extern unsigned short int ntohs(unsigned short int); +extern unsigned long int htonl(unsigned long int); +extern unsigned short int htons(unsigned short int); + +extern unsigned long int __ntohl(unsigned long int); +extern unsigned short int __ntohs(unsigned short int); +extern unsigned long int __constant_ntohl(unsigned long int); +extern unsigned short int __constant_ntohs(unsigned short int); + +/* + * The constant and non-constant versions here are the same. + * Maybe I'll come up with an alpha-optimized routine for the + * non-constant ones (the constant ones don't need it: gcc + * will optimze it to the correct constant) + */ + +extern __inline__ unsigned long int +__ntohl(unsigned long int x) +{ + return (((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24)); +} + +extern __inline__ unsigned long int +__constant_ntohl(unsigned long int x) +{ + return (((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24)); +} + +extern __inline__ unsigned short int +__ntohs(unsigned short int x) +{ + return (((x & 0x00ff) << 8) | + ((x & 0xff00) >> 8)); +} + +extern __inline__ unsigned short int +__constant_ntohs(unsigned short int x) +{ + return (((x & 0x00ff) << 8) | + ((x & 0xff00) >> 8)); +} + +#define __htonl(x) __ntohl(x) +#define __htons(x) __ntohs(x) +#define __constant_htonl(x) __constant_ntohl(x) +#define __constant_htons(x) __constant_ntohs(x) + +#ifdef __OPTIMIZE__ +# define ntohl(x) \ +(__builtin_constant_p((long)(x)) ? \ + __constant_ntohl((x)) : \ + __ntohl((x))) +# define ntohs(x) \ +(__builtin_constant_p((short)(x)) ? \ + __constant_ntohs((x)) : \ + __ntohs((x))) +# define htonl(x) \ +(__builtin_constant_p((long)(x)) ? \ + __constant_htonl((x)) : \ + __htonl((x))) +# define htons(x) \ +(__builtin_constant_p((short)(x)) ? \ + __constant_htons((x)) : \ + __htons((x))) +#endif + +#endif diff -u --recursive --new-file v1.1.76/linux/include/asm-alpha/io.h linux/include/asm-alpha/io.h --- v1.1.76/linux/include/asm-alpha/io.h Wed Dec 14 14:45:57 1994 +++ linux/include/asm-alpha/io.h Wed Jan 4 21:16:05 1995 @@ -102,21 +102,21 @@ mb(); } -extern inline unsigned long inb(unsigned long addr) +extern inline unsigned int inb(unsigned long addr) { long result = *(volatile int *) ((addr << 7) + EISA_IO + 0x00); result >>= (addr & 3) * 8; return 0xffUL & result; } -extern inline unsigned long inw(unsigned long addr) +extern inline unsigned int inw(unsigned long addr) { long result = *(volatile int *) ((addr << 7) + EISA_IO + 0x20); result >>= (addr & 3) * 8; return 0xffffUL & result; } -extern inline unsigned long inl(unsigned long addr) +extern inline unsigned int inl(unsigned long addr) { return *(volatile unsigned int *) ((addr << 7) + EISA_IO + 0x60); } diff -u --recursive --new-file v1.1.76/linux/include/asm-alpha/processor.h linux/include/asm-alpha/processor.h --- v1.1.76/linux/include/asm-alpha/processor.h Mon Jan 2 07:36:19 1995 +++ linux/include/asm-alpha/processor.h Wed Jan 4 21:16:05 1995 @@ -8,6 +8,14 @@ #define __ASM_ALPHA_PROCESSOR_H /* + * We have a 8GB user address space to start with: 33 bits of vm + * can be handled with just 2 page table levels. + * + * Eventually, this should be bumped to 40 bits or so.. + */ +#define TASK_SIZE (0x200000000UL) + +/* * Bus types */ extern int EISA_bus; @@ -16,7 +24,7 @@ struct thread_struct { unsigned long ksp; unsigned long usp; - unsigned long ptbr; + unsigned long cr3; /* ptbr */ unsigned int pcc; unsigned int asn; unsigned long unique; @@ -28,6 +36,37 @@ 0, 0, 0, \ 0, 0, 0, \ 0, 0, 0, \ +} + +/* + * These are the "cli()" and "sti()" for software interrupts + * They work by increasing/decreasing the "intr_count" value, + * and as such can be nested arbitrarily. + */ +extern inline void start_bh_atomic(void) +{ + unsigned long dummy; + __asm__ __volatile__( + "\n1:\t" + "ldq_l %0,%1\n\t" + "addq %0,1,%0\n\t" + "stq_c %0,%1\n\t" + "beq %0,1b\n" + : "=r" (dummy), "=m" (intr_count) + : "0" (0)); +} + +extern inline void end_bh_atomic(void) +{ + unsigned long dummy; + __asm__ __volatile__( + "\n1:\t" + "ldq_l %0,%1\n\t" + "subq %0,1,%0\n\t" + "stq_c %0,%1\n\t" + "beq %0,1b\n" + : "=r" (dummy), "=m" (intr_count) + : "0" (0)); } #endif /* __ASM_ALPHA_PROCESSOR_H */ diff -u --recursive --new-file v1.1.76/linux/include/asm-alpha/ptrace.h linux/include/asm-alpha/ptrace.h --- v1.1.76/linux/include/asm-alpha/ptrace.h Thu Jan 1 02:00:00 1970 +++ linux/include/asm-alpha/ptrace.h Wed Jan 4 21:16:05 1995 @@ -0,0 +1,19 @@ +#ifndef _I386_PTRACE_H +#define _I386_PTRACE_H + + +/* this struct defines the way the registers are stored on the + stack during a system call. */ + +struct pt_regs { + unsigned long ps; + unsigned long pc; + unsigned long gp; + unsigned long a0; + unsigned long a1; + unsigned long a2; +}; + +#define user_mode(regs) ((regs)->ps & 8) + +#endif diff -u --recursive --new-file v1.1.76/linux/include/asm-alpha/segment.h linux/include/asm-alpha/segment.h --- v1.1.76/linux/include/asm-alpha/segment.h Mon Jan 2 09:28:27 1995 +++ linux/include/asm-alpha/segment.h Wed Jan 4 19:11:01 1995 @@ -64,7 +64,7 @@ #define memcpy_tofs(to, from, n) memcpy((to),(from),(n)) /* - * For segmented arhictectures, these are used to specify which segment + * For segmented architectures, these are used to specify which segment * to use for the above functions. * * The alpha is not segmented, so these are just dummies. diff -u --recursive --new-file v1.1.76/linux/include/asm-alpha/string.h linux/include/asm-alpha/string.h --- v1.1.76/linux/include/asm-alpha/string.h Sun Nov 20 19:39:57 1994 +++ linux/include/asm-alpha/string.h Wed Jan 4 21:16:05 1995 @@ -1,19 +1,4 @@ #ifndef __ALPHA_STRING_H #define __ALPHA_STRING_H -/* This doesn't actually work that well for unaligned stuff ;-p */ -extern inline void * memcpy(void * to, const void * from, size_t n) -{ - const unsigned long * f = from; - unsigned long * t = to; - int size = n; - - for (;;) { - size -= 8; - if (size < 0) - return to; - *(t++) = *(f++); - } -} - #endif diff -u --recursive --new-file v1.1.76/linux/include/asm-alpha/unistd.h linux/include/asm-alpha/unistd.h --- v1.1.76/linux/include/asm-alpha/unistd.h Wed Dec 14 14:45:57 1994 +++ linux/include/asm-alpha/unistd.h Wed Jan 4 21:16:05 1995 @@ -12,32 +12,38 @@ #define _syscall0(type,name) \ type name(void) \ { \ + return (type) -1; \ } #define _syscall1(type,name,type1,arg1) \ type name(type1 arg1) \ { \ + return (type) -1; \ } #define _syscall2(type,name,type1,arg1,type2,arg2) \ type name(type1 arg1,type2 arg2) \ { \ + return (type) -1; \ } #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ type name(type1 arg1,type2 arg2,type3 arg3) \ { \ + return (type) -1; \ } #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ { \ + return (type) -1; \ } #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ type5,arg5) \ type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \ { \ + return (type) -1; \ } #endif /* _ALPHA_UNISTD_H */ diff -u --recursive --new-file v1.1.76/linux/include/asm-i386/byteorder.h linux/include/asm-i386/byteorder.h --- v1.1.76/linux/include/asm-i386/byteorder.h Thu Jan 1 02:00:00 1970 +++ linux/include/asm-i386/byteorder.h Wed Jan 4 21:16:05 1995 @@ -0,0 +1,79 @@ +#ifndef _I386_BYTEORDER_H +#define _I386_BYTEORDER_H + +#undef ntohl +#undef ntohs +#undef htonl +#undef htons + +extern unsigned long int ntohl(unsigned long int); +extern unsigned short int ntohs(unsigned short int); +extern unsigned long int htonl(unsigned long int); +extern unsigned short int htons(unsigned short int); + +extern unsigned long int __ntohl(unsigned long int); +extern unsigned short int __ntohs(unsigned short int); +extern unsigned long int __constant_ntohl(unsigned long int); +extern unsigned short int __constant_ntohs(unsigned short int); + +extern __inline__ unsigned long int +__ntohl(unsigned long int x) +{ + __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ + "rorl $16,%0\n\t" /* swap words */ + "xchgb %b0,%h0" /* swap higher bytes */ + :"=q" (x) + : "0" (x)); + return x; +} + +extern __inline__ unsigned long int +__constant_ntohl(unsigned long int x) +{ + return (((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24)); +} + +extern __inline__ unsigned short int +__ntohs(unsigned short int x) +{ + __asm__("xchgb %b0,%h0" /* swap bytes */ + : "=q" (x) + : "0" (x)); + return x; +} + +extern __inline__ unsigned short int +__constant_ntohs(unsigned short int x) +{ + return (((x & 0x00ff) << 8) | + ((x & 0xff00) >> 8)); +} + +#define __htonl(x) __ntohl(x) +#define __htons(x) __ntohs(x) +#define __constant_htonl(x) __constant_ntohl(x) +#define __constant_htons(x) __constant_ntohs(x) + +#ifdef __OPTIMIZE__ +# define ntohl(x) \ +(__builtin_constant_p((long)(x)) ? \ + __constant_ntohl((x)) : \ + __ntohl((x))) +# define ntohs(x) \ +(__builtin_constant_p((short)(x)) ? \ + __constant_ntohs((x)) : \ + __ntohs((x))) +# define htonl(x) \ +(__builtin_constant_p((long)(x)) ? \ + __constant_htonl((x)) : \ + __htonl((x))) +# define htons(x) \ +(__builtin_constant_p((short)(x)) ? \ + __constant_htons((x)) : \ + __htons((x))) +#endif + +#endif diff -u --recursive --new-file v1.1.76/linux/include/asm-i386/ptrace.h linux/include/asm-i386/ptrace.h --- v1.1.76/linux/include/asm-i386/ptrace.h Thu Jan 1 02:00:00 1970 +++ linux/include/asm-i386/ptrace.h Wed Jan 4 21:16:05 1995 @@ -0,0 +1,50 @@ +#ifndef _I386_PTRACE_H +#define _I386_PTRACE_H + +#define EBX 0 +#define ECX 1 +#define EDX 2 +#define ESI 3 +#define EDI 4 +#define EBP 5 +#define EAX 6 +#define DS 7 +#define ES 8 +#define FS 9 +#define GS 10 +#define ORIG_EAX 11 +#define EIP 12 +#define CS 13 +#define EFL 14 +#define UESP 15 +#define SS 16 + + +/* this struct defines the way the registers are stored on the + stack during a system call. */ + +struct pt_regs { + long ebx; + long ecx; + long edx; + long esi; + long edi; + long ebp; + long eax; + unsigned short ds, __dsu; + unsigned short es, __esu; + unsigned short fs, __fsu; + unsigned short gs, __gsu; + long orig_eax; + long eip; + unsigned short cs, __csu; + long eflags; + long esp; + unsigned short ss, __ssu; +}; + +#ifdef __KERNEL__ +#define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->cs)) +#endif + +#endif diff -u --recursive --new-file v1.1.76/linux/include/asm-i386/string.h linux/include/asm-i386/string.h --- v1.1.76/linux/include/asm-i386/string.h Mon Dec 12 21:41:30 1994 +++ linux/include/asm-i386/string.h Wed Jan 4 21:16:05 1995 @@ -337,7 +337,7 @@ return __res; } -extern inline void * memcpy(void * to, const void * from, size_t n) +extern inline void * __memcpy(void * to, const void * from, size_t n) { __asm__ __volatile__( "cld\n\t" @@ -357,6 +357,51 @@ return (to); } +/* + * This looks horribly ugly, but the compiler can optimize it totally, + * as the count is constant. + */ +extern inline void * __constant_memcpy(void * to, const void * from, size_t n) +{ + switch (n) { + case 0: + return to; + case 1: + *(unsigned char *)to = *(unsigned char *)from; + return to; + case 2: + *(unsigned short *)to = *(unsigned short *)from; + return to; + case 3: + *(unsigned short *)to = *(unsigned short *)from; + *(2+(unsigned char *)to) = *(2+(unsigned char *)from); + return to; + case 4: + *(unsigned long *)to = *(unsigned long *)from; + return to; + } +#define COMMON(x) \ +__asm__("cld\n\t" \ + "rep ; movsl" \ + x \ + : /* no outputs */ \ + : "c" (n/4),"D" ((long) to),"S" ((long) from) \ + : "cx","di","si","memory"); + + switch (n % 4) { + case 0: COMMON(""); return to; + case 1: COMMON("\n\tmovsb"); return to; + case 2: COMMON("\n\tmovsw"); return to; + case 3: COMMON("\n\tmovsw\n\tstosb"); return to; + } +#undef COMMON +} + +#define memcpy(t, f, n) \ +(__builtin_constant_p(n) ? \ + __constant_memcpy((t),(f),(n)) : \ + __memcpy((t),(f),(n))) + extern inline void * memmove(void * dest,const void * src, size_t n) { if (dest /* get configuration macros */ +#include /* get configuration macros */ #if defined(CONFIG_ATARI) && !defined(CONFIG_AMIGA) && !defined(CONFIG_MAC) /* block out HSYNC on the atari */ diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/asi.h linux/include/asm-sparc/asi.h --- v1.1.76/linux/include/asm-sparc/asi.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/asi.h Sat Jan 7 12:35:19 1995 @@ -1,7 +1,7 @@ #ifndef _SPARC_ASI_H #define _SPARC_ASI_H -/* asi.h: Address Space Indentifier values for the sparc. +/* asi.h: Address Space Identifier values for the sparc. Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) */ diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/bitops.h linux/include/asm-sparc/bitops.h --- v1.1.76/linux/include/asm-sparc/bitops.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/bitops.h Sat Jan 7 12:35:19 1995 @@ -24,13 +24,12 @@ __asm__ __volatile__("or %%g0, 0x1, %3\n\t" /* produce the mask */ "sll %3, %4, %3\n\t" "rd %%psr, %5\n\t" /* read the psr */ - "andn %5, 0x20, %5\n\t" /* clear ET bit */ - "wr %5, 0x0, %%psr\n\t" /* traps disabled */ + "wr %5, 0x20, %%psr\n\t" /* traps disabled */ "ld [%1], %2\n\t" /* critical section */ "and %3, %2, %0\n\t" "or %3, %2, %2\n\t" "st %2, [%1]\n\t" - "wr %5, 0x20, %%psr\n\t" : /* re-enable traps */ + "wr %5, 0x0, %%psr\n\t" : /* re-enable traps */ "=r" (retval) : "r" (addr), "r" (tmp=0), "r" (mask=0), "r" (nr), "r" (psr=0)); @@ -46,13 +45,12 @@ __asm__ __volatile__("or %%g0, 0x1, %3\n\t" "sll %3, %4, %3\n\t" "rd %%psr, %5\n\t" - "andn %5, 0x20, %5\n\t" /* clear ET bit */ - "wr %5, 0x0, %%psr\n\t" /* disable traps */ + "wr %5, 0x20, %%psr\n\t" /* disable traps */ "ld [%1], %2\n\t" "and %2, %3, %0\n\t" /* get old bit */ "andn %2, %3, %2\n\t" /* set new val */ "st %2, [%1]\n\t" - "wr %5, 0x20, %%psr\n\t" : /* enable traps */ + "wr %5, 0x0, %%psr\n\t" : /* enable traps */ "=r" (retval) : "r" (addr), "r" (tmp=0), "r" (mask=0), "r" (nr), "r" (psr=0)); @@ -68,13 +66,12 @@ __asm__ __volatile__("or %%g0, 0x1, %3\n\t" "sll %3, %4, %3\n\t" "rd %%psr, %5\n\t" - "andn %5, 0x20, %5\n\t" /* clear ET bit */ - "wr %5, 0x0, %%psr\n\t" /* disable traps */ + "wr %5, 0x20, %%psr\n\t" /* disable traps */ "ld [%1], %2\n\t" "and %3, %2, %0\n\t" /* get old bit val */ "xor %3, %2, %2\n\t" /* set new val */ "st %2, [%1]\n\t" - "wr %5, 0x20, %%psr\n\t" : /* enable traps */ + "wr %5, 0x0, %%psr\n\t" : /* enable traps */ "=r" (retval) : "r" (addr), "r" (tmp=0), "r" (mask=0), "r" (nr), "r" (psr=0)); @@ -173,3 +170,4 @@ #endif /* defined(_SPARC_BITOPS_H) */ + diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/byteorder.h linux/include/asm-sparc/byteorder.h --- v1.1.76/linux/include/asm-sparc/byteorder.h Thu Jan 1 02:00:00 1970 +++ linux/include/asm-sparc/byteorder.h Sat Jan 7 12:35:19 1995 @@ -0,0 +1,82 @@ +#ifndef _SPARC_BYTEORDER_H +#define _SPARC_BYTEORDER_H + +#undef ntohl +#undef ntohs +#undef htonl +#undef htons + +extern unsigned long int ntohl(unsigned long int); +extern unsigned short int ntohs(unsigned short int); +extern unsigned long int htonl(unsigned long int); +extern unsigned short int htons(unsigned short int); + +extern unsigned long int __ntohl(unsigned long int); +extern unsigned short int __ntohs(unsigned short int); +extern unsigned long int __constant_ntohl(unsigned long int); +extern unsigned short int __constant_ntohs(unsigned short int); + +/* + * The constant and non-constant versions here are the same. + * Maybe I'll come up with an alpha-optimized routine for the + * non-constant ones (the constant ones don't need it: gcc + * will optimze it to the correct constant) + */ + +extern __inline__ unsigned long int +__ntohl(unsigned long int x) +{ + return (((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24)); +} + +extern __inline__ unsigned long int +__constant_ntohl(unsigned long int x) +{ + return (((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24)); +} + +extern __inline__ unsigned short int +__ntohs(unsigned short int x) +{ + return (((x & 0x00ff) << 8) | + ((x & 0xff00) >> 8)); +} + +extern __inline__ unsigned short int +__constant_ntohs(unsigned short int x) +{ + return (((x & 0x00ff) << 8) | + ((x & 0xff00) >> 8)); +} + +#define __htonl(x) __ntohl(x) +#define __htons(x) __ntohs(x) +#define __constant_htonl(x) __constant_ntohl(x) +#define __constant_htons(x) __constant_ntohs(x) + +#ifdef __OPTIMIZE__ +# define ntohl(x) \ +(__builtin_constant_p((long)(x)) ? \ + __constant_ntohl((x)) : \ + __ntohl((x))) +# define ntohs(x) \ +(__builtin_constant_p((short)(x)) ? \ + __constant_ntohs((x)) : \ + __ntohs((x))) +# define htonl(x) \ +(__builtin_constant_p((long)(x)) ? \ + __constant_htonl((x)) : \ + __htonl((x))) +# define htons(x) \ +(__builtin_constant_p((short)(x)) ? \ + __constant_htons((x)) : \ + __htons((x))) +#endif + +#endif /* !(_SPARC_BYTEORDER_H) */ diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/contregs.h linux/include/asm-sparc/contregs.h --- v1.1.76/linux/include/asm-sparc/contregs.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/contregs.h Sat Jan 7 12:35:19 1995 @@ -9,7 +9,7 @@ #define AC_CONTEXT 0x30000000 /* current mmu-context, handy for invalidate()'s ;-) */ #define AC_SENABLE 0x40000000 /* system dvma/cache enable, plus special reset poking */ -#define AC_CACHETAGS 0x80000000 /* directo access to the VAC cache, unused... */ +#define AC_CACHETAGS 0x80000000 /* direct access to the VAC cache, unused... */ #define AC_SYNC_ERR 0x60000000 /* what type of synchronous memory error happened */ #define AC_SYNC_VA 0x60000004 /* what virtual address caused the error to occur */ #define AC_ASYNC_ERR 0x60000008 /* what type of asynchronous mem-error happened */ diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/delay.h linux/include/asm-sparc/delay.h --- v1.1.76/linux/include/asm-sparc/delay.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/delay.h Sat Jan 7 12:35:19 1995 @@ -33,7 +33,7 @@ extern __inline__ unsigned long muldiv(unsigned long a, unsigned long b, unsigned long c) { - return ((a/c)*c); + return ((a*b)/c); } #endif /* defined(__SPARC_DELAY_H) */ diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/dma.h linux/include/asm-sparc/dma.h --- v1.1.76/linux/include/asm-sparc/dma.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/dma.h Sat Jan 7 12:35:19 1995 @@ -8,6 +8,8 @@ * Copyright (C) David S. Miller (davem@caip.rutgers.edu) */ +#define MAX_DMA_CHANNELS 8 + #ifndef _ASM_SPARC_DMA_H #define _ASM_SPARC_DMA_H diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/head.h linux/include/asm-sparc/head.h --- v1.1.76/linux/include/asm-sparc/head.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/head.h Sat Jan 7 12:35:19 1995 @@ -1,51 +1,47 @@ -#define KERNSIZE 134217728 -#define KERNBASE 0 /* new strategy */ -#define LOAD_ADDR 0x4000 +#ifndef __SPARC_HEAD_H +#define __SPARC_HEAD_H + +#define KERNSIZE 134217728 /* this is how much of a mapping the prom promises */ +#define PAGESIZE 4096 /* luckily this is the same on sun4c's and sun4m's */ +#define PAGESHIFT 12 +#define PROM_BASE -1568768 /* casa 'de PROM */ +#define LOAD_ADDR 0x4000 /* prom jumps to us here */ #define C_STACK 96 #define SUN4C_SEGSZ (1 << 18) -#define NBPG 4096 -#define UPAGES 2 -#define PROM_BASE -1568768 -#define PAGESHIFT_SUN4C 12 /* This is good for sun4m's also */ -#define ASI_CONTROL 0x02 /* for cache enable, context registers, etc. */ -#define ASI_SEGMAP 0x03 -#define ASI_PTE 0x04 -#define AC_CONTEXT 0x30000000 -#define USRSTACK KERNBASE -#define IE_REG_PTE_PG -201326592 +#define USRSTACK 0x0 /* no joke, this is temporary, trust me */ #define INT_ENABLE_REG_PHYSADR 0xf5000000 -#define IE_ALLIE 0x01 +#define INTS_ALL_ENAB 0x01 + +#define WRITE_PAUSE nop; nop; nop; + +/* Here are some trap goodies */ + -/* This crap should go elsewhere */ +/* Generic trap entry. */ -#define PSR_IMPL 0xf0000000 /* implementation */ -#define PSR_VER 0x0f000000 /* version */ -#define PSR_ICC 0x00f00000 /* integer condition codes */ -#define PSR_N 0x00800000 /* negative */ -#define PSR_Z 0x00400000 /* zero */ -#define PSR_O 0x00200000 /* overflow */ -#define PSR_C 0x00100000 /* carry */ -#define PSR_EC 0x00002000 /* coprocessor enable */ -#define PSR_EF 0x00001000 /* FP enable */ -#define PSR_PIL 0x00000f00 /* interrupt level */ -#define PSR_S 0x00000080 /* supervisor (kernel) mode */ -#define PSR_PS 0x00000040 /* previous supervisor mode (traps) */ -#define PSR_ET 0x00000020 /* trap enable */ -#define PSR_CWP 0x0000001f /* current window pointer */ +#define TRAP_ENTRY(type, label) \ + mov (type), %l3; b label; mov %psr, %l0; nop; -#define PCB_WIM 20 +/* This is for hard interrupts from level 1-14, 15 is non-maskable (nmi) and + * gets handled with another macro. + */ -#define HZ 100 +#define TRAP_ENTRY_INTERRUPT(int_level) \ + mov int_level, %l4; b trap_entry; mov %psr, %l0; nop; -/* Offsets into the proc structure for window info, etc. Just dummies - for now. +/* Here is the macro for soft interrupts (ie. not as urgent as hard ones) + which need to jump to a different handler. */ -#define TASK_WIM 0x8 -#define TASK_UW 0x16 -#define TASK_SIZE 8192 -#define TASK_RW 0x32 -#define TASK_NSAVED 0x40 -#define PG_VSHIFT 0x16 -#define PG_PROTSHIFT 0x6 -#define PG_PROTUWRITE 0x4 +#define TRAP_ENTRY_INTERRUPT_SOFT(int_level, ident) \ + mov int_level, %l4; mov %psr, %l0; b trap_entry; mov ident, %l3; + +/* Non-maskable interrupt entry macro. You have to turn off all interrupts + to not receive this. This is usually due to a asynchronous memory error. + All we can really do is stop the show. :-( +*/ +#define TRAP_ENTRY_INTERRUPT_NMI(t_type, jmp_to) \ + mov t_type, %l3; b jmp_to; mov %psr, %l0; nop; + + +#endif __SPARC_HEAD_H diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/openprom.h linux/include/asm-sparc/openprom.h --- v1.1.76/linux/include/asm-sparc/openprom.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/openprom.h Sat Jan 7 12:35:19 1995 @@ -19,7 +19,7 @@ #define LINUX_OPPROM_MAGIC 0x10010407 /* The device functions structure for the v0 prom. Nice and neat, open, - close, read & write divied up between net + block + char devices. We + close, read & write divvied up between net + block + char devices. We also have a seek routine only usable for block devices. The divide and conquer strategy of this struct becomes unnecessary for v2. @@ -54,7 +54,7 @@ the time can be a pain in the rear after a while. Why v2 has memory allocations in here are beyond me. Perhaps they figure that if you are going to use only the prom's device drivers then your memory - management is either non-existant or pretty sad. :-) + management is either non-existent or pretty sad. :-) */ struct linux_dev_v2_funcs { @@ -67,7 +67,7 @@ char* (*v2_dumb_mem_alloc)(char* va, unsigned sz); void (*v2_dumb_mem_free)(char* va, unsigned sz); - /* "dumb" mmap() munmap(), copy on write? whats that? */ + /* "dumb" mmap() munmap(), copy on write? what's that? */ char* (*v2_dumb_mmap)(char* virta, int asi, unsigned prot, unsigned sz); void (*v2_dumb_munmap)(char* virta, unsigned size); @@ -106,7 +106,7 @@ struct linux_mlist_v0 **v0_available; /* what phys. is left over */ }; -/* Arguements sent to the kernel from the boot prompt. */ +/* Arguments sent to the kernel from the boot prompt. */ struct linux_arguments_v0 { char *argv[8]; /* argv format for boot string */ @@ -141,7 +141,7 @@ unsigned int pv_magic_cookie; /* Magic Mushroom... */ unsigned int pv_romvers; /* iface vers (0, 2, or 3) */ unsigned int pv_plugin_revision; /* revision relative to above vers */ - unsigned int pv_printrev; /* printrevision */ + unsigned int pv_printrev; /* print revision */ /* Version 0 memory descriptors (see below). */ struct linux_mem_v0 pv_v0mem; /* V0: Memory description lists. */ @@ -215,7 +215,7 @@ */ void (*pv_setctxt)(int ctxt, char* va, int pmeg); - /* Prov version 3 Multiprocessor routines. This stuff is crazy. + /* Prom version 3 Multiprocessor routines. This stuff is crazy. * No joke. Calling these when there is only one cpu probably * crashes the machine, have to test this. :-) */ @@ -229,7 +229,7 @@ int (*v3_cpustart)(unsigned int whichcpu, int cancontext, int thiscontext, char* prog_counter); - /* v3_cpustop() will cause cpu 'whichcpu' to stop executint + /* v3_cpustop() will cause cpu 'whichcpu' to stop executing * until a resume cpu call is made. */ diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/page.h linux/include/asm-sparc/page.h --- v1.1.76/linux/include/asm-sparc/page.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/page.h Sat Jan 7 12:35:19 1995 @@ -14,7 +14,7 @@ * do an invalidate is flush the VAC. */ -#define invalidate() flush_vac_context() /* how conveeeiiiiinnnent :> */ +#define invalidate() flush_vac_context() /* how conveeeiiiiinnnient :> */ #define PAGE_SHIFT 12 /* This is the virtual page... */ diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h --- v1.1.76/linux/include/asm-sparc/processor.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/processor.h Sat Jan 7 12:35:19 1995 @@ -18,7 +18,7 @@ * * "this is gonna have to change to 1gig for the sparc" - David S. Miller */ -#define TASK_SIZE 0xc0000000 +#define TASK_SIZE (0xc0000000UL) /* * Size of io_bitmap in longwords: 32 is ports 0-0x3ff. @@ -28,18 +28,91 @@ struct thread_struct { unsigned long ksp; /* kernel stack pointer */ unsigned long usp; /* user's sp, throw reg windows here */ - unsigned long ptbr; + unsigned long cr3; /* why changed from ptbr? */ unsigned int pcc; unsigned int asn; unsigned long unique; unsigned long flags; unsigned long res1, res2; + unsigned long psr; /* save for condition codes */ + unsigned long pc; /* program counter */ + unsigned long npc; /* next program counter */ + +/* 8 local registers + 8 in registers * 24 register windows. + * Most sparc's I know of only have 8 windows implemented, + * we determine how many at boot time and store that value + * in nwindows. + */ + unsigned long globl_regs[8]; /* global regs need to be saved too */ + unsigned long reg_window[16*24]; + unsigned long yreg; + unsigned long uwindows; /* how many user windows are in the set */ + unsigned long float_regs[64]; /* V8 and below have 32, V9 has 64 */ }; #define INIT_TSS { \ - 0, 0, 0, \ - 0, 0, 0, \ - 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \ + 0, 0, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \ +} + +/* + * These are the "cli()" and "sti()" for software interrupts + * They work by increasing/decreasing the "intr_count" value, + * and as such can be nested arbitrarily. + */ +extern inline void start_bh_atomic(void) +{ + unsigned long dummy, psr; + __asm__ __volatile__("rd %%psr, %2\n\t" + "wr %2, 0x20, %%psr\n\t" /* disable traps */ + "ld %1,%0\n\t" + "add %0,1,%0\n\t" + "st %0,%1\n\t" + "wr %2, 0x0, %%psr\n\t" /* enable traps */ + : "=r" (dummy), "=m" (intr_count) + : "0" (0), "r" (psr=0)); +} + +extern inline void end_bh_atomic(void) +{ + unsigned long dummy, psr; + __asm__ __volatile__("rd %%psr, %2\n\t" + "wr %2, 0x20, %%psr\n\t" + "ld %1,%0\n\t" + "sub %0,1,%0\n\t" + "st %0,%1\n\t" + "wr %2, 0x0, %2\n\t" + : "=r" (dummy), "=m" (intr_count) + : "0" (0), "r" (psr=0)); } #endif /* __ASM_SPARC_PROCESSOR_H */ diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/ptrace.h linux/include/asm-sparc/ptrace.h --- v1.1.76/linux/include/asm-sparc/ptrace.h Thu Jan 1 02:00:00 1970 +++ linux/include/asm-sparc/ptrace.h Sat Jan 7 12:35:19 1995 @@ -0,0 +1,29 @@ +#ifndef _SPARC_PTRACE_H +#define _SPARC_PTRACE_H + +/* I have not looked enough into how this should be done. Without playing + * lots of tricks to optimize I think we need to save the whole register + * window frame plus the floating-point registers. We'll see... + */ + +/* this struct defines the way the registers are stored on the + stack during a system call. */ + +struct pt_regs { + unsigned long ps; /* previous supervisor, same as alpha I believe */ + unsigned long pc; /* current and next program counter */ + unsigned long npc; + unsigned long sp; /* stack and frame pointer */ + unsigned long fp; + unsigned long psr; /* for condition codes */ + unsigned long nuwin; /* number of user windows */ + /* not sure yet whether all regs are necessary + * but this is how it is traditionally done on the sparc. + */ + unsigned long u_regs[24*16]; + unsigned long f_regs[64]; /* yuck yuck yuck */ +}; + +#define user_mode(regs) (~((regs)->ps&0x1)) /* if previous supervisor is 0, came from user */ + +#endif diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/string.h linux/include/asm-sparc/string.h --- v1.1.76/linux/include/asm-sparc/string.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/string.h Sat Jan 7 12:35:19 1995 @@ -121,17 +121,155 @@ return retval; } -/* extern __inline__ char *strcat(char *dest, char *src); */ +extern __inline__ char *strcat(char *dest, const char *src) +{ + register char *retval; + register char temp; -/* extern __inline__ char *strncat(char *dest, char *src, int len); */ + __asm__("or %%g0, %1, %0\n\t" + "1: ldub [%1], %3\n\t" + "cmp %3, 0x0\n\t" + "bne,a 1b\n\t" + "add %1, 0x1, %1\n\t" + "2: ldub [%2], %3\n\t" + "stb %3, [%1]\n\t" + "add %1, 0x1, %1\n\t" + "cmp %3, 0x0\n\t" + "bne 2b\n\t" + "add %2, 0x1, %2\n\t" : + "=r" (retval) : + "r" (dest), "r" (src), "r" (temp=0)); -/* extern __inline__ char *strchr(char *src, char c); */ + return retval; +} -/* extern __inline__ char *strpbrk(char *cs, char *ct); */ +extern __inline__ char *strncat(char *dest, const char *src, size_t len) +{ + register char *retval; + register char temp; -/* extern __inline__ char *strtok(char *s, char *ct); */ + __asm__("or %%g0, %1, %0\n\t" + "1: ldub [%1], %3\n\t" + "cmp %3, 0x0\n\t" + "bne,a 1b\n\t" + "add %1, 0x1, %1\n\t" + "2: ldub [%2], %3\n\t" + "stb %3, [%1]\n\t" + "add %1, 0x1, %1\n\t" + "add %3, -1, %3\n\t" + "cmp %3, 0x0\n\t" + "bne 2b\n\t" + "add %2, 0x1, %2\n\t" : + "=r" (retval) : + "r" (dest), "r" (src), "r" (len), "r" (temp=0)); -/* extern __inline__ int strspn(char *s, char *accept); */ + return retval; +} + +extern __inline__ char *strchr(const char *src, char c) +{ + register char temp; + register char *trick; + + __asm__("1: ldub [%0], %2\n\t" + "cmp %2, %1\n\t" + "bne,a 1b\n\t" + "add %0, 0x1, %0\n\t" + "or %%g0, %0, %3\n\t" : + "=r" (src) : + "r" (c), "r" (temp=0), "r" (trick=0), "0" (src)); + + return trick; +} + +extern __inline__ char *strpbrk(const char *cs, const char *ct) +{ + register char temp1, temp2; + register char *scratch; + register char *trick; + + __asm__("or %%g0, %1, %4\n\t" + "1: ldub [%0], %2\n\t" + "2: ldub [%1], %3\n\t" + "cmp %3, %2\n\t" + "be 3f\n\t" + "nop\n\t" + "cmp %3, 0x0\n\t" + "bne 2b\n\t" + "add %1, 0x1, %1\n\t" + "or %%g0, %4, %1\n\t" + "b 1b\n\t" + "add %0, 0x1, %0\n\t" + "or %%g0, %0, %5\n\t" : + "=r" (cs) : + "r" (ct), "r" (temp1), "r" (temp2), "r" (scratch), "r" (trick=0), + "0" (cs), "1" (ct)); + + return trick; + +} + + +extern __inline__ size_t strspn(const char *s, const char *accept) +{ + register char temp1, temp2; + register char* scratch; + register size_t trick; + + __asm__("or %%g0, %1, %4\n\t" + "1: ldub [%0], %2\n\t" + "2: ldub [%1], %3\n\t" + "cmp %3, 0x0\n\t" + "be 3f\n\t" + "cmp %3, %2" + "bne 2b\n\t" + "add %1, 0x1, %1\n\t" + "add %0, 0x1, %0\n\t" + "b 1b\n\t" + "add %5, 0x1, %5\n\t" + "3: or %%g0, %0, %4\n\t" : + "=r" (s) : + "r" (accept), "r" (temp1), "r" (temp2), + "r" (scratch), "r" (trick=0), "0" (s)); + + return trick; + +} + +extern __inline__ char *strtok(char *s, const char *ct) +{ + static char* old; /* frob this kludge for now */ + register char *tok; + + if (s == (char *) 0) + { + if (old == (char *) 0) + { + return (char *) 0; + } + else + s = old; + } + + s += strspn(s, ct); + if(*s == '\0') + { + old = (char *) 0; + return (char *) 0; + } + + tok = s; + s = strpbrk(tok, ct); + if (s == (char *) 0) + old = (char *) 0; + else + { + *s = '\0'; + old = s + 1; + } + return tok; +} + extern __inline__ void *memset(void *src, char c, size_t count) { diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/sysen.h linux/include/asm-sparc/sysen.h --- v1.1.76/linux/include/asm-sparc/sysen.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/sysen.h Sat Jan 7 12:35:19 1995 @@ -9,6 +9,6 @@ #define SENABLE_DVMA 0x20 /* enable dvma transfers */ #define SENABLE_CACHE 0x10 /* enable VAC cache */ -#define SENABLE_RESET 0x04 /* reset the whole mache, danger Will Robinson */ +#define SENABLE_RESET 0x04 /* reset the whole machine, danger Will Robinson */ #endif /* _SPARC_SYSEN_H */ diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/system.h linux/include/asm-sparc/system.h --- v1.1.76/linux/include/asm-sparc/system.h Mon Jan 2 15:26:45 1995 +++ linux/include/asm-sparc/system.h Sat Jan 7 12:35:19 1995 @@ -9,7 +9,7 @@ /* * I wish the boot time image was as beautiful as the Alpha's * but no such luck. The icky PROM loads us at 0x0, and jumps - * to magic addess 0x4000 to start thing going. This means that + * to magic address 0x4000 to start thing going. This means that * I can stick the pcb and user/kernel stacks in the area from * 0x0-0x4000 and be reasonably sure that this is sane. * @@ -34,7 +34,7 @@ #define move_to_user_mode() halt() #define switch_to(x) halt() -#ifndef stbar /* store barrier Sparc insn to snchronize stores in PSO */ +#ifndef stbar /* store barrier Sparc insn to synchronize stores in PSO */ #define stbar() __asm__ __volatile__("stbar": : :"memory") #endif @@ -62,6 +62,25 @@ #define save_flags(flags) do { flags = swpipl(15); } while (0) #define restore_flags(flags) swpipl(flags) +#define iret() __asm__ __volatile__ ("jmp %%l1\n\t" \ + "rett %l2\n\t": : :"memory") + +#define _set_gate(gate_addr,type,dpl,addr) \ +__asm__ __volatile__ ("nop\n\t") + +#define set_intr_gate(n,addr) \ + _set_gate(&idt[n],14,0,addr) + +#define set_trap_gate(n,addr) \ + _set_gate(&idt[n],15,0,addr) + +#define set_system_gate(n,addr) \ + _set_gate(&idt[n],15,3,addr) + +#define set_call_gate(a,addr) \ + _set_gate(a,12,3,addr) + + /* Must this be atomic? */ extern inline void *xchg_u32(int * m, unsigned long val) @@ -69,11 +88,11 @@ unsigned long dummy; __asm__ __volatile__( - "ld %1,%2\n\t" - "st %0, %1\n\t" + "ld [%1],%2\n\t" + "st %0, [%1]\n\t" "or %%g0, %2, %0" - : "=r" (val), "=m" (*m), "=r" (dummy) - : "1" (*m), "2" (val)); + : "=r" (val), "=r" (m), "=r" (dummy) + : "0" (val)); return (void *)val; } @@ -86,6 +105,8 @@ { return (void *) xchg_u32((int *) m, (unsigned long) val); } + + #endif /* __ASSEMBLY__ */ diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/types.h linux/include/asm-sparc/types.h --- v1.1.76/linux/include/asm-sparc/types.h Sun Jan 1 15:36:57 1995 +++ linux/include/asm-sparc/types.h Sat Jan 7 12:35:19 1995 @@ -10,21 +10,34 @@ * however this won't be for a while. */ -typedef __signed__ char __s8; +#ifndef _SIZE_T +#define _SIZE_T +typedef long unsigned int size_t; +#endif + +#ifndef _SSIZE_T +#define _SSIZE_T +typedef int ssize_t; +#endif + +#ifndef _PTRDIFF_T +#define _PTRDIFF_T +typedef int ptrdiff_t; +#endif + +typedef signed char __s8; typedef unsigned char __u8; -typedef __signed__ short __s16; +typedef signed short __s16; typedef unsigned short __u16; -typedef __signed__ int __s32; +typedef signed int __s32; typedef unsigned int __u32; /* Only 32-bit sparcs for now so.... */ -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) -typedef __signed__ long long __s64; -typedef unsigned long long __s64; -#endif +typedef signed long long __s64; +typedef unsigned long long __u64; #ifdef __KERNEL__ diff -u --recursive --new-file v1.1.76/linux/include/asm-sparc/version.h linux/include/asm-sparc/version.h --- v1.1.76/linux/include/asm-sparc/version.h Thu Jan 1 02:00:00 1970 +++ linux/include/asm-sparc/version.h Sat Jan 7 12:35:19 1995 @@ -0,0 +1 @@ +#define WHO_COMPILED_ME "someone@somewhere.domain" diff -u --recursive --new-file v1.1.76/linux/include/linux/binfmts.h linux/include/linux/binfmts.h --- v1.1.76/linux/include/linux/binfmts.h Thu Jun 16 10:30:25 1994 +++ linux/include/linux/binfmts.h Wed Jan 4 21:16:05 1995 @@ -45,9 +45,12 @@ extern int open_inode(struct inode * inode, int mode); extern void flush_old_exec(struct linux_binprm * bprm); -extern unsigned long change_ldt(unsigned long text_size,unsigned long * page); +extern unsigned long setup_arg_pages(unsigned long text_size,unsigned long * page); extern unsigned long * create_tables(char * p,int argc,int envc,int ibcs); extern unsigned long copy_strings(int argc,char ** argv,unsigned long *page, unsigned long p, int from_kmem); + +/* this eventually goes away */ +#define change_ldt(a,b) setup_arg_pages(a,b) #endif diff -u --recursive --new-file v1.1.76/linux/include/linux/config.h linux/include/linux/config.h --- v1.1.76/linux/include/linux/config.h Sun Jan 1 16:28:20 1995 +++ linux/include/linux/config.h Thu Jan 5 13:57:22 1995 @@ -38,30 +38,4 @@ #define EXTENDED_VGA 0xfffe /* 80x50 mode */ #define ASK_VGA 0xfffd /* ask for it at bootup */ -/* - * The root-device is no longer hard-coded. You can change the default - * root-device by changing the line ROOT_DEV = XXX in boot/bootsect.s - */ - -/* - * The keyboard is now defined in kernel/chr_dev/keyboard.S - */ - -/* - * HD_TYPE has been superceeded by kernel command line parameters. - */ - -/* - * File type specific stuff goes into this. - */ - -#ifdef ASM_SRC -#endif - -#ifdef C_SRC -#endif - -#ifdef MAKE -#endif - #endif diff -u --recursive --new-file v1.1.76/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h --- v1.1.76/linux/include/linux/ext2_fs.h Fri Nov 4 12:38:45 1994 +++ linux/include/linux/ext2_fs.h Wed Jan 4 21:16:05 1995 @@ -261,7 +261,7 @@ } osd2; /* OS dependent 2 */ }; -#ifdef __linux__ +#if defined(__KERNEL__) || defined(__linux__) #define i_reserved1 osd1.linux1.l_i_reserved1 #define i_frag osd2.linux2.l_i_frag #define i_fsize osd2.linux2.l_i_fsize diff -u --recursive --new-file v1.1.76/linux/include/linux/hdreg.h linux/include/linux/hdreg.h --- v1.1.76/linux/include/linux/hdreg.h Sun Jan 1 16:28:20 1995 +++ linux/include/linux/hdreg.h Thu Jan 5 13:47:08 1995 @@ -1,6 +1,8 @@ #ifndef _LINUX_HDREG_H #define _LINUX_HDREG_H +#include + /* * This file contains some defines for the AT-hd-controller. * Various sources. Check out some definitions (see comments with diff -u --recursive --new-file v1.1.76/linux/include/linux/in.h linux/include/linux/in.h --- v1.1.76/linux/include/linux/in.h Tue Dec 27 09:54:44 1994 +++ linux/include/linux/in.h Wed Jan 4 21:16:05 1995 @@ -111,82 +111,9 @@ #define INADDR_UNSPEC_GROUP 0xe0000000 /* 224.0.0.0 */ #define INADDR_ALLHOSTS_GROUP 0xe0000001 /* 224.0.0.1 */ #define INADDR_MAX_LOCAL_GROUP 0xe00000ff /* 224.0.0.255 */ - -/* Linux Internet number representation function declarations. */ -#undef ntohl -#undef ntohs -#undef htonl -#undef htons +/* contains the htonl type stuff.. */ -extern unsigned long int ntohl(unsigned long int); -extern unsigned short int ntohs(unsigned short int); -extern unsigned long int htonl(unsigned long int); -extern unsigned short int htons(unsigned short int); - -extern unsigned long int __ntohl(unsigned long int); -extern unsigned short int __ntohs(unsigned short int); -extern unsigned long int __constant_ntohl(unsigned long int); -extern unsigned short int __constant_ntohs(unsigned short int); - -extern __inline__ unsigned long int -__ntohl(unsigned long int x) -{ - __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ - "rorl $16,%0\n\t" /* swap words */ - "xchgb %b0,%h0" /* swap higher bytes */ - :"=q" (x) - : "0" (x)); - return x; -} - -extern __inline__ unsigned long int -__constant_ntohl(unsigned long int x) -{ - return (((x & 0x000000ffU) << 24) | - ((x & 0x0000ff00U) << 8) | - ((x & 0x00ff0000U) >> 8) | - ((x & 0xff000000U) >> 24)); -} - -extern __inline__ unsigned short int -__ntohs(unsigned short int x) -{ - __asm__("xchgb %b0,%h0" /* swap bytes */ - : "=q" (x) - : "0" (x)); - return x; -} - -extern __inline__ unsigned short int -__constant_ntohs(unsigned short int x) -{ - return (((x & 0x00ff) << 8) | - ((x & 0xff00) >> 8)); -} - -#define __htonl(x) __ntohl(x) -#define __htons(x) __ntohs(x) -#define __constant_htonl(x) __constant_ntohl(x) -#define __constant_htons(x) __constant_ntohs(x) - -#ifdef __OPTIMIZE__ -# define ntohl(x) \ -(__builtin_constant_p((long)(x)) ? \ - __constant_ntohl((x)) : \ - __ntohl((x))) -# define ntohs(x) \ -(__builtin_constant_p((short)(x)) ? \ - __constant_ntohs((x)) : \ - __ntohs((x))) -# define htonl(x) \ -(__builtin_constant_p((long)(x)) ? \ - __constant_htonl((x)) : \ - __htonl((x))) -# define htons(x) \ -(__builtin_constant_p((short)(x)) ? \ - __constant_htons((x)) : \ - __htons((x))) -#endif +#include #endif /* _LINUX_IN_H */ diff -u --recursive --new-file v1.1.76/linux/include/linux/interrupt.h linux/include/linux/interrupt.h --- v1.1.76/linux/include/linux/interrupt.h Sun Jan 1 16:28:20 1995 +++ linux/include/linux/interrupt.h Wed Jan 4 21:16:05 1995 @@ -13,6 +13,8 @@ extern unsigned long bh_mask; extern struct bh_struct bh_base[32]; +asmlinkage void do_bottom_half(void); + /* Who gets which entry in bh_base. Things which will occur most often should come first - in which case NET should be up the top with SERIAL/TQUEUE! */ @@ -65,9 +67,9 @@ * probe_irq_on() returns a mask of snarfed irq's. * * probe_irq_off() takes the mask as a parameter, - * and returns the irq number which occured, - * or zero if none occured, or a negative irq number - * if more than one irq occured. + * and returns the irq number which occurred, + * or zero if none occurred, or a negative irq number + * if more than one irq occurred. */ extern unsigned int probe_irq_on(void); /* returns 0 on failure */ extern int probe_irq_off(unsigned int); /* returns 0 or negative on failure */ diff -u --recursive --new-file v1.1.76/linux/include/linux/ioport.h linux/include/linux/ioport.h --- v1.1.76/linux/include/linux/ioport.h Tue Dec 6 11:23:54 1994 +++ linux/include/linux/ioport.h Sat Jan 7 12:57:57 1995 @@ -19,6 +19,7 @@ extern void reserve_setup(char *str, int *ints); extern int check_region(unsigned int from, unsigned int extent); extern void snarf_region(unsigned int from, unsigned int extent); +extern void register_iomem(unsigned int from, unsigned int extent,char* name); extern void release_region(unsigned int from, unsigned int extent); extern int get_ioport_list(char *); diff -u --recursive --new-file v1.1.76/linux/include/linux/ip_fw.h linux/include/linux/ip_fw.h --- v1.1.76/linux/include/linux/ip_fw.h Wed Nov 30 21:53:47 1994 +++ linux/include/linux/ip_fw.h Thu Jan 5 13:47:08 1995 @@ -104,6 +104,9 @@ */ #ifdef __KERNEL__ + +#include + #ifdef CONFIG_IP_FIREWALL extern struct ip_fw *ip_fw_blk_chain; extern struct ip_fw *ip_fw_fwd_chain; diff -u --recursive --new-file v1.1.76/linux/include/linux/iso_fs_sb.h linux/include/linux/iso_fs_sb.h --- v1.1.76/linux/include/linux/iso_fs_sb.h Fri Jun 17 08:11:57 1994 +++ linux/include/linux/iso_fs_sb.h Thu Jan 5 09:24:45 1995 @@ -20,6 +20,7 @@ junk */ unsigned char s_nosuid; unsigned char s_nodev; + mode_t s_mode; gid_t s_gid; uid_t s_uid; }; diff -u --recursive --new-file v1.1.76/linux/include/linux/kernel.h linux/include/linux/kernel.h --- v1.1.76/linux/include/linux/kernel.h Thu Aug 11 17:00:17 1994 +++ linux/include/linux/kernel.h Wed Jan 4 21:16:05 1995 @@ -67,7 +67,6 @@ #define suser() (current->euid == 0) #define fsuser() (current->fsuid == 0) -extern int splx (int new_ipl); #endif /* __KERNEL__ */ #define SI_LOAD_SHIFT 16 diff -u --recursive --new-file v1.1.76/linux/include/linux/minix_fs.h linux/include/linux/minix_fs.h --- v1.1.76/linux/include/linux/minix_fs.h Sun Jul 24 17:39:46 1994 +++ linux/include/linux/minix_fs.h Wed Jan 4 21:16:05 1995 @@ -27,14 +27,18 @@ #define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode))) +/* + * This is the original minix inode layout on disk. + * Note the 8-bit gid and atime and ctime. + */ struct minix_inode { - unsigned short i_mode; - unsigned short i_uid; - unsigned long i_size; - unsigned long i_time; - unsigned char i_gid; - unsigned char i_nlinks; - unsigned short i_zone[9]; + u16 i_mode; + u16 i_uid; + u32 i_size; + u32 i_time; + u8 i_gid; + u8 i_nlinks; + u16 i_zone[9]; }; /* @@ -44,34 +48,34 @@ * now 16-bit. The inode is now 64 bytes instead of 32. */ struct new_minix_inode { - unsigned short i_mode; - unsigned short i_nlinks; - unsigned short i_uid; - unsigned short i_gid; - unsigned long i_size; - unsigned long i_atime; - unsigned long i_mtime; - unsigned long i_ctime; - unsigned long i_zone[10]; + u16 i_mode; + u16 i_nlinks; + u16 i_uid; + u16 i_gid; + u32 i_size; + u32 i_atime; + u32 i_mtime; + u32 i_ctime; + u32 i_zone[10]; }; /* * minix super-block data on disk */ struct minix_super_block { - unsigned short s_ninodes; - unsigned short s_nzones; - unsigned short s_imap_blocks; - unsigned short s_zmap_blocks; - unsigned short s_firstdatazone; - unsigned short s_log_zone_size; - unsigned long s_max_size; - unsigned short s_magic; - unsigned short s_state; + u16 s_ninodes; + u16 s_nzones; + u16 s_imap_blocks; + u16 s_zmap_blocks; + u16 s_firstdatazone; + u16 s_log_zone_size; + u32 s_max_size; + u16 s_magic; + u16 s_state; }; struct minix_dir_entry { - unsigned short inode; + u16 inode; char name[0]; }; diff -u --recursive --new-file v1.1.76/linux/include/linux/minix_fs_i.h linux/include/linux/minix_fs_i.h --- v1.1.76/linux/include/linux/minix_fs_i.h Wed Dec 1 14:44:15 1993 +++ linux/include/linux/minix_fs_i.h Wed Jan 4 21:16:05 1995 @@ -5,7 +5,7 @@ * minix fs inode data in memory */ struct minix_inode_info { - unsigned short i_data[16]; + u16 i_data[16]; }; #endif diff -u --recursive --new-file v1.1.76/linux/include/linux/msdos_fs_i.h linux/include/linux/msdos_fs_i.h --- v1.1.76/linux/include/linux/msdos_fs_i.h Wed Aug 10 19:26:43 1994 +++ linux/include/linux/msdos_fs_i.h Thu Jan 5 13:55:40 1995 @@ -1,9 +1,6 @@ #ifndef _MSDOS_FS_I #define _MSDOS_FS_I -#ifndef _LINUX_CONFIG_H -#include -#endif #ifndef _LINUX_PIPE_FS_I_H #include #endif diff -u --recursive --new-file v1.1.76/linux/include/linux/net.h linux/include/linux/net.h --- v1.1.76/linux/include/linux/net.h Fri Oct 21 09:41:17 1994 +++ linux/include/linux/net.h Sat Jan 7 12:57:54 1995 @@ -53,6 +53,8 @@ } socket_state; #define SO_ACCEPTCON (1<<16) /* performed a listen */ +#define SO_WAITDATA (1<<17) /* wait data to read */ +#define SO_NOSPACE (1<<18) /* no space to write */ #ifdef __KERNEL__ /* @@ -128,8 +130,8 @@ void (*init_func)(struct net_proto *); /* Bootstrap */ }; -extern int sock_awaitconn(struct socket *mysock, struct socket *servsock); -extern int sock_wake_async(struct socket *sock); +extern int sock_awaitconn(struct socket *mysock, struct socket *servsock, int flags); +extern int sock_wake_async(struct socket *sock, int how); extern int sock_register(int family, struct proto_ops *ops); extern int sock_unregister(int family); diff -u --recursive --new-file v1.1.76/linux/include/linux/netdevice.h linux/include/linux/netdevice.h --- v1.1.76/linux/include/linux/netdevice.h Tue Dec 6 13:10:22 1994 +++ linux/include/linux/netdevice.h Sat Jan 7 12:57:54 1995 @@ -165,7 +165,7 @@ struct packet_type { unsigned short type; /* This is really htons(ether_type). */ - unsigned short copy:1; + struct device * dev; int (*func) (struct sk_buff *, struct device *, struct packet_type *); void *data; @@ -175,6 +175,8 @@ #ifdef __KERNEL__ +#include + /* Used by dev_rint */ #define IN_SKBUFF 1 @@ -219,14 +221,15 @@ /* Support for loadable net-drivers */ extern int register_netdev(struct device *dev); extern void unregister_netdev(struct device *dev); - +extern int register_netdevice_notifier(struct notifier_block *nb); +extern int unregister_netdevice_notifier(struct notifier_block *nb); /* Functions used for multicast support */ - extern void dev_mc_upload(struct device *dev); extern void dev_mc_delete(struct device *dev, void *addr, int alen, int all); extern void dev_mc_add(struct device *dev, void *addr, int alen, int newonly); extern void dev_mc_discard(struct device *dev); - +/* This is the wrong place but it'll do for the moment */ +extern void ip_mc_allhost(struct device *dev); #endif /* __KERNEL__ */ #endif /* _LINUX_DEV_H */ diff -u --recursive --new-file v1.1.76/linux/include/linux/notifier.h linux/include/linux/notifier.h --- v1.1.76/linux/include/linux/notifier.h Thu Jan 1 02:00:00 1970 +++ linux/include/linux/notifier.h Sat Jan 7 12:57:54 1995 @@ -0,0 +1,96 @@ +/* + * Routines to manage notifier chains for passing status changes to any + * interested routines. We need this instead of hard coded call lists so + * that modules can poke their nose into the innards. The network devices + * needed them so here they are for the rest of you. + * + * Alan Cox + */ + +#ifndef _LINUX_NOTIFIER_H +#define _LINUX_NOTIFIER_H +#include + +struct notifier_block +{ + int (*notifier_call)(unsigned long, void *); + struct notifier_block *next; + int priority; +}; + + +#ifdef __KERNEL__ + +#define NOTIFY_DONE 0x0000 /* Don't care */ +#define NOTIFY_OK 0x0001 /* Suits me */ +#define NOTIFY_STOP_MASK 0x8000 /* Don't call further */ +#define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002) /* Bad/Veto action */ + +extern __inline__ int notifier_chain_register(struct notifier_block **list, struct notifier_block *n) +{ + while(*list) + { + if(n->priority > (*list)->priority) + break; + list= &((*list)->next); + } + n->next = *list; + *list=n; + return 0; +} + +/* + * Warning to any non GPL module writers out there.. these functions are + * GPL'd + */ + +extern __inline__ int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n) +{ + while((*nl)!=NULL) + { + if((*nl)==n) + { + *nl=n->next; + return 0; + } + nl=&((*nl)->next); + } + return -ENOENT; +} + +/* + * This is one of these things that is generally shorter inline + */ + +extern __inline__ int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v) +{ + int ret=NOTIFY_DONE; + struct notifier_block *nb = *n; + while(nb) + { + ret=nb->notifier_call(val,v); + if(ret&NOTIFY_STOP_MASK) + return ret; + nb=nb->next; + } + return ret; +} + + +/* + * Declared notifiers so far. I can imagine quite a few more chains + * over time (eg laptop power reset chains, reboot chain (to clean + * device units up), device [un]mount chain, module load/unload chain, + * low memory chain, screenblank chain (for plug in modular screenblankers) + * VC switch chains (for loadable kernel svgalib VC switch helpers) etc... + */ + +/* netdevice notifier chain */ +#define NETDEV_UP 0x0001 /* For now you can't veto a device up/down */ +#define NETDEV_DOWN 0x0002 +#define NETDEV_REBOOT 0x0003 /* Tell a protocol stack a network interface + detected a hardware crash and restarted + - we can use this eg to kick tcp sessions + once done */ +#endif +#endif diff -u --recursive --new-file v1.1.76/linux/include/linux/pci.h linux/include/linux/pci.h --- v1.1.76/linux/include/linux/pci.h Sat Dec 3 12:02:09 1994 +++ linux/include/linux/pci.h Sat Jan 7 12:57:43 1995 @@ -115,6 +115,79 @@ #define PCI_MIN_GNT 0x3e /* 8 bits */ #define PCI_MAX_LAT 0x3f /* 8 bits */ +#define PCI_CLASS_NOT_DEFINED 0x0000 +#define PCI_CLASS_NOT_DEFINED_VGA 0x0001 + +#define PCI_CLASS_STORAGE_SCSI 0x0100 +#define PCI_CLASS_STORAGE_IDE 0x0101 +#define PCI_CLASS_STORAGE_FLOPPY 0x0102 +#define PCI_CLASS_STORAGE_IPI 0x0103 +#define PCI_CLASS_STORAGE_OTHER 0x0180 + +#define PCI_CLASS_NETWORK_ETHERNET 0x0200 +#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 +#define PCI_CLASS_NETWORK_FDDI 0x0202 +#define PCI_CLASS_NETWORK_OTHER 0x0280 + +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_CLASS_DISPLAY_XGA 0x0301 +#define PCI_CLASS_DISPLAY_OTHER 0x0380 + +#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 +#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 +#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 + +#define PCI_CLASS_MEMORY_RAM 0x0500 +#define PCI_CLASS_MEMORY_FLASH 0x0501 +#define PCI_CLASS_MEMORY_OTHER 0x0580 + +#define PCI_CLASS_BRIDGE_HOST 0x0600 +#define PCI_CLASS_BRIDGE_ISA 0x0601 +#define PCI_CLASS_BRIDGE_EISA 0x0602 +#define PCI_CLASS_BRIDGE_MC 0x0603 +#define PCI_CLASS_BRIDGE_PCI 0x0604 +#define PCI_CLASS_BRIDGE_PCMCIA 0x0605 +#define PCI_CLASS_BRIDGE_OTHER 0x0680 + +#define PCI_CLASS_OTHERS 0xff + +struct pci_class_type { + unsigned long class_id; + char *class_name; +}; + +#define PCI_CLASS_NUM 27 +#define PCI_CLASS_TYPE { \ + {PCI_CLASS_NOT_DEFINED, "Old unidentified device"}, \ + {PCI_CLASS_NOT_DEFINED_VGA, "Old VGA controller"}, \ + {PCI_CLASS_STORAGE_SCSI, "SCSI bus controller"}, \ + {PCI_CLASS_STORAGE_IDE, "IDE controller"}, \ + {PCI_CLASS_STORAGE_FLOPPY, "Floppy controller"}, \ + {PCI_CLASS_STORAGE_IPI, "IPI bus controller"}, \ + {PCI_CLASS_STORAGE_OTHER, "Unknown mass storage controller"}, \ + {PCI_CLASS_NETWORK_ETHERNET, "Ethernet controller"}, \ + {PCI_CLASS_NETWORK_TOKEN_RING, "Token ring controller"}, \ + {PCI_CLASS_NETWORK_FDDI, "FDDI controller"}, \ + {PCI_CLASS_NETWORK_OTHER, "Unknown network controller"}, \ + {PCI_CLASS_DISPLAY_VGA, "VGA display controller"}, \ + {PCI_CLASS_DISPLAY_XGA, "XGA display controller"}, \ + {PCI_CLASS_DISPLAY_OTHER, "Unknown display controller"}, \ + {PCI_CLASS_MULTIMEDIA_VIDEO, "Video device"}, \ + {PCI_CLASS_MULTIMEDIA_AUDIO, "Audio device"}, \ + {PCI_CLASS_MULTIMEDIA_OTHER, "Unknown multimedia device"}, \ + {PCI_CLASS_MEMORY_RAM, "RAM controller"}, \ + {PCI_CLASS_MEMORY_FLASH, "FLASH controller"}, \ + {PCI_CLASS_MEMORY_OTHER, "Unknown memory controller"}, \ + {PCI_CLASS_BRIDGE_HOST, "Host bridge"}, \ + {PCI_CLASS_BRIDGE_ISA, "ISA bridge"}, \ + {PCI_CLASS_BRIDGE_EISA, "EISA bridge"}, \ + {PCI_CLASS_BRIDGE_MC, "MC bridge"}, \ + {PCI_CLASS_BRIDGE_PCI, "PCI to PCI bridge"}, \ + {PCI_CLASS_BRIDGE_PCMCIA, "PCMCIA bridge"}, \ + {PCI_CLASS_BRIDGE_OTHER, "Unknown bridge device"}, \ + {0, "Unknown type of PCI device"} \ +} + #define PCI_VENDOR_ID_NCR 0x1000 #define PCI_DEVICE_ID_NCR_53C810 0x0001 #define PCI_DEVICE_ID_NCR_53C815 0x0004 @@ -123,6 +196,129 @@ #define PCI_VENDOR_ID_ADAPTEC 0x9004 #define PCI_DEVICE_ID_ADAPTEC_2940 0x7178 + +#define PCI_VENDOR_ID_S3 0x5333 +#define PCI_DEVICE_ID_S3_864_1 0x88c0 +#define PCI_DEVICE_ID_S3_864_2 0x88c1 +#define PCI_DEVICE_ID_S3_928 0x88b0 +#define PCI_DEVICE_ID_S3_964 0x88d0 +#define PCI_DEVICE_ID_S3_811 0x8811 + +#define PCI_VENDOR_ID_OPTI 0x1045 +#define PCI_DEVICE_ID_OPTI_82C822 0xc822 +#define PCI_DEVICE_ID_OPTI_82C621 0xc621 + +#define PCI_VENDOR_ID_UMC 0x1060 +#define PCI_DEVICE_ID_UMC_UM8881F 0x8881 +#define PCI_DEVICE_ID_UMC_UM8886F 0x8886 +#define PCI_DEVICE_ID_UMC_UM8673F 0x0101 + +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_DEVICE_ID_DEC_TULIP 0x0002 +#define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009 +#define PCI_DEVICE_ID_DEC_FDDI 0x000F + +#define PCI_VENDOR_ID_MATROX 0x102B + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_82378 0x0484 +#define PCI_DEVICE_ID_INTEL_82424 0x0483 +#define PCI_DEVICE_ID_INTEL_82375 0x0482 +#define PCI_DEVICE_ID_INTEL_82434 0x04a3 + +#define PCI_VENDOR_ID_SMC 0x1042 +#define PCI_DEVICE_ID_SMC_37C665 0x1000 + +#define PCI_VENDOR_ID_ATI 0x1002 +#define PCI_DEVICE_ID_ATI_M32 0x4158 +#define PCI_DEVICE_ID_ATI_M64 0x4758 + +#define PCI_VENDOR_ID_DIAMOND 0x100e +#define PCI_DEVICE_ID_DIAMOND_VIPER 0x9001 + +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#define PCI_DEVICE_ID_CIRRUS_5434 0x00A4 + +#define PCI_VENDOR_ID_BUSLOGIC 0x104B +#define PCI_DEVICE_ID_BUSLOGIC_946C 0x0140 + +#define PCI_VENDOR_ID_N9 0x105D +#define PCI_DEVICE_ID_N9_I128 0x2309 + +#define PCI_VENDOR_ID_ALI 0x1025 +#define PCI_DEVICE_ID_ALI_M1435 0x1435 + +#define PCI_VENDOR_ID_TSENG 0x100c +#define PCI_DEVICE_ID_TSENG_W32P 0x3205 + +struct pci_vendor_type { + unsigned short vendor_id; + char *vendor_name; +}; + + +#define PCI_VENDOR_NUM 16 +#define PCI_VENDOR_TYPE { \ + {PCI_VENDOR_ID_NCR, "NCR"}, \ + {PCI_VENDOR_ID_ADAPTEC, "Adaptec"}, \ + {PCI_VENDOR_ID_S3, "S3 Inc."}, \ + {PCI_VENDOR_ID_OPTI, "OPTI"}, \ + {PCI_VENDOR_ID_UMC, "UMC"}, \ + {PCI_VENDOR_ID_DEC, "DEC"}, \ + {PCI_VENDOR_ID_MATROX, "Matrox"}, \ + {PCI_VENDOR_ID_INTEL, "Intel"}, \ + {PCI_VENDOR_ID_SMC, "SMC"}, \ + {PCI_VENDOR_ID_ATI, "ATI"}, \ + {PCI_VENDOR_ID_DIAMOND, "Diamond"}, \ + {PCI_VENDOR_ID_CIRRUS, "Cirrus Logic"}, \ + {PCI_VENDOR_ID_BUSLOGIC, "Bus Logic"}, \ + {PCI_VENDOR_ID_N9, "Number #9"}, \ + {PCI_VENDOR_ID_ALI, "ALI"}, \ + {PCI_VENDOR_ID_TSENG, "Tseng'Lab"}, \ + {0, "Unknown vendor"} \ +} + +struct pci_device_type { + unsigned short vendor_id; + unsigned short device_id; + char *device_name; +}; + +#define PCI_DEVICE_NUM 31 +#define PCI_DEVICE_TYPE { \ + {PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, "53c810"}, \ + {PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C815, "53c815"}, \ + {PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C820, "53c820"}, \ + {PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C825, "53c825"}, \ + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_2940, "2940"}, \ + {PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_864_1, "Vision 864-P"}, \ + {PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_864_2, "Vision 864-P"}, \ + {PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_928, "Vision 928-P"}, \ + {PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_964, "Vision 964-P"}, \ + {PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_811, "Trio64"}, \ + {PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C822, "82C822"}, \ + {PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, "82C621"}, \ + {PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8881F, "UM8881F"}, \ + {PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886F, "UM8886F"}, \ + {PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F, "UM8673F"}, \ + {PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, "DC21040"}, \ + {PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST, "DC21040"}, \ + {PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_FDDI, "DEFPA"}, \ + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82378, "82378IB"}, \ + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82424, "82424ZX"}, \ + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82375, "82375EB"}, \ + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82434, "82434LX"}, \ + {PCI_VENDOR_ID_SMC, PCI_DEVICE_ID_SMC_37C665, "FDC 37C665"}, \ + {PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_M32, "Mach 32"}, \ + {PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_M64, "Mach 64"}, \ + {PCI_VENDOR_ID_DIAMOND, PCI_DEVICE_ID_DIAMOND_VIPER, "Viper"}, \ + {PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5434, "GD 5434"}, \ + {PCI_VENDOR_ID_BUSLOGIC,PCI_DEVICE_ID_BUSLOGIC_946C, "946C"}, \ + {PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, "Imagine 128"}, \ + {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_M1435, "M1435"}, \ + {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_TSENG_W32P, "ET4000W32P"}, \ + {0,0,"UNKNOWN DEVICE.PLEASE FIND OUT AND MAIL POTTER@CAO-VLSI.IBP.FR"} \ +} /* PCI BIOS */ diff -u --recursive --new-file v1.1.76/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h --- v1.1.76/linux/include/linux/proc_fs.h Tue Dec 27 09:54:43 1994 +++ linux/include/linux/proc_fs.h Thu Jan 5 13:47:08 1995 @@ -1,6 +1,8 @@ #ifndef _LINUX_PROC_FS_H #define _LINUX_PROC_FS_H +#include + /* * The proc filesystem constants/structures */ diff -u --recursive --new-file v1.1.76/linux/include/linux/ptrace.h linux/include/linux/ptrace.h --- v1.1.76/linux/include/linux/ptrace.h Thu Apr 21 13:23:03 1994 +++ linux/include/linux/ptrace.h Wed Jan 4 21:16:05 1995 @@ -21,49 +21,6 @@ #define PTRACE_SYSCALL 24 -/* use ptrace (3 or 6, pid, PT_EXCL, data); to read or write - the processes registers. */ - -#define EBX 0 -#define ECX 1 -#define EDX 2 -#define ESI 3 -#define EDI 4 -#define EBP 5 -#define EAX 6 -#define DS 7 -#define ES 8 -#define FS 9 -#define GS 10 -#define ORIG_EAX 11 -#define EIP 12 -#define CS 13 -#define EFL 14 -#define UESP 15 -#define SS 16 - - -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct pt_regs { - long ebx; - long ecx; - long edx; - long esi; - long edi; - long ebp; - long eax; - unsigned short ds, __dsu; - unsigned short es, __esu; - unsigned short fs, __fsu; - unsigned short gs, __gsu; - long orig_eax; - long eip; - unsigned short cs, __csu; - long eflags; - long esp; - unsigned short ss, __ssu; -}; +#include #endif diff -u --recursive --new-file v1.1.76/linux/include/linux/sbpcd.h linux/include/linux/sbpcd.h --- v1.1.76/linux/include/linux/sbpcd.h Mon Dec 19 17:26:24 1994 +++ linux/include/linux/sbpcd.h Wed Jan 4 08:12:47 1995 @@ -121,7 +121,8 @@ #define DBG_SEQ 26 /* Sequoia interface configuration trace */ #define DBG_LCS 27 /* Longshine LCS-7260 debugging trace */ #define DBG_TEA 28 /* TEAC CD-55A debugging trace */ -#define DBG_000 29 /* unnecessary information */ +#define DBG_CD2 29 /* MKE CD200 debugging trace */ +#define DBG_000 30 /* unnecessary information */ /*==========================================================================*/ /*==========================================================================*/ @@ -129,25 +130,27 @@ /* * bits of flags_cmd_out: */ -#define f_respo3 0x100 -#define f_putcmd 0x80 -#define f_respo2 0x40 -#define f_lopsta 0x20 -#define f_getsta 0x10 -#define f_ResponseStatus 0x08 -#define f_obey_p_check 0x04 -#define f_bit1 0x02 -#define f_wait_if_busy 0x01 +#define f_respo3 0x100 +#define f_putcmd 0x80 +#define f_respo2 0x40 +#define f_lopsta 0x20 +#define f_getsta 0x10 +#define f_ResponseStatus 0x08 +#define f_obey_p_check 0x04 +#define f_bit1 0x02 +#define f_wait_if_busy 0x01 /* * diskstate_flags: */ -#define upc_bit 0x40 -#define volume_bit 0x20 -#define toc_bit 0x10 -#define cd_size_bit 0x04 -#define subq_bit 0x02 -#define frame_size_bit 0x01 +#define x80_bit 0x80 +#define upc_bit 0x40 +#define volume_bit 0x20 +#define toc_bit 0x10 +#define multisession_bit 0x08 +#define cd_size_bit 0x04 +#define subq_bit 0x02 +#define frame_size_bit 0x01 /* * disk states (bits of diskstate_flags): @@ -159,7 +162,6 @@ #define subq_valid (DriveStruct[d].diskstate_flags&subq_bit) #define frame_size_valid (DriveStruct[d].diskstate_flags&frame_size_bit) - /* * bits of the status_byte (result of xx_ReadStatus): */ @@ -181,6 +183,36 @@ #define p_busy_old 0x04 /* + * "generation specific" defs of the status_byte: + */ +#define p0_door_closed 0x80 +#define p0_caddy_in 0x40 +#define p0_spinning 0x20 +#define p0_check 0x10 +#define p0_success 0x08 /* unused */ +#define p0_busy 0x04 +#define p0_bit_1 0x02 /* unused */ +#define p0_disk_ok 0x01 + +#define p1_door_closed 0x80 +#define p1_disk_in 0x40 +#define p1_spinning 0x20 +#define p1_check 0x10 +#define p1_busy 0x08 +#define p1_door_locked 0x04 +#define p1_bit_1 0x02 /* unused */ +#define p1_disk_ok 0x01 + +#define p2_disk_ok 0x80 +#define p2_door_locked 0x40 +#define p2_spinning 0x20 +#define p2_busy2 0x10 +#define p2_busy1 0x08 +#define p2_door_closed 0x04 +#define p2_disk_in 0x02 +#define p2_check 0x01 + +/* * used drive states: */ #define st_door_closed (DriveStruct[d].status_byte&p_door_closed) @@ -207,24 +239,32 @@ /* * drive types (firmware versions): */ -#define drv_old 0x10 /* CR-52x family */ -#define drv_199 0x11 /* <200 */ -#define drv_200 0x12 /* <201 */ -#define drv_201 0x13 /* <210 */ -#define drv_210 0x14 /* <211 */ -#define drv_211 0x15 /* <300 */ -#define drv_300 0x16 /* >=300 */ - -#define drv_lcs 0x20 /* Longshine family */ -#define drv_260 0x21 /* LCS-7260 */ - -#define drv_new 0x40 /* CR-56x family */ -#define drv_099 0x41 /* <100 */ -#define drv_100 0x42 /* >=100 */ - -#define old_drive (DriveStruct[d].drv_type&drv_old) -#define lcs_drive (DriveStruct[d].drv_type&drv_lcs) -#define new_drive (DriveStruct[d].drv_type&drv_new) +#define drv_fam0 0x08 /* CR-52x family */ +#define drv_199 (drv_fam0+0x01) /* <200 */ +#define drv_200 (drv_fam0+0x02) /* <201 */ +#define drv_201 (drv_fam0+0x03) /* <210 */ +#define drv_210 (drv_fam0+0x04) /* <211 */ +#define drv_211 (drv_fam0+0x05) /* <300 */ +#define drv_300 (drv_fam0+0x06) /* >=300 */ + +#define drv_famL 0x10 /* Longshine family */ +#define drv_260 (drv_famL+0x01) /* LCS-7260 */ + +#define drv_fam1 0x20 /* CR-56x family */ +#define drv_099 (drv_fam1+0x01) /* <100 */ +#define drv_100 (drv_fam1+0x02) /* >=100 */ + +#define drv_famT 0x40 /* TEAC CD-55A */ +#define drv_fam2 0x80 /* CD200 family */ + +#define fam0_drive (DriveStruct[d].drv_type&drv_fam0) +#define famL_drive (DriveStruct[d].drv_type&drv_famL) +#define fam1_drive (DriveStruct[d].drv_type&drv_fam1) +#define famT_drive (DriveStruct[d].drv_type&drv_famT) +#define fam2_drive (DriveStruct[d].drv_type&drv_fam2) +#define fam0L_drive (DriveStruct[d].drv_type&(drv_fam0|drv_famL)) +#define fam1L_drive (DriveStruct[d].drv_type&(drv_fam1|drv_famL)) +#define fam01_drive (DriveStruct[d].drv_type&(drv_fam0|drv_fam1)) /* * audio states: @@ -235,12 +275,10 @@ /* * drv_pattern, drv_options: */ -#define speed_auto 0x80 -#define speed_300 0x40 -#define speed_150 0x20 -#define sax_a 0x04 -#define sax_xn2 0x02 -#define sax_xn1 0x01 +#define speed_auto 0x80 +#define speed_300 0x40 +#define speed_150 0x20 +#define audio_mono 0x04 /* * values of cmd_type (0 else): @@ -251,10 +289,11 @@ #define READ_AU 0x08 /* "audio frame": 2352 bytes per frame */ /* - * sense byte: used only if new_drive - * only during cmd 09 00 xx ah al 00 00 + * sense_byte: * * values: 00 + * 01 + * 81 * 82 "raw audio" mode * xx from infobuf[0] after 85 00 00 00 00 00 00 */ @@ -274,6 +313,57 @@ #define aud_14 0x14 /* Audio play operation stopped due to error */ #define aud_15 0x15 /* No current audio status to return */ + +/* + * highest allowed drive number (MINOR+1) + */ +#define NR_SBPCD 4 + +/* + * we try to never disable interrupts - seems to work + */ +#define SBPCD_DIS_IRQ 0 + +/* + * "write byte to port" + */ +#define OUT(x,y) outb(y,x) + +/* + * use "REP INSB" for strobing the data in: + */ +#define READ_DATA(port, buf, nr) insb(port, buf, nr) + +/*==========================================================================*/ + +#define MIXER_CD_Volume 0x28 /* internal SB Pro register address */ + +/*==========================================================================*/ +/* + * Creative Labs Programmers did this: + */ +#define MAX_TRACKS 120 /* why more than 99? */ + +/*==========================================================================*/ +/* + * To make conversions easier (machine dependent!) + */ +typedef union _msf +{ + u_int n; + u_char c[4]; +} +MSF; + +typedef union _blk +{ + u_int n; + u_char c[4]; +} +BLK; + +/*==========================================================================*/ + /*============================================================================ ============================================================================== @@ -425,56 +515,136 @@ ============================================================================== ============================================================================*/ -/*==========================================================================*/ -/*==========================================================================*/ - -/* - * highest allowed drive number (MINOR+1) - */ -#define NR_SBPCD 4 - -/* - * we try to never disable interrupts - seems to work - */ -#define SBPCD_DIS_IRQ 0 - -/* - * "write byte to port" - */ -#define OUT(x,y) outb(y,x) - -/* - * use "REP INSB" for strobing the data in: - */ -#define READ_DATA(port, buf, nr) insb(port, buf, nr) - -/*==========================================================================*/ - -#define MIXER_CD_Volume 0x28 /* internal SB Pro register address */ - -/*==========================================================================*/ /* - * Creative Labs Programmers did this: - */ -#define MAX_TRACKS 120 /* why more than 99? */ + * commands + * + * CR-52x: CMD0_ + * CR-56x: CMD1_ + * CD200: CMD2_ + * LCS-7260: CMDL_ + * TEAC CD-55A: CMDT_ + */ +#define CMD1_RESET 0x0a +#define CMD2_RESET 0x01 +#define CMDL_RESET 0x0a +#define CMDT_RESET 0xc0 +#define CMD1_LOCK_CTL 0x0c +#define CMD2_LOCK_CTL 0x1e +#define CMDL_LOCK_CTL 0x0e +#define CMDT_LOCK_CTL 0x1e +#define CMD1_TRAY_CTL 0x07 +#define CMD2_TRAY_CTL 0x1b +#define CMDL_TRAY_CTL 0x0d +#define CMDT_TRAY_CTL 0x1b +#define CMD1_MULTISESS 0x8d +#define CMD2_MULTISESS 0x43 +#define CMDL_MULTISESS 0x8c +#define CMD1_SUBCHANINF 0x11 +#define CMD2_SUBCHANINF 0x +#define CMD2_x02 0x02 +#define CMD1_x08 0x08 +#define CMD2_x08 0x08 +#define CMDT_x08 0x08 +#define CMD2_xD4 0xd4 +#define CMD2_xDA 0xda + +#define CMD0_PATH_CHECK 0x00 +#define CMD1_PATH_CHECK 0x00 +#define CMD2_PATH_CHECK 0x +#define CMDL_PATH_CHECK 0x00 +#define CMD0_SEEK 0x01 +#define CMD1_SEEK 0x01 +#define CMD2_SEEK 0x2b +#define CMDL_SEEK 0x01 +#define CMDT_SEEK 0x2b +#define CMD0_READ 0x02 +#define CMD1_READ 0x10 +#define CMD2_READ 0x28 +#define CMDL_READ 0x02 +#define CMDT_READ 0x28 +#define CMD0_READ_XA 0x03 +#define CMDL_READ_XA 0x03 /* really ?? */ +#define CMD0_READ_HEAD 0x04 +#define CMD0_SPINUP 0x05 +#define CMD1_SPINUP 0x02 +#define CMD2_SPINUP CMD2_TRAY_CTL +#define CMDL_SPINUP 0x05 +#define CMD0_SPINDOWN 0x06 /* really??? */ +#define CMD1_SPINDOWN 0x06 +#define CMD2_SPINDOWN CMD2_TRAY_CTL +#define CMDL_SPINDOWN 0x0d +#define CMD0_DIAG 0x07 +#define CMD0_READ_UPC 0x08 +#define CMD1_READ_UPC 0x88 +#define CMD2_READ_UPC 0x +#define CMDL_READ_UPC 0x08 +#define CMD0_READ_ISRC 0x09 +#define CMD0_PLAY 0x0a +#define CMD1_PLAY 0x +#define CMD2_PLAY 0x +#define CMDL_PLAY 0x0a +#define CMD0_PLAY_MSF 0x0b +#define CMD1_PLAY_MSF 0x0e +#define CMD2_PLAY_MSF 0x47 +#define CMDL_PLAY_MSF 0x +#define CMDT_PLAY_MSF 0x47 +#define CMD0_PLAY_TI 0x0c +#define CMD0_STATUS 0x81 +#define CMD1_STATUS 0x05 +#define CMD2_STATUS 0x00 +#define CMDL_STATUS 0x81 +#define CMDT_STATUS 0x00 +#define CMD0_READ_ERR 0x82 +#define CMD1_READ_ERR 0x82 +#define CMD2_READ_ERR 0x03 +#define CMDL_READ_ERR 0x82 +#define CMDT_READ_ERR 0x03 /* get audio status */ +#define CMD0_READ_VER 0x83 +#define CMD1_READ_VER 0x83 +#define CMD2_READ_VER 0x12 +#define CMDT_READ_VER 0x12 /* ??? (unused) */ +#define CMDL_READ_VER 0x83 +#define CMD0_SETMODE 0x84 +#define CMD1_SETMODE 0x09 +#define CMD2_SETMODE 0x55 +#define CMDL_SETMODE 0x84 +#define CMDT_SETMODE 0x55 +#define CMD0_GETMODE 0x85 +#define CMD1_GETMODE 0x84 +#define CMD2_GETMODE 0x5a +#define CMDL_GETMODE 0x85 +#define CMDT_GETMODE 0x5a +#define CMD0_SET_XA 0x86 +#define CMD0_GET_XA 0x87 +#define CMD0_CAPACITY 0x88 +#define CMD1_CAPACITY 0x85 +#define CMD2_CAPACITY 0x25 +#define CMDL_CAPACITY 0x88 +#define CMD0_READSUBQ 0x89 +#define CMD1_READSUBQ 0x87 +#define CMD2_READSUBQ 0x42 +#define CMDL_READSUBQ 0x89 +#define CMDT_READSUBQ 0x42 +#define CMD0_DISKCODE 0x8a +#define CMD0_DISKINFO 0x8b +#define CMD1_DISKINFO 0x8b +#define CMD2_DISKINFO 0x +#define CMDL_DISKINFO 0x8b +#define CMDT_DISKINFO 0x43 +#define CMD0_READTOC 0x8c +#define CMD1_READTOC 0x8c +#define CMD2_READTOC 0x +#define CMDL_READTOC 0x8c +#define CMD0_PAU_RES 0x8d +#define CMD1_PAU_RES 0x0d +#define CMD2_PAU_RES 0x4b +#define CMDL_PAU_RES 0x8d +#define CMDT_PAU_RES 0x4b +#define CMD0_PACKET 0x8e +#define CMD1_PACKET 0x8e +#define CMD2_PACKET 0x +#define CMDL_PACKET 0x8e /*==========================================================================*/ -/* - * To make conversions easier (machine dependent!) - */ -typedef union _msf -{ - u_int n; - u_char c[4]; -} -MSF; - -typedef union _blk -{ - u_int n; - u_char c[4]; -} -BLK; - /*==========================================================================*/ #endif _LINUX_SBPCD_H diff -u --recursive --new-file v1.1.76/linux/include/linux/sched.h linux/include/linux/sched.h --- v1.1.76/linux/include/linux/sched.h Fri Dec 30 08:29:42 1994 +++ linux/include/linux/sched.h Wed Jan 4 21:16:06 1995 @@ -264,6 +264,14 @@ extern int request_irq(unsigned int irq,void (*handler)(int), unsigned long flags, const char *device); extern void free_irq(unsigned int irq); +extern unsigned long copy_thread(int, unsigned long, struct task_struct *, struct pt_regs *); +extern void start_thread(struct pt_regs *, unsigned long pc, unsigned long sp); +extern void flush_thread(void); +extern void exit_thread(void); + +extern int do_execve(char *, char **, char **, struct pt_regs *); +asmlinkage int do_signal(unsigned long, struct pt_regs *); + /* * The wait-queues are circular lists, and you have to be *very* sure * to keep them correct. Use only these two functions to add/remove diff -u --recursive --new-file v1.1.76/linux/include/linux/skbuff.h linux/include/linux/skbuff.h --- v1.1.76/linux/include/linux/skbuff.h Wed Nov 30 22:40:54 1994 +++ linux/include/linux/skbuff.h Thu Jan 5 13:47:08 1995 @@ -16,6 +16,7 @@ #include #include #include +#include #undef CONFIG_SKB_CHECK diff -u --recursive --new-file v1.1.76/linux/include/linux/string.h linux/include/linux/string.h --- v1.1.76/linux/include/linux/string.h Wed Dec 28 16:38:30 1994 +++ linux/include/linux/string.h Wed Jan 4 21:16:06 1995 @@ -11,6 +11,7 @@ extern "C" { #endif +extern char * ___strtok; extern char * strcpy(char *,const char *); extern char * strncpy(char *,const char *,size_t); extern char * strcat(char *, const char *); @@ -18,6 +19,7 @@ extern char * strchr(const char *,char); extern char * strpbrk(const char *,const char *); extern char * strtok(char *,const char *); +extern char * strstr(const char *,const char *); extern size_t strlen(const char *); extern size_t strspn(const char *,const char *); extern int strcmp(const char *,const char *); @@ -26,6 +28,7 @@ extern void * memset(void *,char,size_t); extern void * memcpy(void *,const void *,size_t); extern void * memmove(void *,const void *,size_t); +extern void * memscan(void *, unsigned char, size_t); extern int memcmp(const void *,const void *,size_t); /* diff -u --recursive --new-file v1.1.76/linux/include/linux/sysv_fs_sb.h linux/include/linux/sysv_fs_sb.h --- v1.1.76/linux/include/linux/sysv_fs_sb.h Sat Nov 5 13:32:13 1994 +++ linux/include/linux/sysv_fs_sb.h Wed Jan 4 19:11:02 1995 @@ -57,6 +57,7 @@ unsigned long *s_sb_flc_blocks; /* pointer to s_sbd->s_free */ unsigned long *s_sb_total_free_blocks;/* pointer to s_sbd->s_tfree */ unsigned long *s_sb_time; /* pointer to s_sbd->s_time */ + unsigned long *s_sb_state; /* pointer to s_sbd->s_state */ /* We keep those superblock entities that don't change here; this saves us an indirection and perhaps a conversion. */ unsigned long s_firstinodezone; /* index of first inode zone */ @@ -110,6 +111,7 @@ #define sv_sb_flc_blocks u.sysv_sb.s_sb_flc_blocks #define sv_sb_total_free_blocks u.sysv_sb.s_sb_total_free_blocks #define sv_sb_time u.sysv_sb.s_sb_time +#define sv_sb_state u.sysv_sb.s_sb_state #define sv_firstinodezone u.sysv_sb.s_firstinodezone #define sv_firstdatazone u.sysv_sb.s_firstdatazone #define sv_ninodes u.sysv_sb.s_ninodes diff -u --recursive --new-file v1.1.76/linux/init/main.c linux/init/main.c --- v1.1.76/linux/init/main.c Sun Jan 1 16:28:20 1995 +++ linux/init/main.c Sat Jan 7 12:58:21 1995 @@ -81,15 +81,6 @@ unsigned long net_dev_init(unsigned long, unsigned long); extern long bios32_init(long, long); -#ifdef CONFIG_BLK_DEV_IDE -extern void ide_setup(char *str, int *ints); -extern void hda_setup(char *str, int *ints); -extern void hdb_setup(char *str, int *ints); -extern void hdc_setup(char *str, int *ints); -extern void hdd_setup(char *str, int *ints); -#else -extern void hd_setup(char *str, int *ints); -#endif /* CONFIG_BLK_DEV_IDE */ extern void bmouse_setup(char *str, int *ints); extern void eth_setup(char *str, int *ints); extern void xd_setup(char *str, int *ints); @@ -107,6 +98,7 @@ extern void buslogic_setup(char *str, int *ints); extern void scsi_luns_setup(char *str, int *ints); extern void sound_setup(char *str, int *ints); +extern void sock_setup(char *str, int *ints); #ifdef CONFIG_SBPCD extern void sbpcd_setup(char *str, int *ints); #endif CONFIG_SBPCD diff -u --recursive --new-file v1.1.76/linux/ipc/Makefile linux/ipc/Makefile --- v1.1.76/linux/ipc/Makefile Wed Dec 1 14:44:15 1993 +++ linux/ipc/Makefile Wed Jan 4 21:16:06 1995 @@ -28,6 +28,7 @@ dep: $(CPP) -M $(SRCS) > .depend +modules: dummy: # diff -u --recursive --new-file v1.1.76/linux/kernel/Makefile linux/kernel/Makefile --- v1.1.76/linux/kernel/Makefile Tue Aug 23 09:55:17 1994 +++ linux/kernel/Makefile Wed Jan 4 21:16:06 1995 @@ -16,10 +16,8 @@ .c.o: $(CC) $(CFLAGS) -c $< -OBJS = sched.o entry.o traps.o irq.o dma.o fork.o exec_domain.o \ - panic.o printk.o vsprintf.o sys.o module.o ksyms.o exit.o \ - signal.o ptrace.o ioport.o itimer.o \ - info.o ldt.o time.o tqueue.o vm86.o bios32.o splx.o +OBJS = sched.o dma.o fork.o exec_domain.o panic.o printk.o vsprintf.o sys.o \ + module.o ksyms.o exit.o signal.o itimer.o info.o time.o softirq.o all: kernel.o @@ -27,10 +25,6 @@ $(LD) -r -o kernel.o $(OBJS) sync -entry.s: entry.S - -entry.o: entry.s - sched.o: sched.c $(CC) $(CFLAGS) $(PROFILING) -fno-omit-frame-pointer -c $< @@ -38,6 +32,7 @@ $(CPP) -M *.c > .depend dummy: +modules: # # include a dependency file if one exists diff -u --recursive --new-file v1.1.76/linux/kernel/bios32.c linux/kernel/bios32.c --- v1.1.76/linux/kernel/bios32.c Sat Dec 3 10:48:14 1994 +++ linux/kernel/bios32.c Thu Jan 1 02:00:00 1970 @@ -1,466 +0,0 @@ -/* - * 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 - * - * 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; - } -} - -#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); - } -#endif - return memory_start; -} diff -u --recursive --new-file v1.1.76/linux/kernel/dma.c linux/kernel/dma.c --- v1.1.76/linux/kernel/dma.c Sun Jan 1 19:49:19 1995 +++ linux/kernel/dma.c Wed Jan 4 21:16:06 1995 @@ -13,6 +13,7 @@ #include #include #include +#include /* A note on resource allocation: @@ -32,7 +33,7 @@ -/* Channel n is busy iff dma_chan_busy[n] != 0. +/* Channel n is busy iff dma_chan_busy[n].lock != 0. * DMA0 used to be reserved for DRAM refresh, but apparently not any more... * DMA4 is reserved for cascading. */ @@ -42,7 +43,7 @@ char *device_id; }; -static volatile struct dma_chan dma_chan_busy[MAX_DMA_CHANNELS] = { +static struct dma_chan dma_chan_busy[MAX_DMA_CHANNELS] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, @@ -53,31 +54,6 @@ { 0, 0 } }; -/* Atomically swap memory location [32 bits] with `newval'. - * This avoid the cli()/sti() junk and related problems. - * [And it's faster too :-)] - * Maybe this should be in include/asm/mutex.h and be used for - * implementing kernel-semaphores as well. - */ -static __inline__ unsigned int mutex_atomic_swap(volatile unsigned int * p, unsigned int newval) -{ - unsigned int semval = newval; - - /* If one of the operands for the XCHG instructions is a memory ref, - * it makes the swap an uninterruptible RMW cycle. - * - * One operand must be in memory, the other in a register, otherwise - * the swap may not be atomic. - */ - - asm __volatile__ ("xchgl %2, %0\n" - : /* outputs: semval */ "=r" (semval) - : /* inputs: newval, p */ "0" (semval), "m" (*p) - ); /* p is a var, containing an address */ - return semval; -} /* mutex_atomic_swap */ - - int get_dma_list(char *buf) { int i, len = 0; @@ -98,7 +74,7 @@ if (dmanr >= MAX_DMA_CHANNELS) return -EINVAL; - if (mutex_atomic_swap((unsigned int *) &dma_chan_busy[dmanr].lock, 1) != 0) + if (xchg_u32(&dma_chan_busy[dmanr].lock, 1) != 0) return -EBUSY; dma_chan_busy[dmanr].device_id = device_id; @@ -115,7 +91,7 @@ return; } - if (mutex_atomic_swap((unsigned int *) &dma_chan_busy[dmanr].lock, 0) == 0) { + if (xchg_u32(&dma_chan_busy[dmanr].lock, 0) == 0) { printk("Trying to free free DMA%d\n", dmanr); return; } diff -u --recursive --new-file v1.1.76/linux/kernel/exit.c linux/kernel/exit.c --- v1.1.76/linux/kernel/exit.c Wed Nov 30 17:17:39 1994 +++ linux/kernel/exit.c Wed Jan 4 21:16:06 1995 @@ -371,17 +371,6 @@ mpnt = next; } - /* 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); - } - free_page_tables(current); } @@ -416,6 +405,7 @@ exit_mm(); exit_files(); exit_fs(); + exit_thread(); forget_original_parent(current); /* * Check to see if any process groups have become orphaned diff -u --recursive --new-file v1.1.76/linux/kernel/fork.c linux/kernel/fork.c --- v1.1.76/linux/kernel/fork.c Fri Dec 30 08:29:42 1994 +++ linux/kernel/fork.c Wed Jan 4 21:16:06 1995 @@ -24,8 +24,6 @@ #include #include -asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call"); - /* These should maybe be in */ #define MAX_TASKS_PER_USER (NR_TASKS/2) @@ -162,8 +160,6 @@ current->fs->root->i_count++; } -#define IS_CLONE (regs.orig_eax == __NR_clone) - /* * Ok, this is the main fork-routine. It copies the system process * information (task[nr]) and sets up the necessary registers. It @@ -171,9 +167,8 @@ */ asmlinkage int sys_fork(struct pt_regs regs) { - struct pt_regs * childregs; + int nr; struct task_struct *p; - int i,nr; unsigned long clone_flags = COPYVM | SIGCHLD; if(!(p = (struct task_struct*)__get_free_page(GFP_KERNEL))) @@ -181,7 +176,6 @@ nr = find_empty_process(); if (nr < 0) goto bad_fork_free; - task[nr] = p; *p = *current; if (p->exec_domain && p->exec_domain->use_count) @@ -205,58 +199,23 @@ p->utime = p->stime = 0; p->cutime = p->cstime = 0; p->start_time = jiffies; -/* - * set up new TSS and kernel stack - */ + task[nr] = p; + + /* build new kernel stack */ if (!(p->kernel_stack_page = get_free_page(GFP_KERNEL))) goto bad_fork_cleanup; *(unsigned long *)p->kernel_stack_page = STACK_MAGIC; - 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->exit_signal = clone_flags & CSIGNAL; - 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); - } - 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)); + + /* copy all the process information */ + clone_flags = copy_thread(nr, COPYVM | SIGCHLD, p, ®s); if (copy_mm(clone_flags, p)) goto bad_fork_cleanup; p->semundo = NULL; copy_files(clone_flags, p); copy_fs(clone_flags, p); - 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); + /* ok, now we should be set up.. */ + p->exit_signal = clone_flags & CSIGNAL; p->counter = current->counter >> 1; p->state = TASK_RUNNING; /* do this last, just in case */ return p->pid; diff -u --recursive --new-file v1.1.76/linux/kernel/ioport.c linux/kernel/ioport.c --- v1.1.76/linux/kernel/ioport.c Tue Dec 6 11:23:54 1994 +++ linux/kernel/ioport.c Thu Jan 1 02:00:00 1970 @@ -1,214 +0,0 @@ -/* - * 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 _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; -} - -int get_ioport_list(char *buf) -{ int len=0,num,from; - for(num=0;num4000) { - len+=sprintf((buf+len),"4k-Limit reached!\n"); - return len; - } - } - 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; -} - - -void 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; -} - -void release_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, 0); - return; -} - -int check_region(unsigned int from, unsigned int num) -{ - if (from > 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) - snarf_region(ints[i], ints[i+1]); -} diff -u --recursive --new-file v1.1.76/linux/kernel/irq.c linux/kernel/irq.c --- v1.1.76/linux/kernel/irq.c Sun Jan 1 16:28:20 1995 +++ linux/kernel/irq.c Thu Jan 1 02:00:00 1970 @@ -1,411 +0,0 @@ -/* - * 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; - -unsigned long intr_count = 0; -unsigned long bh_active = 0; -unsigned long bh_mask = 0xFFFFFFFF; -struct bh_struct bh_base[32]; - -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); -} - -/* - * do_bottom_half() runs at normal kernel priority: all interrupts - * enabled. do_bottom_half() is atomic with respect to itself: a - * bottom_half handler need not be re-entrant. - */ -asmlinkage void do_bottom_half(void) -{ - unsigned long active; - unsigned long mask, left; - struct bh_struct *bh; - - bh = bh_base; - active = bh_active & bh_mask; - for (mask = 1, left = ~0 ; left & active ; bh++,mask += mask,left += left) { - if (mask & active) { - void (*fn)(void *); - bh_active &= ~mask; - fn = bh->routine; - if (!fn) - goto bad_bh; - fn(bh->data); - } - } - return; -bad_bh: - printk ("irq.c:bad bottom half entry %08lx\n", mask); -} - -/* - * 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"); - - /* initialize the bottom half routines. */ - for (i = 0; i < 32; i++) { - bh_base[i].routine = NULL; - bh_base[i].data = NULL; - } - bh_active = 0; - intr_count = 0; -} diff -u --recursive --new-file v1.1.76/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v1.1.76/linux/kernel/ksyms.c Tue Dec 27 08:37:13 1994 +++ linux/kernel/ksyms.c Sat Jan 7 12:58:20 1995 @@ -30,6 +30,8 @@ #include #include #include +#include + #ifdef CONFIG_INET #include #include @@ -59,10 +61,6 @@ extern int request_dma(unsigned int dmanr, char * deviceID); extern void free_dma(unsigned int dmanr); -extern int do_execve(char * filename, char ** argv, char ** envp, - struct pt_regs * regs); -extern int do_signal(unsigned long oldmask, struct pt_regs * regs); - extern void (* iABI_hook)(struct pt_regs * regs); struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ @@ -72,7 +70,9 @@ /* system info variables */ X(EISA_bus), +#ifdef __i386__ X(wp_works_ok), +#endif #ifdef CONFIG_PCI /* PCI BIOS support */ @@ -217,7 +217,7 @@ X(send_sig), /* Program loader interfaces */ - X(change_ldt), + X(setup_arg_pages), X(copy_strings), X(create_tables), X(do_execve), @@ -247,6 +247,7 @@ X(kfree_skb), X(dev_kfree_skb), X(snarf_region), + X(register_iomem), X(release_region), X(netif_rx), X(dev_rint), @@ -275,6 +276,8 @@ X(get_hash_table), X(get_empty_inode), X(insert_inode_hash), + X(event), + X(__down), #if defined(CONFIG_MSDOS_FS) && !defined(CONFIG_UMSDOS_FS) /* support for umsdos fs */ X(msdos_create), diff -u --recursive --new-file v1.1.76/linux/kernel/ldt.c linux/kernel/ldt.c --- v1.1.76/linux/kernel/ldt.c Tue Jun 21 14:10:04 1994 +++ linux/kernel/ldt.c Thu Jan 1 02:00:00 1970 @@ -1,103 +0,0 @@ -/* - * linux/kernel/ldt.c - * - * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds - */ - -#include -#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/kernel/module.c linux/kernel/module.c --- v1.1.76/linux/kernel/module.c Mon Nov 28 12:20:44 1994 +++ linux/kernel/module.c Wed Jan 4 21:16:06 1995 @@ -117,7 +117,7 @@ /* * Allocate space for a module. */ -asmlinkage int +asmlinkage unsigned long sys_create_module(char *module_name, unsigned long size) { struct module *mp; @@ -162,7 +162,7 @@ PRINTK(("module `%s' (%lu pages @ 0x%08lx) created\n", mp->name, (unsigned long) mp->size, (unsigned long) mp->addr)); - return (int) addr; + return (unsigned long) addr; } /* @@ -248,7 +248,7 @@ /* relocate name pointers, index referred from start of table */ for (sym = &(newtab->symbol[0]), i = 0; i < newtab->n_symbols; ++sym, ++i) { - if ((int)sym->name < legal_start || size <= (int)sym->name) { + if ((unsigned long)sym->name < legal_start || size <= (unsigned long)sym->name) { printk("Illegal symbol table! Rejected!\n"); kfree_s(newtab, size); return -EINVAL; diff -u --recursive --new-file v1.1.76/linux/kernel/ptrace.c linux/kernel/ptrace.c --- v1.1.76/linux/kernel/ptrace.c Wed Aug 10 19:26:45 1994 +++ linux/kernel/ptrace.c Thu Jan 1 02:00:00 1970 @@ -1,517 +0,0 @@ -/* 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/kernel/sched.c linux/kernel/sched.c --- v1.1.76/linux/kernel/sched.c Wed Dec 28 18:50:44 1994 +++ linux/kernel/sched.c Wed Jan 4 21:16:06 1995 @@ -607,7 +607,7 @@ jiffies++; calc_load(); - if ((VM_MASK & regs->eflags) || (3 & regs->cs)) { + if (user_mode(regs)) { current->utime++; if (current != task[0]) { if (current->priority < 15) diff -u --recursive --new-file v1.1.76/linux/kernel/signal.c linux/kernel/signal.c --- v1.1.76/linux/kernel/signal.c Mon Oct 10 10:33:49 1994 +++ linux/kernel/signal.c Wed Jan 4 21:16:06 1995 @@ -18,8 +18,6 @@ #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) -asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs); - asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset) { sigset_t new_set, old_set = current->blocked; @@ -77,25 +75,6 @@ } /* - * 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; - } -} - -/* * POSIX 3.3.1.3: * "Setting a signal action to SIG_IGN for a signal that is pending * shall cause the pending signal to be discarded, whether or not @@ -129,24 +108,26 @@ } } -asmlinkage int sys_signal(int signum, unsigned long handler) +asmlinkage unsigned long sys_signal(int signum, void (*handler)(int)) { + int err; struct sigaction tmp; if (signum<1 || signum>32) return -EINVAL; if (signum==SIGKILL || signum==SIGSTOP) return -EINVAL; - if (handler >= TASK_SIZE) - return -EFAULT; - tmp.sa_handler = (void (*)(int)) handler; + err = verify_area(VERIFY_READ, handler, 1); + if (err) + return err; + tmp.sa_handler = handler; tmp.sa_mask = 0; tmp.sa_flags = SA_ONESHOT | SA_NOMASK; tmp.sa_restorer = NULL; - handler = (long) current->sigaction[signum-1].sa_handler; + handler = current->sigaction[signum-1].sa_handler; current->sigaction[signum-1] = tmp; check_pending(signum); - return handler; + return (unsigned long) handler; } asmlinkage int sys_sigaction(int signum, const struct sigaction * action, @@ -170,8 +151,9 @@ new_sa.sa_mask |= _S(signum); new_sa.sa_mask &= _BLOCKABLE; } - if (TASK_SIZE <= (unsigned long) new_sa.sa_handler) - return -EFAULT; + err = verify_area(VERIFY_READ, new_sa.sa_handler, 1); + if (err) + return err; } if (oldaction) { int err = verify_area(VERIFY_WRITE, oldaction, sizeof(*oldaction)); @@ -184,224 +166,4 @@ check_pending(signum); } return 0; -} - -asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); - -/* - * 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. - */ -static 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/kernel/softirq.c linux/kernel/softirq.c --- v1.1.76/linux/kernel/softirq.c Thu Jan 1 02:00:00 1970 +++ linux/kernel/softirq.c Wed Jan 4 21:16:06 1995 @@ -0,0 +1,54 @@ +/* + * linux/kernel/softirq.c + * + * Copyright (C) 1992 Linus Torvalds + * + * do_bottom_half() runs at normal kernel priority: all interrupts + * enabled. do_bottom_half() is atomic with respect to itself: a + * bottom_half handler need not be re-entrant. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define INCLUDE_INLINE_FUNCS +#include + +unsigned long intr_count = 0; + +unsigned long bh_active = 0; +unsigned long bh_mask = ~0UL; +struct bh_struct bh_base[32]; + + +asmlinkage void do_bottom_half(void) +{ + unsigned long active; + unsigned long mask, left; + struct bh_struct *bh; + + bh = bh_base; + active = bh_active & bh_mask; + for (mask = 1, left = ~0 ; left & active ; bh++,mask += mask,left += left) { + if (mask & active) { + void (*fn)(void *); + bh_active &= ~mask; + fn = bh->routine; + if (!fn) + goto bad_bh; + fn(bh->data); + } + } + return; +bad_bh: + printk ("irq.c:bad bottom half entry %08lx\n", mask); +} diff -u --recursive --new-file v1.1.76/linux/kernel/splx.c linux/kernel/splx.c --- v1.1.76/linux/kernel/splx.c Tue Aug 9 09:34:45 1994 +++ linux/kernel/splx.c Thu Jan 1 02:00:00 1970 @@ -1,27 +0,0 @@ -/* - * splx.c - SYSV DDI/DKI ipl manipulation functions - * - * Internally, many unices use a range of different interrupt - * privilege levels, ie from "allow all interrupts" (7) to - * "allow no interrupts." (0) under SYSV. - * - * This a simple splx() function behaves as the SYSV DDI/DKI function does, - * although since Linux only implements the equivalent of level 0 (cli) and - * level 7 (sti), this implementation only implements those levels. - * - * Also, unlike the current Linux routines, splx() also returns the - * old privilege level so that it can be restored. - */ - -#include - -int splx (int new_level) { - register int old_level, tmp; - save_flags(tmp); - old_level = (tmp & 0x200) ? 7 : 0; - if (new_level) - sti(); - else - cli(); - return old_level; -} diff -u --recursive --new-file v1.1.76/linux/kernel/sys.c linux/kernel/sys.c --- v1.1.76/linux/kernel/sys.c Fri Dec 30 08:20:19 1994 +++ linux/kernel/sys.c Thu Jan 5 13:55:40 1995 @@ -4,7 +4,6 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include #include #include #include @@ -33,26 +32,6 @@ asmlinkage int sys_ni_syscall(void) { return -ENOSYS; -} - -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(); - } } static int proc_sel(struct task_struct *p, int which, int who) diff -u --recursive --new-file v1.1.76/linux/kernel/time.c linux/kernel/time.c --- v1.1.76/linux/kernel/time.c Sat Dec 3 13:09:33 1994 +++ linux/kernel/time.c Thu Jan 5 13:55:40 1995 @@ -18,7 +18,6 @@ * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime */ -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/kernel/tqueue.c linux/kernel/tqueue.c --- v1.1.76/linux/kernel/tqueue.c Mon May 23 07:55:15 1994 +++ linux/kernel/tqueue.c Thu Jan 1 02:00:00 1970 @@ -1,10 +0,0 @@ -/* - * tqueue.c --- task queue handling for Linux. - * - * This routine merely draws in the static portion of the task queue - * inline functions. Look in tqueue.h for the relevant functions. - */ - -#define INCLUDE_INLINE_FUNCS - -#include diff -u --recursive --new-file v1.1.76/linux/kernel/vm86.c linux/kernel/vm86.c --- v1.1.76/linux/kernel/vm86.c Wed Dec 28 17:02:47 1994 +++ linux/kernel/vm86.c Thu Jan 1 02:00:00 1970 @@ -1,404 +0,0 @@ -/* - * 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/lib/Makefile linux/lib/Makefile --- v1.1.76/linux/lib/Makefile Thu Apr 21 10:47:54 1994 +++ linux/lib/Makefile Wed Jan 4 21:16:06 1995 @@ -20,6 +20,7 @@ $(AR) rcs lib.a $(OBJS) sync +modules: dep: $(CPP) -M *.c > .depend diff -u --recursive --new-file v1.1.76/linux/lib/_exit.c linux/lib/_exit.c --- v1.1.76/linux/lib/_exit.c Wed Dec 1 14:44:15 1993 +++ linux/lib/_exit.c Wed Jan 4 21:16:06 1995 @@ -7,12 +7,15 @@ #define __LIBRARY__ #include +#define exit __sys_exit +static inline _syscall0(int, exit) +#undef exit + volatile void _exit(int exit_code) { fake_volatile: - __asm__("movl %1,%%ebx\n\t" - "int $0x80" - : /* no outputs */ - :"a" (__NR_exit),"g" (exit_code)); + __sys_exit(); goto fake_volatile; } + + diff -u --recursive --new-file v1.1.76/linux/lib/open.c linux/lib/open.c --- v1.1.76/linux/lib/open.c Wed Dec 1 14:44:15 1993 +++ linux/lib/open.c Wed Jan 4 21:16:06 1995 @@ -8,19 +8,17 @@ #include #include +#define open __sys_open +static inline _syscall3(int, open, const char *, filename, int, flag, int, mode) +#undef open + int open(const char * filename, int flag, ...) { - register int res; + int res; va_list arg; va_start(arg,flag); - __asm__("movl %2,%%ebx\n\t" - "int $0x80" - :"=a" (res) - :"0" (__NR_open),"g" ((long)(filename)),"c" (flag), - "d" (va_arg(arg,int))); - if (res>=0) - return res; - errno = -res; - return -1; + res = __sys_open(filename, flag, va_arg(arg, int)); + va_end(arg); + return res; } diff -u --recursive --new-file v1.1.76/linux/lib/string.c linux/lib/string.c --- v1.1.76/linux/lib/string.c Wed Dec 28 18:34:49 1994 +++ linux/lib/string.c Wed Jan 4 21:16:06 1995 @@ -197,3 +197,19 @@ return((*su1 < *su2) ? -1 : +1); return(0); } + +/* + * find the first occurrence of byte 'c', or 1 past the area if none + */ +extern inline void * memscan(void * addr, unsigned char c, size_t size) +{ + unsigned char * p = (unsigned char *) addr; + + while (size) { + if (*p == c) + return (void *) p; + p++; + size--; + } + return (void *) p; +} diff -u --recursive --new-file v1.1.76/linux/mm/Makefile linux/mm/Makefile --- v1.1.76/linux/mm/Makefile Thu Dec 1 12:00:50 1994 +++ linux/mm/Makefile Wed Jan 4 21:16:06 1995 @@ -19,6 +19,8 @@ mm.o: $(OBJS) $(LD) -r -o mm.o $(OBJS) +modules: + dep: $(CPP) -M *.c > .depend diff -u --recursive --new-file v1.1.76/linux/mm/memory.c linux/mm/memory.c --- v1.1.76/linux/mm/memory.c Wed Dec 28 17:05:30 1994 +++ linux/mm/memory.c Wed Jan 4 21:16:06 1995 @@ -72,8 +72,7 @@ struct mem_list free_area_list[NR_MEM_LISTS]; unsigned char * free_area_map[NR_MEM_LISTS]; -#define copy_page(from,to) \ -__asm__("cld ; rep ; movsl": :"S" (from),"D" (to),"c" (1024):"cx","di","si") +#define copy_page(from,to) memcpy((void *) to, (void *) from, PAGE_SIZE) unsigned short * mem_map = NULL; diff -u --recursive --new-file v1.1.76/linux/mm/swap.c linux/mm/swap.c --- v1.1.76/linux/mm/swap.c Wed Dec 14 08:36:40 1994 +++ linux/mm/swap.c Tue Jan 3 16:15:52 1995 @@ -526,8 +526,7 @@ static inline void add_mem_queue(struct mem_list * head, struct mem_list * entry) { entry->prev = head; - entry->next = head->next; - entry->next->prev = entry; + (entry->next = head->next)->prev = entry; head->next = entry; } @@ -1043,9 +1042,10 @@ while (p > mem_map) *--p = MAP_PAGE_RESERVED; - for (i = 0 ; i < NR_MEM_LISTS ; i++, mask <<= 1) { + for (i = 0 ; i < NR_MEM_LISTS ; i++) { unsigned long bitmap_size; free_area_list[i].prev = free_area_list[i].next = &free_area_list[i]; + mask += mask; end_mem = (end_mem + ~mask) & mask; bitmap_size = end_mem >> (PAGE_SHIFT + i); bitmap_size = (bitmap_size + 7) >> 3; diff -u --recursive --new-file v1.1.76/linux/mm/vmalloc.c linux/mm/vmalloc.c --- v1.1.76/linux/mm/vmalloc.c Mon Jan 24 09:45:03 1994 +++ linux/mm/vmalloc.c Thu Jan 5 13:55:40 1995 @@ -5,7 +5,6 @@ */ #include -#include #include #include diff -u --recursive --new-file v1.1.76/linux/modules/NET_MODULES linux/modules/NET_MODULES --- v1.1.76/linux/modules/NET_MODULES Mon Jan 2 15:17:03 1995 +++ linux/modules/NET_MODULES Sat Jan 7 12:57:54 1995 @@ -1 +1 @@ -3c509.o de600.o de620.o 3c501.o apricot.o eexpress.o plip.o 8390.o slip.o slhc.o +3c509.o de600.o de620.o 3c501.o apricot.o eexpress.o plip.o 8390.o slip.o slhc.o dummy.o ewrk3.o depca.o diff -u --recursive --new-file v1.1.76/linux/net/Makefile linux/net/Makefile --- v1.1.76/linux/net/Makefile Thu Jul 14 20:27:23 1994 +++ linux/net/Makefile Wed Jan 4 21:16:06 1995 @@ -40,6 +40,8 @@ $(CPP) -M *.c > .depend set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done +modules: + dummy: # diff -u --recursive --new-file v1.1.76/linux/net/inet/Makefile linux/net/inet/Makefile --- v1.1.76/linux/net/inet/Makefile Wed Nov 30 21:53:47 1994 +++ linux/net/inet/Makefile Sat Jan 7 12:57:54 1995 @@ -42,6 +42,12 @@ endif +ifdef CONFIG_APPLETALK + +OBJS := $(OBJS) atalk_ddp.o atalk_arp.o + +endif + ifdef CONFIG_NET inet.o: $(OBJS) diff -u --recursive --new-file v1.1.76/linux/net/inet/af_inet.c linux/net/inet/af_inet.c --- v1.1.76/linux/net/inet/af_inet.c Wed Dec 14 09:02:26 1994 +++ linux/net/inet/af_inet.c Sat Jan 7 13:51:26 1995 @@ -25,6 +25,7 @@ * to close when you look carefully. With * this fixed and the accept bug fixed * some RPC stuff seems happier. + * Niibe Yutaka : 4.4BSD style write async I/O * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -259,7 +260,9 @@ /* Now we can no longer get new packets. */ delete_timer(sk); - + /* Nor send them */ + del_timer(&sk->retransmit_timer); + while ((skb = tcp_dequeue_partial(sk)) != NULL) { IS_SKB(skb); kfree_skb(skb, FREE_WRITE); @@ -480,10 +483,18 @@ if(!sk->dead) { wake_up_interruptible(sk->sleep); - sock_wake_async(sk->socket); + sock_wake_async(sk->socket, 1); } } +static void def_callback3(struct sock *sk) +{ + if(!sk->dead) + { + wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket, 2); + } +} /* * Create an inet socket. @@ -645,6 +656,7 @@ sk->broadcast = 0; sk->localroute = 0; init_timer(&sk->timer); + init_timer(&sk->retransmit_timer); sk->timer.data = (unsigned long)sk; sk->timer.function = &net_timer; skb_queue_head_init(&sk->back_log); @@ -672,7 +684,7 @@ sk->state_change = def_callback1; sk->data_ready = def_callback2; - sk->write_space = def_callback1; + sk->write_space = def_callback3; sk->error_report = def_callback1; if (sk->num) @@ -865,7 +877,7 @@ cli(); err=sk->err; sk->err=0; - sti(); + restore_flags(flags); return -err; } @@ -1466,7 +1478,7 @@ int i; - printk("Swansea University Computer Society TCP/IP for NET3.018\n"); + printk("Swansea University Computer Society TCP/IP for NET3.019\n"); /* * Tell SOCKET that we are alive... diff -u --recursive --new-file v1.1.76/linux/net/inet/arp.c linux/net/inet/arp.c --- v1.1.76/linux/net/inet/arp.c Wed Nov 30 21:53:47 1994 +++ linux/net/inet/arp.c Sat Jan 7 12:57:54 1995 @@ -31,10 +31,12 @@ * Alan Cox : Use init_timer(). * Alan Cox : Double lock fixes. * Martin Seine : Move the arphdr structure - * to if_arp.h for compatibility + * to if_arp.h for compatibility. * with BSD based programs. * Andrew Tridgell : Added ARP netmask code and - * re-arranged proxy handling + * re-arranged proxy handling. + * Alan Cox : Changed to use notifiers. + * Niibe Yutaka : Reply for this device or proxies only. */ #include @@ -246,15 +248,19 @@ /* * Purge a device from the ARP queue */ - -void arp_device_down(struct device *dev) + +int arp_device_event(unsigned long event, void *ptr) { + struct device *dev=ptr; int i; unsigned long flags; + if(event!=NETDEV_DOWN) + return NOTIFY_DONE; /* * This is a bit OTT - maybe we need some arp semaphores instead. */ + save_flags(flags); cli(); for (i = 0; i < FULL_ARP_TABLE_SIZE; i++) @@ -275,6 +281,7 @@ } } restore_flags(flags); + return NOTIFY_DONE; } @@ -694,7 +701,8 @@ /* * To get here, it must be an arp request for us. We need to reply. */ - arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr); + if(tip==dev->pa_addr) /* Only reply for the real device address */ + arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr); } } @@ -1235,12 +1243,18 @@ static struct packet_type arp_packet_type = { 0, /* Should be: __constant_htons(ETH_P_ARP) - but this _doesn't_ come out constant! */ - 0, /* copy */ + NULL, /* All devices */ arp_rcv, NULL, NULL }; +static struct notifier_block arp_dev_notifier={ + arp_device_event, + NULL, + 0 +}; + void arp_init (void) { /* Register the packet type */ @@ -1248,5 +1262,7 @@ dev_add_pack(&arp_packet_type); /* Start with the regular checks for expired arp entries. */ add_timer(&arp_timer); + /* Register for device down reports */ + register_netdevice_notifier(&arp_dev_notifier); } diff -u --recursive --new-file v1.1.76/linux/net/inet/datagram.c linux/net/inet/datagram.c --- v1.1.76/linux/net/inet/datagram.c Mon Aug 22 21:14:36 1994 +++ linux/net/inet/datagram.c Sat Jan 7 12:57:54 1995 @@ -21,7 +21,6 @@ * occurs. Using this will make things reasonably clean. */ -#include #include #include #include @@ -53,8 +52,10 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err) { struct sk_buff *skb; + unsigned long intflags; /* Socket is inuse - so the timer doesn't attack it */ + save_flags(intflags); restart: sk->inuse = 1; while(skb_peek(&sk->receive_queue) == NULL) /* No data */ @@ -101,7 +102,7 @@ /* Signals may need a restart of the syscall */ if (current->signal & ~current->blocked) { - sti(); + restore_flags(intflags);; *err=-ERESTARTSYS; return(NULL); } @@ -110,13 +111,13 @@ peer has finally turned up now */ { *err = -sk->err; - sti(); sk->err=0; + restore_flags(intflags); return NULL; } } sk->inuse = 1; - sti(); + restore_flags(intflags); } /* Again only user level code calls this function, so nothing interrupt level will suddenly eat the receive_queue */ @@ -134,7 +135,7 @@ skb=skb_peek(&sk->receive_queue); if(skb!=NULL) skb->users++; - sti(); + restore_flags(intflags); if(skb==NULL) /* shouldn't happen but .. */ *err=-EAGAIN; } diff -u --recursive --new-file v1.1.76/linux/net/inet/dev.c linux/net/inet/dev.c --- v1.1.76/linux/net/inet/dev.c Wed Dec 14 09:02:26 1994 +++ linux/net/inet/dev.c Sat Jan 7 12:57:54 1995 @@ -27,6 +27,7 @@ * Tim Kordas : SIOCADDMULTI/SIOCDELMULTI * Alan Cox : 100 backlog just doesn't cut it when * you start doing multicast video 8) + * Alan Cox : Rewrote net_bh and list manager. * * Cleaned up and recommented by Alan Cox 2nd April 1994. I hope to have * the rest as well commented in the end. @@ -54,6 +55,7 @@ #include #include #include +#include #include "ip.h" #include "route.h" #include @@ -69,6 +71,12 @@ struct packet_type *ptype_base = NULL; /* + * Our notifier list + */ + +struct notifier_block *netdev_chain=NULL; + +/* * Device drivers call our routines to queue packets here. We empty the * queue in the bottom half handler. */ @@ -88,13 +96,6 @@ static int backlog_size = 0; /* - * The number of sockets open for 'all' protocol use. We have to - * know this to copy a buffer the correct number of times. - */ - -static int dev_nit=0; - -/* * Return the lesser of the two values. */ @@ -110,70 +111,24 @@ *******************************************************************************************/ +/* + * For efficiency + */ + +static int dev_nit=0; /* - * Add a protocol ID to the list. + * Add a protocol ID to the list. Now that the input handler is + * smarter we can dispense with all the messy stuff that used to be + * here. */ void dev_add_pack(struct packet_type *pt) { - struct packet_type *p1; + if(pt->type==htons(ETH_P_ALL)) + dev_nit++; pt->next = ptype_base; - - /* - * Don't use copy counts on ETH_P_ALL. Instead keep a global - * count of number of these and use it and pt->copy to decide - * copies - */ - - pt->copy=0; /* Assume we will not be copying the buffer before - * this routine gets it - */ - - if(pt->type == htons(ETH_P_ALL)) - dev_nit++; /* I'd like a /dev/nit too one day 8) */ - else - { - /* - * See if we need to copy it - that is another process also - * wishes to receive this type of packet. - */ - for (p1 = ptype_base; p1 != NULL; p1 = p1->next) - { - if (p1->type == pt->type) - { - pt->copy = 1; /* We will need to copy */ - break; - } - } - } - - /* - * NIT taps must go at the end or net_bh will leak! - */ - - if (pt->type == htons(ETH_P_ALL)) - { - pt->next=NULL; - if(ptype_base==NULL) - ptype_base=pt; - else - { - /* - * Move to the end of the list - */ - for(p1=ptype_base;p1->next!=NULL;p1=p1->next); - /* - * Hook on the end - */ - p1->next=pt; - } - } - else -/* - * It goes on the start - */ - ptype_base = pt; + ptype_base = pt; } @@ -183,47 +138,16 @@ void dev_remove_pack(struct packet_type *pt) { - struct packet_type *lpt, *pt1; - - /* - * Keep the count of nit (Network Interface Tap) sockets correct. - */ - - if (pt->type == htons(ETH_P_ALL)) - dev_nit--; - - /* - * If we are first, just unhook us. - */ - - if (pt == ptype_base) - { - ptype_base = pt->next; - return; - } - - lpt = NULL; - - /* - * This is harder. What we do is to walk the list of sockets - * for this type. We unhook the entry, and if there is a previous - * entry that is copying _and_ we are not copying, (ie we are the - * last entry for this type) then the previous one is set to - * non-copying as it is now the last. - */ - for (pt1 = ptype_base; pt1->next != NULL; pt1 = pt1->next) + struct packet_type **pt1; + if(pt->type==htons(ETH_P_ALL)) + dev_nit--; + for(pt1=&ptype_base; (*pt1)!=NULL; pt1=&((*pt1)->next)) { - if (pt1->next == pt ) + if(pt==(*pt1)) { - cli(); - if (!pt->copy && lpt) - lpt->copy = 0; - pt1->next = pt->next; - sti(); + *pt1=pt->next; return; } - if (pt1->next->type == pt->type && pt->type != htons(ETH_P_ALL)) - lpt = pt1->next; } } @@ -281,6 +205,7 @@ ip_mc_allhost(dev); #endif dev_mc_upload(dev); + notifier_call_chain(&netdev_chain, NETDEV_UP, dev); } return(ret); } @@ -288,11 +213,6 @@ /* * Completely shutdown an interface. - * - * WARNING: Both because of the way the upper layers work (that can be fixed) - * and because of races during a close (that can't be fixed any other way) - * a device may be given things to transmit EVEN WHEN IT IS DOWN. The driver - * MUST cope with this (eg by freeing and dumping the frame). */ int dev_close(struct device *dev) @@ -310,6 +230,9 @@ */ if (dev->stop) dev->stop(dev); + + notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); +#if 0 /* * Delete the route to the device. */ @@ -320,6 +243,7 @@ #ifdef CONFIG_IPX ipxrtr_device_down(dev); #endif +#endif /* * Flush the multicast chain */ @@ -348,6 +272,23 @@ /* + * Device change register/unregister. These are not inline or static + * as we export them to the world. + */ + +int register_netdevice_notifier(struct notifier_block *nb) +{ + return notifier_chain_register(&netdev_chain, nb); +} + +int unregister_netdevice_notifer(struct notifier_block *nb) +{ + return notifier_chain_unregister(&netdev_chain,nb); +} + + + +/* * Send (or queue for sending) a packet. * * IMPORTANT: When this is called to resend frames. The caller MUST @@ -445,9 +386,10 @@ /* copy outgoing packets to any sniffer packet handlers */ if(!where) { - for (nitcount = dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next) + for (nitcount= dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next) { - if (ptype->type == htons(ETH_P_ALL)) { + if (ptype->type == htons(ETH_P_ALL) && (ptype->dev==dev || !ptype->dev)) + { struct sk_buff *skb2; if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) break; @@ -669,9 +611,8 @@ { struct sk_buff *skb; struct packet_type *ptype; + struct packet_type *pt_prev=NULL; unsigned short type; - unsigned char flag = 0; - int nitcount; /* * Atomically check and mark our BUSY state. @@ -707,8 +648,6 @@ */ backlog_size--; - nitcount=dev_nit; - flag=0; sti(); /* @@ -748,66 +687,48 @@ for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) { - if (ptype->type == type || ptype->type == htons(ETH_P_ALL)) + if ((ptype->type == type || ptype->type == htons(ETH_P_ALL)) && (!ptype->dev || ptype->dev==skb->dev)) { - struct sk_buff *skb2; - - if (ptype->type == htons(ETH_P_ALL)) - nitcount--; - if (ptype->copy || nitcount) - { - /* - * copy if we need to - */ -#ifdef OLD - skb2 = alloc_skb(skb->len, GFP_ATOMIC); - if (skb2 == NULL) - continue; - memcpy(skb2, skb, skb2->mem_len); - skb2->mem_addr = skb2; - skb2->h.raw = (unsigned char *)( - (unsigned long) skb2 + - (unsigned long) skb->h.raw - - (unsigned long) skb - ); - skb2->free = 1; -#else - skb2=skb_clone(skb, GFP_ATOMIC); - if(skb2==NULL) - continue; -#endif - } - else - { - skb2 = skb; - } - /* - * Protocol located. + * We already have a match queued. Deliver + * to it and then remember the new match */ - - flag = 1; + if(pt_prev) + { + struct sk_buff *skb2; - /* - * Kick the protocol handler. This should be fast - * and efficient code. - */ + skb2=skb_clone(skb, GFP_ATOMIC); - ptype->func(skb2, skb->dev, ptype); + /* + * Kick the protocol handler. This should be fast + * and efficient code. + */ + + if(skb2) + pt_prev->func(skb2, skb->dev, pt_prev); + } + /* Remember the current last to do */ + pt_prev=ptype; } } /* End of protocol list loop */ + + /* + * Is there a last item to send to ? + */ + if(pt_prev) + pt_prev->func(skb, skb->dev, pt_prev); /* * Has an unknown packet has been received ? */ - if (!flag) - { + else kfree_skb(skb, FREE_WRITE); - } /* * Again, see if we can transmit anything now. + * [Ought to take this out judging by tests it slows + * us down not speeds us up] */ dev_transmit(); @@ -1266,12 +1187,12 @@ case SIOCGIFMEM: /* Get the per device memory space. We can add this but currently do not support it */ - printk("NET: ioctl(SIOCGIFMEM, 0x%08X)\n", (int)arg); + printk("NET: ioctl(SIOCGIFMEM, %p)\n", arg); ret = -EINVAL; break; case SIOCSIFMEM: /* Set the per device memory buffer space. Not applicable in our case */ - printk("NET: ioctl(SIOCSIFMEM, 0x%08X)\n", (int)arg); + printk("NET: ioctl(SIOCSIFMEM, %p)\n", arg); ret = -EINVAL; break; diff -u --recursive --new-file v1.1.76/linux/net/inet/dev_mcast.c linux/net/inet/dev_mcast.c --- v1.1.76/linux/net/inet/dev_mcast.c Wed Dec 14 09:02:26 1994 +++ linux/net/inet/dev_mcast.c Thu Jan 5 13:55:40 1995 @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/net/inet/devinet.c linux/net/inet/devinet.c --- v1.1.76/linux/net/inet/devinet.c Thu Nov 24 11:08:11 1994 +++ linux/net/inet/devinet.c Thu Jan 5 13:55:40 1995 @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/net/inet/eth.c linux/net/inet/eth.c --- v1.1.76/linux/net/inet/eth.c Thu Aug 11 20:38:53 1994 +++ linux/net/inet/eth.c Thu Jan 5 13:47:08 1995 @@ -45,6 +45,8 @@ #include #include #include +#include + #include "arp.h" void eth_setup(char *str, int *ints) diff -u --recursive --new-file v1.1.76/linux/net/inet/igmp.c linux/net/inet/igmp.c --- v1.1.76/linux/net/inet/igmp.c Thu Dec 15 08:28:07 1994 +++ linux/net/inet/igmp.c Sat Jan 7 12:57:54 1995 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -177,6 +178,7 @@ del_timer(&im->timer); igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE); ip_mc_filter_del(im->interface, im->multiaddr); + printk("Left group %lX\n",im->multiaddr); } static void igmp_group_added(struct ip_mc_list *im) @@ -184,6 +186,7 @@ igmp_init_timer(im); igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT); ip_mc_filter_add(im->interface, im->multiaddr); + printk("Joined group %lX\n",im->multiaddr); } int igmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, diff -u --recursive --new-file v1.1.76/linux/net/inet/ip.c linux/net/inet/ip.c --- v1.1.76/linux/net/inet/ip.c Thu Dec 29 10:31:50 1994 +++ linux/net/inet/ip.c Sat Jan 7 12:57:54 1995 @@ -60,6 +60,9 @@ * Alan Cox : RAW sockets demultiplex in the BSD style. * Gunther Mayer : Fix the SNMP reporting typo * Alan Cox : Always in group 224.0.0.1 + * Alan Cox : Multicast loopback error for 224.0.0.1 + * Alan Cox : IP_MULTICAST_LOOP option. + * Alan Cox : Use notifiers. * * To Fix: * IP option processing is mostly not needed. ip_forward needs to know about routing rules @@ -85,12 +88,15 @@ #include #include #include +#include + #include #include #include #include #include #include + #include "snmp.h" #include "ip.h" #include "protocol.h" @@ -1923,9 +1929,6 @@ /* Interrupt restore */ restore_flags(flags); - /* Set the IP write timeout to the round trip time for the packet. - If an acknowledge has not arrived by then we may wish to act */ - reset_timer(sk, TIME_WRITE, sk->rto); } else /* Remember who owns the buffer */ @@ -1950,7 +1953,7 @@ { if(sk==NULL || sk->ip_mc_loop) { - if(skb->daddr==IGMP_ALL_HOSTS) + if(iph->daddr==IGMP_ALL_HOSTS) ip_loopback(dev,skb); else { @@ -2405,12 +2408,28 @@ static struct packet_type ip_packet_type = { 0, /* MUTTER ntohs(ETH_P_IP),*/ - 0, /* copy */ + NULL, /* All devices */ ip_rcv, NULL, NULL, }; +/* + * Device notifier + */ + +static int ip_rt_event(unsigned long event, void *ptr) +{ + if(event==NETDEV_DOWN) + ip_rt_flush(ptr); + return NOTIFY_DONE; +} + +struct notifier_block ip_rt_notifier={ + ip_rt_event, + NULL, + 0 +}; /* * IP registers the packet type and then calls the subprotocol initialisers @@ -2420,6 +2439,9 @@ { ip_packet_type.type=htons(ETH_P_IP); dev_add_pack(&ip_packet_type); + + /* So we flush routes when a device is downed */ + register_netdevice_notifier(&ip_rt_notifier); /* ip_raw_init(); ip_packet_init(); ip_tcp_init(); diff -u --recursive --new-file v1.1.76/linux/net/inet/ip.h linux/net/inet/ip.h --- v1.1.76/linux/net/inet/ip.h Mon Dec 12 21:01:10 1994 +++ linux/net/inet/ip.h Thu Jan 5 13:47:08 1995 @@ -21,6 +21,7 @@ #include +#include #ifndef _SNMP_H #include "snmp.h" diff -u --recursive --new-file v1.1.76/linux/net/inet/ip_fw.c linux/net/inet/ip_fw.c --- v1.1.76/linux/net/inet/ip_fw.c Mon Dec 12 21:54:05 1994 +++ linux/net/inet/ip_fw.c Thu Jan 5 13:47:08 1995 @@ -32,6 +32,8 @@ #include #include #include +#include + #include #include #include diff -u --recursive --new-file v1.1.76/linux/net/inet/ipx.c linux/net/inet/ipx.c --- v1.1.76/linux/net/inet/ipx.c Fri Nov 18 15:17:48 1994 +++ linux/net/inet/ipx.c Sat Jan 7 12:57:55 1995 @@ -31,11 +31,9 @@ * Revision 0.29: Assorted major errors removed * Small correction to promisc mode error fix * Asynchronous I/O support. - * - * - * + * Changed to use notifiers and the newer packet_type stuff. */ - + #include #include #include @@ -390,11 +388,14 @@ return -ENOENT; } -void ipxrtr_device_down(struct device *dev) +int ipxrtr_device_event(unsigned long event, void *ptr) { + struct device *dev=ptr; ipx_route **r = &ipx_router_list; ipx_route *tmp; + if(event!=NETDEV_DOWN) + return NOTIFY_DONE; while ((tmp = *r) != NULL) { if (tmp->dev == dev) { *r = tmp->next; @@ -404,6 +405,7 @@ } r = &tmp->next; } + return NOTIFY_DONE; } static int ipxrtr_ioctl(unsigned int cmd, void *arg) @@ -568,7 +570,7 @@ if(!sk->dead) { wake_up_interruptible(sk->sleep); - sock_wake_async(sk->socket); + sock_wake_async(sk->socket, 1); } } @@ -1328,12 +1330,18 @@ static struct packet_type ipx_dix_packet_type = { 0, /* MUTTER ntohs(ETH_P_IPX),*/ - 0, /* copy */ + NULL, /* Al devices */ ipx_rcv, NULL, NULL, }; +static struct notifier_block ipx_dev_notifier={ + ipxrtr_device_event, + NULL, + 0 +}; + extern struct datalink_proto *make_EII_client(void); extern struct datalink_proto *make_8023_client(void); @@ -1353,8 +1361,10 @@ if ((p8022_datalink = register_8022_client(val, ipx_rcv)) == NULL) printk("IPX: Unable to register with 802.2\n"); - - printk("Swansea University Computer Society IPX 0.29 BETA for NET3.017\n"); + + register_netdevice_notifier(&ipx_dev_notifier); + + printk("Swansea University Computer Society IPX 0.29 BETA for NET3.019\n"); } #endif diff -u --recursive --new-file v1.1.76/linux/net/inet/p8022.c linux/net/inet/p8022.c --- v1.1.76/linux/net/inet/p8022.c Wed Aug 31 10:13:21 1994 +++ linux/net/inet/p8022.c Sat Jan 7 12:57:55 1995 @@ -59,7 +59,7 @@ static struct packet_type p8022_packet_type = { 0, /* MUTTER ntohs(ETH_P_IPX),*/ - 0, /* copy */ + NULL, /* All devices */ p8022_rcv, NULL, NULL, diff -u --recursive --new-file v1.1.76/linux/net/inet/packet.c linux/net/inet/packet.c --- v1.1.76/linux/net/inet/packet.c Mon Dec 12 21:01:10 1994 +++ linux/net/inet/packet.c Sat Jan 7 12:57:55 1995 @@ -23,7 +23,10 @@ * Alan Cox : Re-commented the code. * Alan Cox : Use new kernel side addressing * Rob Janssen : Correct MTU usage. - * + * Dave Platt : Counter leaks caused by incorrect + * interrupt locking and some slightly + * dubious gcc output. Can you read + * compiler: it said _VOLATILE_ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -67,6 +70,7 @@ int packet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { struct sock *sk; + unsigned long flags; /* * When we registered the protocol we saved the socket in the data @@ -84,19 +88,28 @@ skb->dev = dev; skb->len += dev->hard_header_len; - skb->sk = sk; - /* * Charge the memory to the socket. This is done specifically * to prevent sockets using all the memory up. */ + if (sk->rmem_alloc & 0xFF000000) { + printk("packet_rcv: sk->rmem_alloc = %ld\n", sk->rmem_alloc); + sk->rmem_alloc = 0; + } + if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) { +/* printk("packet_rcv: drop, %d+%d>%d\n", sk->rmem_alloc, skb->mem_len, sk->rcvbuf); */ skb->sk = NULL; kfree_skb(skb, FREE_READ); return(0); } + + save_flags(flags); + cli(); + + skb->sk = sk; sk->rmem_alloc += skb->mem_len; /* @@ -104,7 +117,10 @@ */ skb_queue_tail(&sk->receive_queue,skb); - wake_up_interruptible(sk->sleep); + if(!sk->dead) + sk->data_ready(sk,skb->len); + + restore_flags(flags); /* * Processing complete. @@ -246,6 +262,7 @@ p->func = packet_rcv; p->type = sk->num; p->data = (void *)sk; + p->dev = NULL; dev_add_pack(p); /* diff -u --recursive --new-file v1.1.76/linux/net/inet/protocol.c linux/net/inet/protocol.c --- v1.1.76/linux/net/inet/protocol.c Wed Nov 30 21:53:47 1994 +++ linux/net/inet/protocol.c Thu Jan 5 13:47:08 1995 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include diff -u --recursive --new-file v1.1.76/linux/net/inet/skbuff.c linux/net/inet/skbuff.c --- v1.1.76/linux/net/inet/skbuff.c Sat Dec 3 12:02:03 1994 +++ linux/net/inet/skbuff.c Sat Jan 7 12:57:55 1995 @@ -6,6 +6,7 @@ * * Fixes: * Alan Cox : Fixed the worst of the load balancer bugs. + * Dave Platt : Interrupt stacking fix * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -364,11 +365,15 @@ } else { + unsigned long flags; /* Non INET - default wmalloc/rmalloc handler */ + save_flags(flags); + cli(); if (rw) skb->sk->rmem_alloc-=skb->mem_len; else skb->sk->wmem_alloc-=skb->mem_len; + restore_flags(flags); if(!skb->sk->dead) skb->sk->write_space(skb->sk); kfree_skbmem(skb,skb->mem_len); diff -u --recursive --new-file v1.1.76/linux/net/inet/sock.c linux/net/inet/sock.c --- v1.1.76/linux/net/inet/sock.c Sat Dec 3 12:02:09 1994 +++ linux/net/inet/sock.c Sat Jan 7 12:57:55 1995 @@ -320,9 +320,11 @@ struct sk_buff * c = alloc_skb(size, priority); if (c) { + unsigned long flags; + save_flags(flags); cli(); sk->wmem_alloc+= c->mem_len; - sti(); + restore_flags(flags); /* was sti(); */ } return c; } @@ -341,9 +343,11 @@ struct sk_buff *c = alloc_skb(size, priority); if (c) { + unsigned long flags; + save_flags(flags); cli(); sk->rmem_alloc += c->mem_len; - sti(); + restore_flags(flags); /* was sti(); */ } return(c); } @@ -392,7 +396,11 @@ kfree_skbmem(skb, size); if (sk) { + unsigned long flags; + save_flags(flags); + cli(); sk->wmem_alloc -= size; + restore_flags(flags); /* In case it might be waiting for more memory. */ if (!sk->dead) sk->write_space(sk); @@ -409,7 +417,11 @@ kfree_skbmem(skb, size); if (sk) { + unsigned long flags; + save_flags(flags); + cli(); sk->rmem_alloc -= size; + restore_flags(flags); } } @@ -447,6 +459,8 @@ if(skb==NULL) { unsigned long tmp; + + sk->socket->flags |= SO_NOSPACE; if(noblock) { *errcode=-EAGAIN; @@ -468,6 +482,7 @@ if( tmp <= sk->wmem_alloc) { + sk->socket->flags &= ~SO_NOSPACE; interruptible_sleep_on(sk->sleep); if (current->signal & ~current->blocked) { @@ -491,10 +506,14 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) { + unsigned long flags; if(sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) return -ENOMEM; + save_flags(flags); + cli(); sk->rmem_alloc+=skb->mem_len; skb->sk=sk; + restore_flags(flags); skb_queue_tail(&sk->receive_queue,skb); if(!sk->dead) sk->data_ready(sk,skb->len); diff -u --recursive --new-file v1.1.76/linux/net/inet/sock.h linux/net/inet/sock.h --- v1.1.76/linux/net/inet/sock.h Wed Nov 30 21:53:47 1994 +++ linux/net/inet/sock.h Sat Jan 7 12:57:55 1995 @@ -33,6 +33,7 @@ #include #include /* struct options */ #include /* struct tcphdr */ +#include #include /* struct sk_buff */ #include "protocol.h" /* struct inet_protocol */ @@ -159,6 +160,10 @@ int ip_ttl; /* TTL setting */ int ip_tos; /* TOS */ struct tcphdr dummy_th; + struct timer_list keepalive_timer; /* TCP keepalive hack */ + struct timer_list retransmit_timer; /* TCP retransmit timer */ + struct timer_list ack_timer; /* TCP delayed ack timer */ + int ip_xmit_timeout; /* Why the timeout is running */ #ifdef CONFIG_IP_MULTICAST int ip_mc_ttl; /* Multicasting TTL */ int ip_mc_loop; /* Loopback (not implemented yet) */ @@ -168,7 +173,7 @@ /* This part is used for the timeout functions (timer.c). */ int timeout; /* What are we waiting for? */ - struct timer_list timer; + struct timer_list timer; /* This is the TIME_WAIT/receive timer when we are doing IP */ struct timeval stamp; /* identd */ diff -u --recursive --new-file v1.1.76/linux/net/inet/tcp.c linux/net/inet/tcp.c --- v1.1.76/linux/net/inet/tcp.c Mon Jan 2 13:03:00 1995 +++ linux/net/inet/tcp.c Sat Jan 7 12:57:55 1995 @@ -117,6 +117,10 @@ * works see the 4.4lite source. * A.N.Kuznetsov : Don't time wait on completion of tidy * close. + * Linus Torvalds : Fin/Shutdown & copied_seq changes. + * Linus Torvalds : Fixed BSD port reuse to work first syn + * Alan Cox : Reimplemented timers as per the RFC and using multiple + * timers for sanity. * * * To Fix: @@ -179,6 +183,7 @@ #include #include #include +#include #include #include #include @@ -191,6 +196,7 @@ #include "protocol.h" #include "icmp.h" #include "tcp.h" +#include "arp.h" #include #include "sock.h" #include "route.h" @@ -202,6 +208,8 @@ #undef TCP_FASTPATH +#define reset_msl_timer(x,y,z) reset_timer(x,y,z) + #define SEQ_TICK 3 unsigned long seq_offset; struct tcp_mib tcp_statistics; @@ -350,7 +358,7 @@ sk->shutdown = SHUTDOWN_MASK; if (!sk->dead) sk->state_change(sk); - reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); } /* @@ -455,12 +463,30 @@ } /* + * Reset the retransmission timer + */ + +static void reset_xmit_timer(struct sock *sk, int why, unsigned long when) +{ + del_timer(&sk->retransmit_timer); + sk->ip_xmit_timeout = why; + if((int)when < 0) + { + when=3; + printk("Error: Negative timer in xmit_timer\n"); + } + sk->retransmit_timer.expires=when; + add_timer(&sk->retransmit_timer); +} + +/* * This is the normal code called for timeouts. It does the retransmission * and then does backoff. tcp_do_retransmit is separated out because * tcp_ack needs to send stuff from the retransmit queue without * initiating a backoff. */ + void tcp_retransmit_time(struct sock *sk, int all) { tcp_do_retransmit(sk, all); @@ -481,7 +507,7 @@ sk->retransmits++; sk->backoff++; sk->rto = min(sk->rto << 1, 120*HZ); - reset_timer(sk, TIME_WRITE, sk->rto); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); } @@ -510,6 +536,154 @@ tcp_retransmit_time(sk, all); } +/* + * The TCP retransmit timer. + */ + + + +static void retransmit_timer(unsigned long data) +{ + struct sock *sk = (struct sock*)data; + int why = sk->ip_xmit_timeout; + + /* + * only process if socket is not in use + */ + + cli(); + if (sk->inuse || in_bh) + { + sk->retransmit_timer.expires = 10; + add_timer(&sk->timer); + sti(); + return; + } + + sk->inuse = 1; + sti(); + + /* Always see if we need to send an ack. */ + + if (sk->ack_backlog && !sk->zapped) + { + sk->prot->read_wakeup (sk); + if (! sk->dead) + sk->data_ready(sk,0); + } + + /* Now we need to figure out why the socket was on the timer. */ + + switch (why) + { + /* Window probing */ + case TIME_PROBE0: + tcp_send_probe0(sk); + release_sock (sk); + break; + /* Retransmitting */ + case TIME_WRITE: + /* It could be we got here because we needed to send an ack. + * So we need to check for that. + */ + { + struct sk_buff *skb; + unsigned long flags; + + save_flags(flags); + cli(); + skb = sk->send_head; + if (!skb) + { + restore_flags(flags); + } + else + { + if (jiffies < skb->when + sk->rto) + { + reset_xmit_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies); + restore_flags(flags); + release_sock (sk); + break; + } + restore_flags(flags); + /* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq, + sk->retransmits, sk->packets_out, sk->cong_window); */ + sk->prot->retransmit (sk, 0); + if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) + || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) + { + arp_destroy (sk->daddr, 0); + ip_route_check (sk->daddr); + } + if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) + { + sk->err = ETIMEDOUT; + if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 || sk->state == TCP_CLOSING) + { + sk->state = TCP_TIME_WAIT; + reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + } + else + { + sk->prot->close (sk, 1); + break; + } + } + } + release_sock (sk); + break; + } + /* Sending Keepalives */ + case TIME_KEEPOPEN: + /* + * this reset_timer() call is a hack, this is not + * how KEEPOPEN is supposed to work. + */ + reset_timer (sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); + + /* Send something to keep the connection open. */ + if (sk->prot->write_wakeup) + sk->prot->write_wakeup (sk); + sk->retransmits++; + if (sk->shutdown == SHUTDOWN_MASK) + { + sk->prot->close (sk, 1); + sk->state = TCP_CLOSE; + } + if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) + || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) + { + arp_destroy (sk->daddr, 0); + ip_route_check (sk->daddr); + release_sock (sk); + break; + } + if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) + { + arp_destroy (sk->daddr, 0); + sk->err = ETIMEDOUT; + if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) + { + sk->state = TCP_TIME_WAIT; + if (!sk->dead) + sk->state_change(sk); + release_sock (sk); + } + else + { + sk->prot->close (sk, 1); + } + break; + } + release_sock (sk); + break; + default: + printk ("rexmit_timer: timer expired - reason unknown\n"); + release_sock (sk); + break; + } +} /* * This routine is called by the ICMP module when it gets some @@ -896,7 +1070,7 @@ skb->h.seq = ntohl(th->seq) + size - 4*th->doff; if (after(skb->h.seq, sk->window_seq) || - (sk->retransmits && sk->timeout == TIME_WRITE) || + (sk->retransmits && sk->ip_xmit_timeout == TIME_WRITE) || sk->packets_out >= sk->cong_window) { /* checksum will be supplied by tcp_write_xmit. So @@ -911,7 +1085,7 @@ if (before(sk->window_seq, sk->write_queue.next->h.seq) && sk->send_head == NULL && sk->ack_backlog == 0) - reset_timer(sk, TIME_PROBE0, sk->rto); + reset_xmit_timer(sk, TIME_PROBE0, sk->rto); } else { @@ -922,6 +1096,7 @@ sk->sent_seq = sk->write_seq; sk->prot->queue_xmit(sk, skb->dev, skb, 0); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); } } @@ -998,9 +1173,9 @@ { /* Force it to send an ack. */ sk->ack_backlog++; - if (sk->timeout != TIME_WRITE && tcp_connected(sk->state)) + if (sk->ip_xmit_timeout != TIME_WRITE && tcp_connected(sk->state)) { - reset_timer(sk, TIME_WRITE, 10); + reset_xmit_timer(sk, TIME_WRITE, 10); } return; } @@ -1048,10 +1223,10 @@ sk->bytes_rcv = 0; sk->ack_timed = 0; if (sk->send_head == NULL && skb_peek(&sk->write_queue) == NULL - && sk->timeout == TIME_WRITE) + && sk->ip_xmit_timeout == TIME_WRITE) { if(sk->keepopen) { - reset_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN); + reset_xmit_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN); } else { delete_timer(sk); } @@ -1303,6 +1478,7 @@ if (skb == NULL) { + sk->socket->flags |= SO_NOSPACE; if (nonblock) { release_sock(sk); @@ -1325,6 +1501,7 @@ (sk->state == TCP_ESTABLISHED||sk->state == TCP_CLOSE_WAIT) && sk->err == 0) { + sk->socket->flags &= ~SO_NOSPACE; interruptible_sleep_on(sk->sleep); if (current->signal & ~current->blocked) { @@ -1466,7 +1643,7 @@ if (buff == NULL) { /* Try again real soon. */ - reset_timer(sk, TIME_WRITE, 10); + reset_xmit_timer(sk, TIME_WRITE, 10); return; } @@ -1589,13 +1766,13 @@ else { /* Force it to send an ack soon. */ - int was_active = del_timer(&sk->timer); + int was_active = del_timer(&sk->retransmit_timer); if (!was_active || TCP_ACK_TIME < sk->timer.expires) { - reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); + reset_xmit_timer(sk, TIME_WRITE, TCP_ACK_TIME); } else - add_timer(&sk->timer); + add_timer(&sk->retransmit_timer); } } } @@ -1688,7 +1865,7 @@ unsigned long offset; /* - * are we at urgent data? Stop if we have read anything. + * Are we at urgent data? Stop if we have read anything. */ if (copied && sk->urg_data && sk->urg_seq == *seq) break; @@ -1750,7 +1927,9 @@ cleanup_rbuf(sk); release_sock(sk); + sk->socket->flags |= SO_WAITDATA; schedule(); + sk->socket->flags &= ~SO_WAITDATA; sk->inuse = 1; if (current->signal & ~current->blocked) @@ -1953,6 +2132,7 @@ { sk->sent_seq = sk->write_seq; sk->prot->queue_xmit(sk, dev, buff, 0); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); } if (sk->state == TCP_ESTABLISHED) @@ -2265,6 +2445,7 @@ newsk->fin_seq = skb->h.th->seq; newsk->state = TCP_SYN_RECV; newsk->timeout = 0; + newsk->ip_xmit_timeout = 0; newsk->write_seq = seq; newsk->window_seq = newsk->write_seq; newsk->rcv_ack_seq = newsk->write_seq; @@ -2273,8 +2454,11 @@ newsk->linger=0; newsk->destroy = 0; init_timer(&newsk->timer); + init_timer(&newsk->retransmit_timer); newsk->timer.data = (unsigned long)newsk; newsk->timer.function = &net_timer; + newsk->retransmit_timer.data = (unsigned long)newsk; + newsk->retransmit_timer.function=&retransmit_timer; newsk->dummy_th.source = skb->h.th->dest; newsk->dummy_th.dest = skb->h.th->source; @@ -2421,8 +2605,9 @@ tcp_send_check(t1, daddr, saddr, sizeof(*t1)+4, newsk); newsk->prot->queue_xmit(newsk, ndev, buff, 0); + reset_xmit_timer(newsk, TIME_WRITE, newsk->rto); - reset_timer(newsk, TIME_WRITE , TCP_TIMEOUT_INIT); + reset_xmit_timer(newsk, TIME_WRITE , TCP_TIMEOUT_INIT); skb->sk = newsk; /* @@ -2509,7 +2694,7 @@ if (timer_active) add_timer(&sk->timer); else - reset_timer(sk, TIME_CLOSE, 4 * sk->rto); + reset_msl_timer(sk, TIME_CLOSE, 4 * sk->rto); } if (timeout) tcp_time_wait(sk); @@ -2551,7 +2736,7 @@ release_sock(sk); if (sk->state != TCP_CLOSE_WAIT) tcp_set_state(sk,TCP_ESTABLISHED); - reset_timer(sk, TIME_CLOSE, 100); + reset_msl_timer(sk, TIME_CLOSE, 100); return; } buff->sk = sk; @@ -2580,7 +2765,7 @@ tcp_set_state(sk,TCP_FIN_WAIT1); else tcp_set_state(sk,TCP_FIN_WAIT2); - reset_timer(sk, TIME_CLOSE,4*sk->rto); + reset_msl_timer(sk, TIME_CLOSE,4*sk->rto); if(timeout) tcp_time_wait(sk); @@ -2615,10 +2800,11 @@ { sk->sent_seq = sk->write_seq; prot->queue_xmit(sk, dev, buff, 0); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); } else { - reset_timer(sk, TIME_WRITE, sk->rto); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); if (buff->next != NULL) { printk("tcp_close: next != NULL\n"); @@ -2665,7 +2851,7 @@ while((skb = skb_peek(&sk->write_queue)) != NULL && before(skb->h.seq, sk->window_seq + 1) && (sk->retransmits == 0 || - sk->timeout != TIME_WRITE || + sk->ip_xmit_timeout != TIME_WRITE || before(skb->h.seq, sk->rcv_ack_seq + 1)) && sk->packets_out < sk->cong_window) { @@ -2703,6 +2889,7 @@ sk->sent_seq = skb->h.seq; sk->prot->queue_xmit(sk, skb->dev, skb, skb->free); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); } } } @@ -2738,7 +2925,7 @@ #endif } - if (sk->retransmits && sk->timeout == TIME_KEEPOPEN) + if (sk->retransmits && sk->ip_xmit_timeout == TIME_KEEPOPEN) sk->retransmits = 0; if (after(ack, sk->sent_seq) || before(ack, sk->rcv_ack_seq)) @@ -2756,8 +2943,8 @@ } if (sk->keepopen) { - if(sk->timeout==TIME_KEEPOPEN) - reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); + if(sk->ip_xmit_timeout==TIME_KEEPOPEN) + reset_xmit_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); } return(1); } @@ -2846,7 +3033,7 @@ sk->window_seq = ack + ntohs(th->window); /* We don't want too many packets out there. */ - if (sk->timeout == TIME_WRITE && + if (sk->ip_xmit_timeout == TIME_WRITE && sk->cong_window < 2048 && after(ack, sk->rcv_ack_seq)) { /* @@ -2887,7 +3074,7 @@ * it needs to be for normal retransmission. */ - if (sk->timeout == TIME_PROBE0) + if (sk->ip_xmit_timeout == TIME_PROBE0) { if (skb_peek(&sk->write_queue) != NULL && /* should always be non-null */ ! before (sk->window_seq, sk->write_queue.next->h.seq)) @@ -3044,7 +3231,7 @@ { if (after (sk->window_seq+1, sk->write_queue.next->h.seq) && (sk->retransmits == 0 || - sk->timeout != TIME_WRITE || + sk->ip_xmit_timeout != TIME_WRITE || before(sk->write_queue.next->h.seq, sk->rcv_ack_seq + 1)) && sk->packets_out < sk->cong_window) { @@ -3056,7 +3243,7 @@ sk->ack_backlog == 0 && sk->state != TCP_TIME_WAIT) { - reset_timer(sk, TIME_PROBE0, sk->rto); + reset_xmit_timer(sk, TIME_PROBE0, sk->rto); } } else @@ -3080,7 +3267,7 @@ * keep us in TIME_WAIT until we stop getting packets, * reset the timeout. */ - reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); break; case TCP_CLOSE: /* @@ -3093,11 +3280,12 @@ * to determine which timeout to use. */ if (sk->send_head || skb_peek(&sk->write_queue) != NULL || sk->ack_backlog) { - reset_timer(sk, TIME_WRITE, sk->rto); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); } else if (sk->keepopen) { - reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); + reset_xmit_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); } else { - delete_timer(sk); + del_timer(&sk->retransmit_timer); + sk->ip_xmit_timeout = 0; } break; } @@ -3228,7 +3416,7 @@ else { tcp_do_retransmit(sk, 1); - reset_timer(sk, TIME_WRITE, sk->rto); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); } } @@ -3259,6 +3447,7 @@ if (!sk->dead) { sk->state_change(sk); + sock_wake_async(sk->socket, 1); } switch(sk->state) @@ -3270,7 +3459,7 @@ * move to CLOSE_WAIT, tcp_data() already handled * sending the ack. */ /* Check me --------------vvvvvvv */ - reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); tcp_set_state(sk,TCP_CLOSE_WAIT); if (th->rst) sk->shutdown = SHUTDOWN_MASK; @@ -3288,7 +3477,7 @@ * received a retransmission of the FIN, * restart the TIME_WAIT timer. */ - reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); return(0); case TCP_FIN_WAIT1: /* @@ -3303,15 +3492,15 @@ * for handling this timeout. */ - if(sk->timeout != TIME_WRITE) - reset_timer(sk, TIME_WRITE, sk->rto); + if(sk->ip_xmit_timeout != TIME_WRITE) + reset_xmit_timer(sk, TIME_WRITE, sk->rto); tcp_set_state(sk,TCP_CLOSING); break; case TCP_FIN_WAIT2: /* * received a FIN -- send ACK and enter TIME_WAIT */ - reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); sk->shutdown|=SHUTDOWN_MASK; tcp_set_state(sk,TCP_TIME_WAIT); break; @@ -3324,7 +3513,7 @@ tcp_set_state(sk,TCP_LAST_ACK); /* Start the timers. */ - reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); return(0); } @@ -3391,7 +3580,7 @@ /* Do this the way 4.4BSD treats it. Not what I'd regard as the meaning of the spec but its what BSD does and clearly they know everything 8) */ - + /* * This is valid because of two things * @@ -3594,7 +3783,7 @@ sk->ack_backlog++; if(sk->debug) printk("Ack queued.\n"); - reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); + reset_xmit_timer(sk, TIME_WRITE, TCP_ACK_TIME); } } } @@ -3637,7 +3826,7 @@ } tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr); sk->ack_backlog++; - reset_timer(sk, TIME_WRITE, TCP_ACK_TIME); + reset_xmit_timer(sk, TIME_WRITE, TCP_ACK_TIME); } else { @@ -3941,10 +4130,14 @@ tcp_set_state(sk,TCP_SYN_SENT); sk->rto = TCP_TIMEOUT_INIT; - reset_timer(sk, TIME_WRITE, sk->rto); /* Timer for repeating the SYN until an answer */ + init_timer(&sk->retransmit_timer); + sk->retransmit_timer.function=&retransmit_timer; + sk->retransmit_timer.data = (unsigned long)sk; + reset_xmit_timer(sk, TIME_WRITE, sk->rto); /* Timer for repeating the SYN until an answer */ sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES; sk->prot->queue_xmit(sk, dev, buff, 0); + reset_xmit_timer(sk, TIME_WRITE, sk->rto); tcp_statistics.TcpActiveOpens++; tcp_statistics.TcpOutSegs++; @@ -4275,7 +4468,10 @@ sk->dummy_th.dest=th->source; sk->copied_seq = sk->acked_seq; if(!sk->dead) + { sk->state_change(sk); + sock_wake_async(sk->socket, 0); + } if(sk->max_window==0) { sk->max_window = 32; @@ -4519,7 +4715,7 @@ sk->backoff++; sk->rto = min(sk->rto << 1, 120*HZ); - reset_timer (sk, TIME_PROBE0, sk->rto); + reset_xmit_timer (sk, TIME_PROBE0, sk->rto); sk->retransmits++; sk->prot->retransmits ++; } diff -u --recursive --new-file v1.1.76/linux/net/inet/timer.c linux/net/inet/timer.c --- v1.1.76/linux/net/inet/timer.c Fri Oct 21 09:39:34 1994 +++ linux/net/inet/timer.c Sat Jan 7 12:57:55 1995 @@ -154,6 +154,7 @@ reset_timer (sk, TIME_DESTROY, TCP_DONE_TIME); release_sock (sk); break; +#if 0 case TIME_PROBE0: tcp_send_probe0(sk); release_sock (sk); @@ -253,8 +254,9 @@ } release_sock (sk); break; +#endif default: - printk ("net_timer: timer expired - reason unknown\n"); + printk ("net_timer: timer expired - reason %d is unknown\n", why); release_sock (sk); break; } diff -u --recursive --new-file v1.1.76/linux/net/inet/udp.c linux/net/inet/udp.c --- v1.1.76/linux/net/inet/udp.c Mon Dec 12 22:45:44 1994 +++ linux/net/inet/udp.c Thu Jan 5 13:47:08 1995 @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include "snmp.h" diff -u --recursive --new-file v1.1.76/linux/net/socket.c linux/net/socket.c --- v1.1.76/linux/net/socket.c Mon Dec 12 21:35:37 1994 +++ linux/net/socket.c Sat Jan 7 12:57:55 1995 @@ -20,6 +20,8 @@ * Rob Janssen : Allow 0 length sends. * Alan Cox : Asynchronous I/O support (cribbed from the * tty drivers). + * Niibe Yutaka : Asynchronous I/O for writes (4.4BSD style) + * Jeff Uphoff : Made max number of sockets command-line configurable. * * * This program is free software; you can redistribute it and/or @@ -88,9 +90,9 @@ }; /* - * The list of sockets - make this atomic. + * The list of sockets -- allocated in sock_init(). */ -static struct socket sockets[NSOCKETS]; +static struct socket *sockets = NULL; /* * Used to wait for a socket. */ @@ -99,8 +101,20 @@ * The protocol list. Each protocol is registered in here. */ static struct proto_ops *pops[NPROTO]; +/* + * Maximum number of sockets -- override-able on command-line. + */ +static int nsockets = NSOCKETS; + +#define last_socket (sockets + nsockets - 1) -#define last_socket (sockets + NSOCKETS - 1) +/* + * Overrides default max number of sockets if supplied on command-line. + */ +void sock_setup(char *str, int *ints) +{ + nsockets = ints[0] ? ints[1] : NSOCKETS; +} /* @@ -302,6 +316,7 @@ { peer->state = SS_DISCONNECTING; wake_up_interruptible(peer->wait); + sock_wake_async(peer, 1); } @@ -536,11 +551,27 @@ return 0; } -int sock_wake_async(struct socket *sock) +int sock_wake_async(struct socket *sock, int how) { if (!sock || !sock->fasync_list) return -1; - kill_fasync(sock->fasync_list, SIGIO); + switch (how) + { + case 0: + kill_fasync(sock->fasync_list, SIGIO); + break; + case 1: + if (!(sock->flags & SO_WAITDATA)) + kill_fasync(sock->fasync_list, SIGIO); + break; + case 2: + if (sock->flags & SO_NOSPACE) + { + kill_fasync(sock->fasync_list, SIGIO); + sock->flags &= ~SO_NOSPACE; + } + break; + } return 0; } @@ -549,7 +580,7 @@ * Wait for a connection. */ -int sock_awaitconn(struct socket *mysock, struct socket *servsock) +int sock_awaitconn(struct socket *mysock, struct socket *servsock, int flags) { struct socket *last; @@ -584,8 +615,13 @@ * SS_CONNECTED if we're connected. */ wake_up_interruptible(servsock->wait); + sock_wake_async(servsock, 0); + if (mysock->state != SS_CONNECTED) { + if (flags & O_NONBLOCK) + return -EINPROGRESS; + interruptible_sleep_on(mysock->wait); if (mysock->state != SS_CONNECTED && mysock->state != SS_DISCONNECTING) @@ -906,8 +942,7 @@ * an async connect fork and both children connect. Clean * this up in the protocols! */ - return(sock->ops->connect(sock, uservaddr, - addrlen, file->f_flags)); + break; default: return(-EINVAL); } @@ -1374,8 +1409,13 @@ struct socket *sock; int i; - printk("Swansea University Computer Society NET3.017\n"); + printk("Swansea University Computer Society NET3.019\n"); + /* + * The list of sockets. + */ + sockets = (struct socket *)kmalloc(sizeof(struct socket) * nsockets, GFP_KERNEL); + printk("Allocated %d sockets.\n", nsockets); /* * Release all sockets. */ diff -u --recursive --new-file v1.1.76/linux/net/unix/sock.c linux/net/unix/sock.c --- v1.1.76/linux/net/unix/sock.c Fri Jun 17 07:54:05 1994 +++ linux/net/unix/sock.c Sat Jan 7 12:57:55 1995 @@ -27,7 +27,6 @@ * 2 of the License, or(at your option) any later version. */ -#include #include #include #include @@ -496,7 +495,7 @@ return(-EINVAL); } - if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) + if ((i = sock_awaitconn(sock, serv_upd->socket, flags)) < 0) { return(i); } @@ -546,13 +545,14 @@ { if (flags & O_NONBLOCK) return(-EAGAIN); + sock->flags |= SO_WAITDATA; interruptible_sleep_on(sock->wait); + sock->flags &= ~SO_WAITDATA; if (current->signal & ~current->blocked) { return(-ERESTARTSYS); } } - /* * Great. Finish the connection relative to server and client, * wake up the client and return the new fd to the server. @@ -569,6 +569,7 @@ UN_DATA(newsock)->sockaddr_un = UN_DATA(sock)->sockaddr_un; UN_DATA(newsock)->sockaddr_len = UN_DATA(sock)->sockaddr_len; wake_up_interruptible(clientsock->wait); + sock_wake_async(clientsock, 0); return(0); } @@ -622,7 +623,9 @@ } if (nonblock) return(-EAGAIN); + sock->flags |= SO_WAITDATA; interruptible_sleep_on(sock->wait); + sock->flags &= ~SO_WAITDATA; if (current->signal & ~current->blocked) { return(-ERESTARTSYS); @@ -655,7 +658,10 @@ ubuf += cando; todo -= cando; if (sock->state == SS_CONNECTED) + { wake_up_interruptible(sock->conn->wait); + sock_wake_async(sock->conn, 2); + } avail = UN_BUF_AVAIL(upd); } while(todo && avail); @@ -690,8 +696,10 @@ while(!(space = UN_BUF_SPACE(pupd))) { + sock->flags |= SO_NOSPACE; if (nonblock) return(-EAGAIN); + sock->flags &= ~SO_NOSPACE; interruptible_sleep_on(sock->wait); if (current->signal & ~current->blocked) { @@ -745,7 +753,10 @@ ubuf += cando; todo -= cando; if (sock->state == SS_CONNECTED) + { wake_up_interruptible(sock->conn->wait); + sock_wake_async(sock->conn, 1); + } space = UN_BUF_SPACE(pupd); } while(todo && space);