Linux 0.99 patchlevel 12 released

As promised, the 0.99.12 kernel made it out this weekend: it's
essentially the last ALPHA-pl12 with some minor changes that shouldn't
break anything (famous last words) while they should be a boon
especially for NFS users.

Nic.funet.fi: pub/OS/Linux/PEOPLE/Linus now contains the 0.99.12 kernel
as both full source (linux-0.99.12.tar.gz) and as patches against pl11
(linux-0.99.patch12.gz).  It's usually easier to get the full sources:
expecially due to some cosmetic fixes the patches are pretty large.

Note that kbd.tar.gz (at the same place as the kernel) has been updated
yet again to fix some problems with the Swiss keyboard mappings.  Hope
that caught the last of these problems.  Also note that the manual pages
in kbd.tar.gz aren't up-to-date but that the format of the keyboard
files shouldn't really pose any problems as they are pretty self-
explanatory (the man-pages will be fixed eventually, probabably not by
me).

Also note that the pl12 kernel is more strict about routing entries, and
that there is a bug in the 4.4.1 library which may make adding routes
extremely difficult (especially if you try to add a C-net route that has
been subnetted from a B-net).  Libc-4.4.2 fixes the problem, but if you
don't use subnetting or any other special netmasks, you'll never see the
bug anyway.

I'm including some of the README so that people can see what's new..

		Linus

PS.  The network card configs are now in the main "make config", but you
should check the net/inet/CONFIG file as well anyway.  Also, the 3c509
driver is probably not functional yet, so don't get too excited.
----------

	Linux kernel release 0.99 patchlevel 12

These are the release notes for linux version 0.99.12.  Read them
carefully, as they tell you what's new, explain how to install the
kernel, and what to do if something goes wrong.

NOTE! There has been some indication that gcc versions older than 2.4.5
result in bad kernels being built: 2.3.3 will fail even to build the
kernel, and I have at least one report of trouble with a 2.4.3-built
kernel that went away when the kernel was recompiled with 2.4.5.

CHANGES since 0.99 patchlevel 11 and earlier:

 - The memory manager cleanup has continued, and seems to be mostly
   ready, as proven by the ease of adding mmap() over NFS with the new
   routines.  So yes, the pl12 kernel will demand-load your binaries
   over NFS, sharing code and clean data, as well as running shared
   libraries over NFS.  Memory management by Eric and me, while the NFS
   mmap code was written by Jon Tombs,

 - ** IMPORTANT **: The keyboard driver has been enhanced even further,
   and almost everything is completely re-mappable.  This means that
   there is a new version of 'loadkeys' and 'dumpkeys' that you must use
   with this kernel or you'll have problems.  The default keyboard is
   still the US mapping, but if you want to create your own mappings
   you'll have to load them with the new binaries.  Get the 'kbd.tar.gz'
   archive from the same place you get the kernel.

   The new keymappings allow things like function key string changes,
   remapping of the control keys, and freedom to remap any of the normal
   keyboard functions: including special features like rebooting,
   console switching etc.  The keyboard remapping code has been done
   mostly by Risto Kankkunen (Risto.Kankku...@Helsinki.FI).

 - updated network drivers by Donald Becker

 - updated serial drivers - ty...@Athena.mit.edu

 - updated 387 emulation (Bill Metzenthen).  The updated emulator code
   has more exact trigonometric functions and improved exception
   handling.  It now behaves very much like a real 486, with only small
   changes (greater accuracy, slightly different denormal NaN handling
   etc - hard to detect the differences even if you are looking for
   them).

 - network timer fixes by Florian La Roche (much cleaned up net/inet/timer.c
   and some bad race-conditions fixed).

 - Scsi code updates by Eric Youngdale and others

 - Sony CDU-31A CDROM driver by Corey Minyard added to the standard
   kernel distribution.

 - The Mitsumi CDROM driver is now part of the standard kernel.  Driver
   by Martin Harriss with patches by stu...@cc4.kuleuven.ac.be (yes, he
   probably has a real name, but no, I haven't found it) and Jon Tombs.

 - various other minor patches (preliminary ldt support etc)

[ rest deleted ]
diff --git a/Makefile b/Makefile
index 7ee2bf5..e8a3c95 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 
 .EXPORT_ALL_VARIABLES:
 
-BASH = $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
+CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
 	  else if [ -x /bin/bash ]; then echo /bin/bash; \
 	  else echo sh; fi ; fi)
 
@@ -96,13 +96,8 @@
 Version: dummy
 	rm -f tools/version.h
 
-lilo: $(CONFIGURE) Image
-	if [ -f /vmlinux ]; then mv /vmlinux /vmlinux.old; fi
-	cat Image > /vmlinux
-	/etc/lilo/install
-
 config:
-	$(BASH) Configure $(OPTS) < config.in
+	$(CONFIG_SHELL) Configure $(OPTS) < config.in
 	mv .config~ .config
 	$(MAKE) soundconf
 
@@ -116,20 +111,13 @@
 
 tools/version.h: $(CONFIGURE) Makefile
 	@./makever.sh
-	@echo \#define UTS_RELEASE \"0.99.11\" > tools/version.h
+	@echo \#define UTS_RELEASE \"0.99.12\" > tools/version.h
 	@echo \#define UTS_VERSION \"\#`cat .version` `date`\" >> tools/version.h
 	@echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h
 	@echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h
 	@echo \#define LINUX_COMPILE_HOST \"`hostname`\" >> tools/version.h
 	@echo \#define LINUX_COMPILE_DOMAIN \"`domainname`\" >> tools/version.h
 
-Image: $(CONFIGURE) boot/bootsect boot/setup tools/system tools/build
-	tools/build boot/bootsect boot/setup tools/system $(ROOT_DEV) > Image
-	sync
-
-disk: Image
-	dd bs=8192 if=Image of=/dev/fd0
-
 tools/build: $(CONFIGURE) tools/build.c
 	$(HOSTCC) $(CFLAGS) \
 	-o tools/build tools/build.c
@@ -246,3 +234,27 @@
 dummy:
 
 endif
+
+#
+# Leave these dummy entries for now to tell people that they are going away..
+#
+lilo:
+	@echo
+	@echo Uncompressed kernel images no longer supported. Use
+	@echo \"make zlilo\" instead.
+	@echo
+	@exit 1
+
+Image:
+	@echo
+	@echo Uncompressed kernel images no longer supported. Use
+	@echo \"make zImage\" instead.
+	@echo
+	@exit 1
+
+disk:
+	@echo
+	@echo Uncompressed kernel images no longer supported. Use
+	@echo \"make zdisk\" instead.
+	@echo
+	@exit 1
diff --git a/README b/README
index b10f98a..dfd94ed 100644
--- a/README
+++ b/README
@@ -1,109 +1,249 @@
 
-		DON'T PANIC
+	Linux kernel release 0.99 patchlevel 12
 
-This is the README for the Linux kernel sources.  It tells a few small
-things about kernel configuration and other things that can perhaps be
-useful if you want to compile the kernel from scratch.  It leaves out a
-LOT as well, but it shouldn't really be that hard to compile the kernel. 
-I hope. 
+These are the release notes for linux version 0.99.12.  Read them
+carefully, as they tell you what's new, explain how to install the
+kernel, and what to do if something goes wrong. 
 
-In order to compile this version of the kernel you need GCC 2.4.3 or
-newer (older compiler versions have problems - no guarantees it will
-even compile much less work).  Some makefile targets require special
-commands which may not be available on all machines (see below).  Normal
-utilities like ls etc are not explicitly listed, they are assumed to be
-available on all systems. 
+NOTE! There has been some indication that gcc versions older than 2.4.5
+result in bad kernels being built: 2.3.3 will fail even to build the
+kernel, and I have at least one report of trouble with a 2.4.3-built
+kernel that went away when the kernel was recompiled with 2.4.5. 
 
-Kernel sources are usually kept in /usr/src/linux.  If you have them
-elsewhere, you will have to change path names in a few places. 
-Generally, if you aren't sure of what you are doing, make your life
-easier by using the standard /usr/src/linux source tree.  Filenames that
-aren't absolute are supposed to be relative to the toplevel kernel
-source directory. 
+CHANGES since 0.99 patchlevel 11 and earlier:
 
-* Basic configuration
+ - The memory manager cleanup has continued, and seems to be mostly
+   ready, as proven by the ease of adding mmap() over NFS with the new
+   routines.  So yes, the pl12 kernel will demand-load your binaries
+   over NFS, sharing code and clean data, as well as running shared
+   libraries over NFS.  Memory management by Eric and me, while the NFS
+   mmap code was written by Jon Tombs,
 
-	* SETUP
+ - ** IMPORTANT **: The keyboard driver has been enhanced even further,
+   and almost everything is completely re-mappable.  This means that
+   there is a new version of 'loadkeys' and 'dumpkeys' that you must use
+   with this kernel or you'll have problems.  The default keyboard is
+   still the US mapping, but if you want to create your own mappings
+   you'll have to load them with the new binaries.  Get the 'kbd.tar.gz'
+   archive from the same place you get the kernel. 
 
-1.  make sure /usr/include/asm and /usr/include/linux are symlinks to
-the linux source tree include files.  The output of
+   The new keymappings allow things like function key string changes,
+   remapping of the control keys, and freedom to remap any of the normal
+   keyboard functions: including special features like rebooting,
+   console switching etc.  The keyboard remapping code has been done
+   mostly by Risto Kankkunen (Risto.Kankkunen@Helsinki.FI). 
 
-  # ls -ld /usr/include/asm /usr/include/linux
+ - updated network drivers by Donald Becker
 
-should look like this:
+ - updated serial drivers - tytso@Athena.mit.edu
 
-  lrwxrwxrwx   1 root     root           26 Apr 19 20:03 /usr/include/asm -> /usr/src/linux/include/asm
-  lrwxrwxrwx   1 root     root           28 Apr 19 20:03 /usr/include/linux -> /usr/src/linux/include/linux
+ - updated 387 emulation (Bill Metzenthen).  The updated emulator code
+   has more exact trigonometric functions and improved exception
+   handling.  It now behaves very much like a real 486, with only small
+   changes (greater accuracy, slightly different denormal NaN handling
+   etc - hard to detect the differences even if you are looking for
+   them). 
 
-If it doesn't, create the appropriate symlinks with
+ - network timer fixes by Florian La Roche (much cleaned up net/inet/timer.c
+   and some bad race-conditions fixed).
 
-  # cd /usr/include
-  # rm -rf linux asm
-  # ln -s /usr/src/linux/include/linux .
-  # ln -s /usr/src/linux/include/asm .
+ - Scsi code updates by Eric Youngdale and others
 
-Also, if you are installing a new version of linux over the sources of
-an old one (or have user kernel patches to get a new version), you
-should probably do a "make mrproper" to remove any traces of old object
-files or incorrect dependency information. 
+ - Sony CDU-31A CDROM driver by Corey Minyard added to the standard
+   kernel distribution. 
 
-2.  Edit Makefile: Check the definitions of macros ROOTDEV, RAMDISK and
-SVGA_MODE before you run make.  They are explained in the Makefile. 
+ - The Mitsumi CDROM driver is now part of the standard kernel.  Driver
+   by Martin Harriss with patches by stud11@cc4.kuleuven.ac.be (yes, he
+   probably has a real name, but no, I haven't found it) and Jon Tombs. 
 
-3.  Run "make config" in /usr/src/linux, and answer the questions that
-the config script asks you.  It should hopefully set up most of the rest
-of the flags for your system. 
+ - various other minor patches (preliminary ldt support etc)
 
-4.  Run "make dep" to set up all the dependencies correctly.  The
-default dependencies may not fit your system due to different compiler
-versions or similar.  Also, you may wish to run "make clean" first to
-make sure you don't have any old object files that mess things up if you
-have changed or patched your kernel. 
+NOTABLE changes since patchlevel 10 or earlier:
 
-* Running make
+ - The memory manager has been cleaned up substantially, and mmap()
+   works for MAP_PRIVATE.  MAP_SHARED is still not supported for
+   anything else than /dev/mem, but even so it actually is usable for a
+   lot of applications.  The shared library routines have been rewritten
+   to use mmap() instead of the old hardcoded behaviour. 
 
-Unless you know what you're doing, don't ever run the makefiles in
-subdirectories by hand.  There is a bit of interaction between the
-various makefiles, e.g.  in the form of inherited macros and the like. 
+ - The kernel is now compiled with C++ instead of plain C.  Very few
+   actual C++ features are used, but even so C++ allows for more
+   type-checking and type-safe linkage. 
 
-The following targets all apply for the makefile at the root of the
-kernel source tree. 
+ - The filesystem routines have been cleaned up for multiple block
+   sizes.  None of the filesystems use it yet, but people are working on
+   it. 
 
-"make" or "make all" compiles the kernel and makes a compressed kernel
-image called "zImage".  It also bumps compilation numbers to help you
-keep track of different kernels. 
+ - named pipes and normal pipes should hopefully have the right select()
+   semantics in the presense/absense of writers. 
 
-"make Image" is like "make all", but it doesn't bump the number in
-.version, which tells how many times this version has been compiled
-(helps you differentiate between different configurations etc). 
+ - QIC-02 tape driver by Hennus Bergman
 
-"make disk" is like "make Image", but it additionally writes out a copy
-of the boot image to a floppy in your first floppy drive (/dev/fd0;
-change the filename if you want a different floppy).  You need to have a
-formatted, overwritable floppy in that drive when it is time to do the
-copy.  This requires dd. 
+ - selection patches in the default kernel
 
-"make zdisk" and "make zImage" are the same as their 'z-less'
-counterparts, but create a compressed kernel that autodecompresses on
-bootup.  This is the preferred mode of operation, as it both allows for
-a larger kernel and makes the images smaller. 
+ - fixed a bug in the pty code which led to busy waiting in some
+   circumstances instead of sleeping. 
 
-"make dep" updates all dependencies.  This requires sed.  It modifies
-the makefiles directly (the end of them, starting at the ###Dependencies
--line at the end).  "make dep" is required after patching, or the kernel
-may not compile cleanly. 
+ - Compressed SLIP support (Charles Hedrick). See net/inet/CONFIG
 
-"make clean" will remove all object files and other files created by the
-compilation.  This requires basename. 
+ - the 'clear_bit()' function was changed to return the previous setting
+   of the bit instead of the old "error-code".  This makes use of the
+   bit operations more logical. 
 
-You may wish to redirect compiler error messages to a file so that you
-can review them later and to ease problem fixing.  You can do this with
-Bash with:
+ - udelay() function for short delays (busy-waiting) added.  Used
+   currently only by the QIC driver. 
 
-	make something 2>&1 | tee make.out
+ - fork() and sheduler changes to make task switches happen only from
+   kernel mode to kernel mode.  Cleaner and more portable than the old
+   code which counted on being able to task-switch directly into user
+   mode. 
 
-The tee part is so that you can check what is going on while the
-compilation runs.  If you have GNU emacs and use M-x compile you don't
-need this, of course. 
+ - debugging malloc code.
 
-		Lars Wirzenius & Linus Torvalds
+INSTALLING the kernel:
+
+ - if you install by patching, you need a *clean* 0.99.11 source tree,
+   which presumably exists in /usr/src/linux.  If so, to get the kernel
+   patched, just do a
+
+		cd /usr/src
+		patch -p0 < linux-0.99.patch12
+
+   and you should be ok.  You may want to remove the backup files (xxx~
+   or xxx.orig), and make sure that there are no failed patches (xxx# or
+   xxx.rej).
+
+ - If you install the full sources, do a
+
+		cd /usr/src
+		tar xvf linux-0.99.12.tar
+
+   to get it all put in place.
+
+ - make sure your /usr/include/linux and /usr/include/asm directories
+   are just symlinks to the kernel sources:
+
+		cd /usr/include
+		rm -rf linux
+		rm -rf asm
+		ln -s /usr/src/linux/include/linux .
+		ln -s /usr/src/linux/include/asm .
+
+ - make sure you have no stale .o files and dependencies lying around:
+
+		cd /usr/src/linux
+		make mrproper
+
+   You should now have the sources correctly installed.
+
+CONFIGURING the kernel:
+
+ - do a "make config" to configure the basic kernel.  "make config"
+   needs bash to work: it will search for bash in $BASH, /bin/bash and
+   /bin/sh (in that order), so hopefully one of those is correct. 
+
+	NOTES on "make config":
+	- compiling the kernel with "-m486" for a number of 486-specific
+	  will result in a kernel that still works on a 386: it may be
+	  slightly larger and possibly slower by an insignificant amount,
+	  but it should not hurt performance. 
+	- A kernel with math-emulation compiled in will still use the
+	  coprocessor if one is present: the math emulation will just
+	  never get used in that case.  The kernel will be slighly larger,
+	  but will work on different machines regardless of whether they
+	  have a math coprocessor or not. 
+	- the "kernel hacking" configuration details usually result in a
+	  bigger or slower kernel (or both), and can even make the kernel
+	  less stable by configuring some routines to actively try to
+	  break bad code to find kernel problems (kmalloc()).  Thus you
+	  should probably answer 'n' to the questions for a "production"
+	  kernel. 
+
+ - edit net/inet/CONFIG to configure the networking parts of the kernel. 
+   The comments should hopefully clarify it all. 
+
+ - Check the top Makefile for further site-dependent configuration
+   (default SVGA mode etc). 
+
+ - Finally, do a "make dep" to set up all the dependencies correctly. 
+
+COMPILING the kernel:
+
+ - make sure you have gcc-2.4.5 or newer available with g++.  It seems
+   older gcc versions can have problems compiling linux 0.99.10 and
+   newer versions.  If you upgrade, remember to get the new binutils
+   package too (for as/ld/nm and company)
+
+ - do a "make zImage" to create a compressed kernel image.  If you want
+   to make a bootdisk (without root filesystem or lilo), insert a floppy
+   in your A: drive, and do a "make zdisk".  It is also possible to do
+   "make zlilo" if you have lilo installed to suit the kernel makefiles,
+   but you may want to check your particular lilo setup first. 
+
+ - keep a backup kernel handy in case something goes wrong. 
+
+ - reboot with the new kernel and enjoy. 
+
+IF SOMETHING GOES WRONG:
+
+ - if you have problems that seem to be due to kernel bugs, please mail
+   them to me (Linus.Torvalds@Helsinki.FI), and possibly to any other
+   relevant mailing-list or to the newsgroup.  The mailing-lists are
+   useful especially for SCSI and NETworking problems, as I can't test
+   either of those personally anyway. 
+
+ - In all bug-reports, *please* tell what kernel you are talking about,
+   how to duplicate the problem, and what your setup is (use your common
+   sense).  If the problem is new, tell me so, and if the problem is
+   old, please try to tell me when you first noticed it.
+
+ - if the bug results in a message like
+
+	unable to handle kernel paging request at address C0000010
+	Oops: 0002
+	EIP:   0010:xxxxxxxx
+	eax: xxxxxxxx   ebx: xxxxxxxx   ecx: xxxxxxxx   edx: xxxxxxxx
+	esi: xxxxxxxx   edi: xxxxxxxx   ebp: xxxxxxxx
+	ds: xxxx  es: xxxx  fs: xxxx  gs: xxxx
+	Pid: xx, process nr: xx
+	xx xx xx xx xx xx xx xx xx xx
+
+   or similar kernel debugging information on your screen or in your
+   system log, please duplicate it *exactly*.  The dump may look
+   incomprehensible to you, but it does contain information that may
+   help debugging the problem.  The text above the dump is also
+   important: it tells something about why the kernel dumped code (in
+   the above example it's due to a bad kernel pointer)
+
+ - in debugging dumps like the above, it helps enourmously if you can
+   look up what the EIP value means.  The hex value as such doesn't help
+   me or anybody else very much: it will depend on your particular
+   kernel setup.  What you should do is take the hex value from the EIP
+   line (ignore the "0010:"), and look it up in the kernel namelist to
+   see which kernel function contains the offending address.
+
+   To find out the kernel function name, you'll need to find the system
+   binary associated with the kernel that exhibited the symptom.  In the
+   case of compressed kernels, this will be 'linux/tools/zSystem', while
+   uncompressed kernels use the file 'tools/system'.  To extract the
+   namelist and match it against the EIP from the kernel crash, do:
+
+		nm tools/zSystem | sort | less
+
+   This will give you a list of kernel addresses sorted in ascending
+   order, from which it is simple to find the function that contains the
+   offending address.  Note that the address given by the kernel
+   debugging messages will not necessarily match exactly with the
+   function addresses (in fact, that is very unlikely), so you can't
+   just 'grep' the list: the list will, however, give you the starting
+   point of each kernel function, so by looking for the function that
+   has a starting address lower than the one you are searching for but
+   is followed by a function with a higher address you will find the one
+   you want.  In fact, it may be a good idea to include a bit of
+   "context" in your problem report, giving a few lines around the
+   interesting one. 
+
+   If you for some reason cannot do the above (you have a pre-compiled
+   kernel image or similar), telling me as much about your setup as
+   possible will help. 
+
diff --git a/boot/head.S b/boot/head.S
index ed12f94..322f198 100644
--- a/boot/head.S
+++ b/boot/head.S
@@ -35,6 +35,20 @@
 	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
@@ -72,16 +86,6 @@
 	rep
 	movsb
 1:
-/*
- * Clear BSS
- */
-	xorl %eax,%eax
-	movl $__edata,%edi
-	movl $__end,%ecx
-	subl %edi,%ecx
-	cld
-	rep
-	stosb
 /* check if it is 486 or 386. */
 /*
  * XXX - this does a lot of unnecessary setup.  Alignment checks don't
@@ -90,6 +94,7 @@
  */
 	movl %esp,%edi		# save stack pointer
 	andl $0xfffffffc,%esp	# align stack to avoid AC fault
+	movl $3,_x86
 	pushfl			# push EFLAGS
 	popl %eax		# get EFLAGS
 	movl %eax,%ecx		# save original EFLAGS
@@ -100,28 +105,31 @@
 	popl %eax		# put it in eax
 	xorl %ecx,%eax		# change in flags
 	andl $0x40000,%eax	# check if AC bit changed
-	jnz 1f			# 486
-	pushl %ecx		# restore original EFLAGS
-	popfl
-	movl %edi,%esp		# restore esp
-	movl %cr0,%eax		# 386
-	andl $0x80000011,%eax	# Save PG,PE,ET
-	orl $2,%eax		# set MP
-	jmp 2f	
-/*
- * NOTE! 486 should set bit 16, to check for write-protect in supervisor
- * mode. Then it would be unnecessary with the "verify_area()"-calls.
- * 486 users probably want to set the NE (#5) bit also, so as to use
- * int 16 for math errors.
- * XXX - the above is out of date.  We set all the bits, but don't take
- * advantage of WP (26 Dec 92).
- */
-1:	pushl %ecx		# restore original EFLAGS
+	je is386
+	movl $4,_x86
+	movl %ecx,%eax
+	xorl $0x200000,%eax	# check ID flag
+	pushl %eax
+	popfl			# if we are on a 486,
+	pushfl			# we can't change it
+	popl %eax
+	xorl %ecx,%eax
+	andl $0x200000,%eax
+	je is486
+	movl $5,_x86		# 586 setup same as 486 at least for now
+is486:	pushl %ecx		# restore original EFLAGS
 	popfl
 	movl %edi,%esp		# restore esp
 	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 %edi,%esp		# restore esp
+	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
@@ -134,9 +142,11 @@
 	mov %ax,%fs
 	mov %ax,%gs
 	lss _stack_start,%esp
-	pushl $0		# These are the parameters to main :-)
-	pushl $0
-	pushl $0
+	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:
@@ -148,6 +158,7 @@
  */
 check_x87:
 	movl $0,_hard_math
+	clts
 	fninit
 	fstsw %ax
 	cmpb $0,%al
diff --git a/config.in b/config.in
index 5620e2c..e122208 100644
--- a/config.in
+++ b/config.in
@@ -5,7 +5,7 @@
 *
 * General setup
 *
-bool 'Kernel math emulation' CONFIG_MATH_EMULATION n
+bool 'Kernel math emulation' CONFIG_MATH_EMULATION y
 bool 'Normal harddisk support' CONFIG_BLK_DEV_HD y
 bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
 bool 'TCP/IP networking' CONFIG_INET y
@@ -18,7 +18,7 @@
 bool 'SCSI support?' CONFIG_SCSI n
 if [ "$CONFIG_SCSI" = "n" ]
 :
-: Skipping SCSI  onfiguration options...
+: Skipping SCSI configuration options...
 :
 else
 	*
@@ -38,6 +38,32 @@
 bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n
 fi
 *
+* Network device support
+*
+bool 'Network device support?' CONFIG_ETHERCARDS y
+if [ "$CONFIG_ETHERCARDS" = "n" ]
+:
+: Skipping ethercard configuration options...
+:
+else
+bool 'SLIP (serial line) support' CONFIG_SLIP n
+bool 'PLIP (parallel port) support' CONFIG_PLIP n
+bool 'NE2000/NE1000 support' CONFIG_NE2000 y
+bool 'WD80*3 support' CONFIG_WD80x3 y
+#bool '3c501 support' CONFIG_EL1 n
+bool '3c503 support' CONFIG_EL2 y
+#bool '3c509 support' CONFIG_EL3 n
+bool 'HP PCLAN support' CONFIG_HPLAN y
+bool 'AT1500 and NE2100 support' CONFIG_AT1500 y
+#bool 'DEPCA support' CONFIG_DEPCA n
+bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 y
+#bool 'AT-LAN-TEC pocket adaptor support' CONFIG_ATP n
+#bool 'EtherExpress support' CONFIG_EEXPRESS n
+fi
+*
+bool 'Sony CDU31A CDROM driver support' CONFIG_CDU31A n
+bool 'Mitsumi CDROM driver support' CONFIG_MCD n
+*
 * Filesystems
 *
 bool 'Standard (minix) fs support' CONFIG_MINIX_FS y
@@ -46,12 +72,13 @@
 bool 'xiafs filesystem support' CONFIG_XIA_FS n
 bool 'msdos fs support' CONFIG_MSDOS_FS y
 bool '/proc filesystem support' CONFIG_PROC_FS y
-bool 'NFS filesystem support' CONFIG_NFS_FS n
+bool 'NFS filesystem support' CONFIG_NFS_FS y
 bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
 *
 *  character devices
 *
 bool 'Keyboard meta-key sends ESC-prefix' CONFIG_KBD_META y
+bool 'Keyboard Num Lock on by default' CONFIG_KBD_NUML y
 bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
 bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
 bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
@@ -67,4 +94,6 @@
 *
 bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
 bool 'Kernel profiling support' CONFIG_PROFILE n
-
+if [ "$CONFIG_SCSI" = "y" ]
+bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS n
+fi
diff --git a/fs/block_dev.c b/fs/block_dev.c
index c50b9b8..4fdf3a8 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -44,7 +44,7 @@
 	if (blk_size[MAJOR(dev)])
 		size = (blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS) >> blocksize_bits;
 	else
-		size = 0x7fffffff;
+		size = INT_MAX;
 	while (count>0) {
 		if (block >= size)
 			return written;
@@ -106,7 +106,7 @@
 	if (blk_size[MAJOR(dev)])
 		size = blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS;
 	else
-		size = 0x7fffffff;
+		size = INT_MAX;
 
 	if (offset > size)
 		left = 0;
diff --git a/fs/buffer.c b/fs/buffer.c
index e3eb636..dc767ac 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -6,7 +6,7 @@
 
 /*
  *  'buffer.c' implements the buffer-cache functions. Race-conditions have
- * been avoided by NEVER letting a interrupt change a buffer (except for the
+ * been avoided by NEVER letting an interrupt change a buffer (except for the
  * data, of course), but instead letting the caller do it.
  */
 
@@ -38,6 +38,12 @@
 extern int revalidate_scsidisk(int, int);
 #endif
 #endif
+#ifdef CONFIG_CDU31A
+extern int check_cdu31a_media_change(int, int);
+#endif
+#ifdef CONFIG_MCD
+extern int check_mcd_media_change(int, int);
+#endif
 
 static struct buffer_head * hash_table[NR_HASH];
 static struct buffer_head * free_list = NULL;
@@ -233,6 +239,18 @@
 		break;
 #endif
 
+#if defined(CONFIG_CDU31A)
+         case 15: /* Sony CDROM */
+		i = check_cdu31a_media_change(dev, 0);
+		break;
+#endif
+
+#if defined(CONFIG_MCD)
+         case 23: /* Sony CDROM */
+		i = check_mcd_media_change(dev, 0);
+		break;
+#endif
+
          default:
 		return;
 	};
@@ -379,13 +397,15 @@
 	if (!blksize_size[MAJOR(dev)])
 		return;
 
-	if (size != 512 && size != 1024 && size != 2048 &&  size != 4096) 
-		panic("Invalid blocksize passed to set_blocksize");
+	switch(size) {
+		default: panic("Invalid blocksize passed to set_blocksize");
+		case 512: case 1024: case 2048: case 4096:;
+	}
 
 	if (blksize_size[MAJOR(dev)][MINOR(dev)] == 0 && size == BLOCK_SIZE) {
 		blksize_size[MAJOR(dev)][MINOR(dev)] = size;
 		return;
-	};
+	}
 	if (blksize_size[MAJOR(dev)][MINOR(dev)] == size)
 		return;
 	sync_buffers(dev, 2);
@@ -437,7 +457,7 @@
 	grow_size -= size;
 	if (nr_free_pages > min_free_pages && grow_size <= 0) {
 		grow_buffers(size);
-		grow_size = 4096;
+		grow_size = PAGE_SIZE;
 	}
 	buffers = nr_buffers;
 	bh = NULL;
@@ -588,19 +608,18 @@
 
 static void get_more_buffer_heads(void)
 {
-	unsigned long page;
+	int i;
 	struct buffer_head * bh;
 
 	if (unused_list)
 		return;
-	page = get_free_page(GFP_BUFFER);
-	if (!page)
+
+	if(! (bh = (struct buffer_head*) get_free_page(GFP_BUFFER)))
 		return;
-	bh = (struct buffer_head *) page;
-	while ((unsigned long) (bh+1) <= page+4096) {
-		put_unused_buffer_head(bh);
-		bh++;
-		nr_buffer_heads++;
+
+	for (nr_buffer_heads+=i=PAGE_SIZE/sizeof*bh ; i>0; i--) {
+		bh->b_next_free = unused_list;	/* only make link */
+		unused_list = bh++;
 	}
 }
 
@@ -632,8 +651,8 @@
 	unsigned long offset;
 
 	head = NULL;
-	offset = 4096;
-	while ((offset -= size) < 4096) {
+	offset = PAGE_SIZE;
+	while ((offset -= size) < PAGE_SIZE) {
 		bh = get_unused_buffer_head();
 		if (!bh)
 			goto no_grow;
@@ -685,14 +704,14 @@
 	int nrbuf;
 
 	page = (unsigned long) first->b_data;
-	if (page & 0xfff) {
+	if (page & ~PAGE_MASK) {
 		brelse(first);
 		return 0;
 	}
 	mem_map[MAP_NR(page)]++;
 	bh[0] = first;
 	nrbuf = 1;
-	for (offset = size ; offset < 4096 ; offset += size) {
+	for (offset = size ; offset < PAGE_SIZE ; offset += size) {
 		block = *++b;
 		if (!block)
 			goto no_go;
@@ -728,7 +747,7 @@
 	if (!bh)
 		return 0;
 	p = b;
-	for (offset = 0 ; offset < 4096 ; offset += size) {
+	for (offset = 0 ; offset < PAGE_SIZE ; offset += size) {
 		block = *(p++);
 		if (!block)
 			goto not_aligned;
@@ -755,7 +774,7 @@
 		else
 			break;
 	}
-	buffermem += 4096;
+	buffermem += PAGE_SIZE;
 	bh->b_this_page = tmp;
 	mem_map[MAP_NR(address)]++;
 	read_buffers(arr,block);
@@ -847,12 +866,11 @@
 	unsigned long page;
 	struct buffer_head *bh, *tmp;
 
-	if ((size & 511) || (size > 4096)) {
+	if ((size & 511) || (size > PAGE_SIZE)) {
 		printk("VFS: grow_buffers: size = %d\n",size);
 		return;
 	}
-	page = get_free_page(GFP_BUFFER);
-	if (!page)
+	if(!(page = __get_free_page(GFP_BUFFER)))
 		return;
 	bh = create_buffers(page, size);
 	if (!bh) {
@@ -878,7 +896,7 @@
 			break;
 	}
 	tmp->b_this_page = bh;
-	buffermem += 4096;
+	buffermem += PAGE_SIZE;
 	return;
 }
 
@@ -893,7 +911,7 @@
 
 	*bhp = bh;
 	page = (unsigned long) bh->b_data;
-	page &= 0xfffff000;
+	page &= PAGE_MASK;
 	tmp = bh;
 	do {
 		if (!tmp)
@@ -912,7 +930,7 @@
 		remove_from_queues(p);
 		put_unused_buffer_head(p);
 	} while (tmp != bh);
-	buffermem -= 4096;
+	buffermem -= PAGE_SIZE;
 	free_page(page);
 	return !mem_map[MAP_NR(page)];
 }
diff --git a/fs/exec.c b/fs/exec.c
index 74451c8..10c63e7 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -15,6 +15,12 @@
  *
  * Once more I can proudly say that linux stood up to being changed: it
  * was less than 2 hours work to get demand-loading completely implemented.
+ *
+ * Demand loading changed July 1993 by Eric Youngdale.   Use mmap instead,
+ * current->executable is only used by the procfs.  This allows a dispatch
+ * table to check for several different types  of binary formats.  We keep
+ * trying until we recognize the file or we run out of supported binary
+ * formats. 
  */
 
 #include <linux/fs.h>
@@ -31,17 +37,56 @@
 #include <linux/ptrace.h>
 #include <linux/user.h>
 #include <linux/segment.h>
+#include <asm/system.h>
+
+#include <linux/binfmts.h>
 
 #include <asm/segment.h>
+#include <asm/system.h>
+
+extern "C" int sys_exit(int exit_code);
+extern "C" int sys_close(unsigned fd);
+extern "C" int sys_open(const char *, int, int);
 
 extern void shm_exit (void);
 
-/*
- * MAX_ARG_PAGES defines the number of pages allocated for arguments
- * and envelope for the new program. 32 should suffice, this gives
- * a maximum env+arg of 128kB !
- */
-#define MAX_ARG_PAGES 32
+static int open_inode(struct inode * inode, int mode)
+{
+	int error, fd;
+	struct file *f, **fpp;
+
+	if (!inode->i_op || !inode->i_op->default_file_ops)
+		return -EINVAL;
+	f = get_empty_filp();
+	if (!f)
+		return -EMFILE;
+	fd = 0;
+	fpp = current->filp;
+	for (;;) {
+		if (!*fpp)
+			break;
+		if (++fd > NR_OPEN)
+			return -ENFILE;
+		fpp++;
+	}
+	*fpp = f;
+	f->f_flags = mode;
+	f->f_mode = (mode+1) & O_ACCMODE;
+	f->f_inode = inode;
+	f->f_pos = 0;
+	f->f_reada = 0;
+	f->f_op = inode->i_op->default_file_ops;
+	if (f->f_op->open) {
+		error = f->f_op->open(inode,f);
+		if (error) {
+			*fpp = NULL;
+			f->f_count--;
+			return error;
+		}
+	}
+	inode->i_count++;
+	return fd;
+}
 
 /*
  * These are the only things you should do on a core-file: use only these
@@ -78,9 +123,6 @@
 		return 0;
 	current->dumpable = 0;
 
-	if (current->elf_executable)
-		return 0;
-
 /* See if we have enough room to write the upage.  */
 	if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE)
 		return 0;
@@ -185,54 +227,27 @@
  */
 extern "C" int sys_uselib(const char * library)
 {
-	int fd, error;
+	int fd, retval;
 	struct file * file;
-	struct inode * inode;
-	struct exec ex;
-	unsigned int len;
-	unsigned int bss;
+	struct linux_binfmt * fmt;
 
 	fd = sys_open(library, 0, 0);
 	if (fd < 0)
 		return fd;
 	file = current->filp[fd];
-	if (!file || !(inode = file->f_inode) || !file->f_op || !file->f_op->read) {
-		sys_close(fd);
-		return -ENOEXEC;
+	retval = -ENOEXEC;
+	if (file && file->f_inode && file->f_op && file->f_op->read) {
+		fmt = formats;
+		do {
+			int (*fn)(int) = fmt->load_shlib;
+			if (!fn)
+				break;
+			retval = fn(fd);
+			fmt++;
+		} while (retval == -ENOEXEC);
 	}
-	set_fs(KERNEL_DS);
-	if (file->f_op->read(inode, file, (char *) &ex, sizeof(ex)) != sizeof(ex)) {
-		sys_close(fd);
-		return -EACCES;
-	}
-	set_fs(USER_DS);
-
-	/* We come in here for the regular a.out style of shared libraries */
-	if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize ||
-	    ex.a_drsize || ex.a_entry & 0xfff ||
-	    inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
-		sys_close(fd);
-		return -ENOEXEC;
-	}
-	if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && 
-	    (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
-		printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
-		sys_close(fd);
-		return -ENOEXEC;
-	}
-
-	/* Now use mmap to map the library into memory. */
-	error = do_mmap(file, ex.a_entry, ex.a_text + ex.a_data,
-		PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE,
-		N_TXTOFF(ex));
 	sys_close(fd);
-	if (error != ex.a_entry)
-		return error;
-	len = (ex.a_text + ex.a_data + 0xfff) & 0xfffff000;
-	bss = ex.a_text + ex.a_data + ex.a_bss;
-	if (bss > len)
-		zeromap_page_range(ex.a_entry + len, bss-len, PAGE_COPY);
-	return 0;
+  	return retval;
 }
 
 /*
@@ -240,7 +255,7 @@
  * memory and creates the pointer tables from them, and puts their
  * addresses on the "stack", returning the new stack pointer value.
  */
-static unsigned long * create_tables(char * p,int argc,int envc)
+unsigned long * create_tables(char * p,int argc,int envc)
 {
 	unsigned long *argv,*envp;
 	unsigned long * sp;
@@ -351,7 +366,7 @@
 	return p;
 }
 
-static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
+unsigned long change_ldt(unsigned long text_size,unsigned long * page)
 {
 	unsigned long code_limit,data_limit,code_base,data_base;
 	int i;
@@ -363,8 +378,10 @@
 	data_base += data_limit;
 	for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
 		data_base -= PAGE_SIZE;
-		if (page[i])
+		if (page[i]) {
+			current->rss++;
 			put_dirty_page(current,page[i],data_base);
+		}
 	}
 	return data_limit;
 }
@@ -374,7 +391,7 @@
  * that aren't on a block boundary, and for files on filesystems
  * without bmap support.
  */
-static int read_exec(struct inode *inode, unsigned long offset,
+int read_exec(struct inode *inode, unsigned long offset,
 	char * addr, unsigned long count)
 {
 	struct file file;
@@ -413,14 +430,16 @@
  * that a new one can be started
  */
 
-static void flush_old_exec(struct inode * inode, char * filename, int e_uid, int e_gid)
+void flush_old_exec(struct linux_binprm * bprm)
 {
 	int i;
 	int ch;
+	char * name;
 	struct vm_area_struct * mpnt, *mpnt1;
 
 	current->dumpable = 1;
-	for (i=0; (ch = get_fs_byte(filename++)) != '\0';) {
+	name = bprm->filename;
+	for (i=0; (ch = *(name++)) != '\0';) {
 		if (ch == '/')
 			i = 0;
 		else
@@ -445,7 +464,22 @@
 		kfree(mpnt);
 		mpnt = mpnt1;
 	}
-	if (e_uid != current->euid || e_gid != current->egid || !permission(inode,MAY_READ))
+
+	/* Flush the old ldt stuff... */
+	if (current->ldt) {
+		free_page((unsigned long) current->ldt);
+		current->ldt = NULL;
+		for (i=1 ; i<NR_TASKS ; i++) {
+			if (task[i] == current)  {
+				set_ldt_desc(gdt+(i<<1)+
+					     FIRST_LDT_ENTRY,&default_ldt, 1);
+				load_ldt(i);
+			}
+		}	
+	}
+
+	if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || 
+	    !permission(bprm->inode,MAY_READ))
 		current->dumpable = 0;
 	current->signal = 0;
 	for (i=0 ; i<32 ; i++) {
@@ -468,78 +502,72 @@
 /*
  * sys_execve() executes a new program.
  */
-extern "C" int sys_execve(struct pt_regs regs)
+static int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
 {
-	char * filename = (char *) regs.ebx;
-	char ** argv = (char **) regs.ecx;
-	char ** envp = (char **) regs.edx;
-	struct inode * inode;
-	char buf[128];
+	struct linux_binprm bprm;
+	struct linux_binfmt * fmt;
 	unsigned long old_fs;
-	struct exec ex;
-	unsigned long page[MAX_ARG_PAGES];
-	int i,argc,envc;
-	int e_uid, e_gid;
+	int i;
 	int retval;
 	int sh_bang = 0;
-	unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4;
 
-	if (regs.cs != USER_CS)
-		panic("VFS: execve called from supervisor mode");
+	if (regs->cs != USER_CS)
+		return -EINVAL;
+	bprm.p = PAGE_SIZE*MAX_ARG_PAGES-4;
 	for (i=0 ; i<MAX_ARG_PAGES ; i++)	/* clear page-table */
-		page[i]=0;
-	retval = namei(filename,&inode);	/* get executable inode */
+		bprm.page[i] = 0;
+	retval = open_namei(filename, 0, 0, &bprm.inode, NULL);
 	if (retval)
 		return retval;
-	argc = count(argv);
-	envc = count(envp);
+	bprm.filename = filename;
+	bprm.argc = count(argv);
+	bprm.envc = count(envp);
 	
 restart_interp:
-	if (!S_ISREG(inode->i_mode)) {	/* must be regular file */
+	if (!S_ISREG(bprm.inode->i_mode)) {	/* must be regular file */
 		retval = -EACCES;
 		goto exec_error2;
 	}
-	if (IS_NOEXEC(inode)) {		/* FS mustn't be mounted noexec */
+	if (IS_NOEXEC(bprm.inode)) {		/* FS mustn't be mounted noexec */
 		retval = -EPERM;
 		goto exec_error2;
 	}
-	if (!inode->i_sb) {
+	if (!bprm.inode->i_sb) {
 		retval = -EACCES;
 		goto exec_error2;
 	}
-	i = inode->i_mode;
-	if (IS_NOSUID(inode) && (((i & S_ISUID) && inode->i_uid != current->
-	    euid) || ((i & S_ISGID) && !in_group_p(inode->i_gid))) &&
+	i = bprm.inode->i_mode;
+	if (IS_NOSUID(bprm.inode) && (((i & S_ISUID) && bprm.inode->i_uid != current->
+	    euid) || ((i & S_ISGID) && !in_group_p(bprm.inode->i_gid))) &&
 	    !suser()) {
 		retval = -EPERM;
 		goto exec_error2;
 	}
 	/* make sure we don't let suid, sgid files be ptraced. */
 	if (current->flags & PF_PTRACED) {
-		e_uid = current->euid;
-		e_gid = current->egid;
+		bprm.e_uid = current->euid;
+		bprm.e_gid = current->egid;
 	} else {
-		e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;
-		e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;
+		bprm.e_uid = (i & S_ISUID) ? bprm.inode->i_uid : current->euid;
+		bprm.e_gid = (i & S_ISGID) ? bprm.inode->i_gid : current->egid;
 	}
-	if (current->euid == inode->i_uid)
+	if (current->euid == bprm.inode->i_uid)
 		i >>= 6;
-	else if (in_group_p(inode->i_gid))
+	else if (in_group_p(bprm.inode->i_gid))
 		i >>= 3;
 	if (!(i & 1) &&
-	    !((inode->i_mode & 0111) && suser())) {
+	    !((bprm.inode->i_mode & 0111) && suser())) {
 		retval = -EACCES;
 		goto exec_error2;
 	}
-	memset(buf,0,sizeof(buf));
+	memset(bprm.buf,0,sizeof(bprm.buf));
 	old_fs = get_fs();
 	set_fs(get_ds());
-	retval = read_exec(inode,0,buf,128);
+	retval = read_exec(bprm.inode,0,bprm.buf,128);
 	set_fs(old_fs);
 	if (retval < 0)
 		goto exec_error2;
-	ex = *((struct exec *) buf);		/* exec-header */
-	if ((buf[0] == '#') && (buf[1] == '!') && (!sh_bang)) {
+	if ((bprm.buf[0] == '#') && (bprm.buf[1] == '!') && (!sh_bang)) {
 		/*
 		 * This section does the #! interpretation.
 		 * Sorta complicated, but hopefully it will work.  -TYT
@@ -547,19 +575,19 @@
 
 		char *cp, *interp, *i_name, *i_arg;
 
-		iput(inode);
-		buf[127] = '\0';
-		if ((cp = strchr(buf, '\n')) == NULL)
-			cp = buf+127;
+		iput(bprm.inode);
+		bprm.buf[127] = '\0';
+		if ((cp = strchr(bprm.buf, '\n')) == NULL)
+			cp = bprm.buf+127;
 		*cp = '\0';
-		while (cp > buf) {
+		while (cp > bprm.buf) {
 			cp--;
 			if ((*cp == ' ') || (*cp == '\t'))
 				*cp = '\0';
 			else
 				break;
 		}
-		for (cp = buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
+		for (cp = bprm.buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
 		if (!cp || *cp == '\0') {
 			retval = -ENOEXEC; /* No interpreter name found */
 			goto exec_error1;
@@ -579,8 +607,8 @@
 		 * (optional) argument.
 		 */
 		if (sh_bang++ == 0) {
-			p = copy_strings(envc, envp, page, p, 0);
-			p = copy_strings(--argc, argv+1, page, p, 0);
+			bprm.p = copy_strings(bprm.envc, envp, bprm.page, bprm.p, 0);
+			bprm.p = copy_strings(--bprm.argc, argv+1, bprm.page, bprm.p, 0);
 		}
 		/*
 		 * Splice in (1) the interpreter's name for argv[0]
@@ -590,15 +618,15 @@
 		 * This is done in reverse order, because of how the
 		 * user environment and arguments are stored.
 		 */
-		p = copy_strings(1, &filename, page, p, 1);
-		argc++;
+		bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2);
+		bprm.argc++;
 		if (i_arg) {
-			p = copy_strings(1, &i_arg, page, p, 2);
-			argc++;
+			bprm.p = copy_strings(1, &i_arg, bprm.page, bprm.p, 2);
+			bprm.argc++;
 		}
-		p = copy_strings(1, &i_name, page, p, 2);
-		argc++;
-		if (!p) {
+		bprm.p = copy_strings(1, &i_name, bprm.page, bprm.p, 2);
+		bprm.argc++;
+		if (!bprm.p) {
 			retval = -E2BIG;
 			goto exec_error1;
 		}
@@ -607,71 +635,198 @@
 		 * Note that we use open_namei() as the name is now in kernel
 		 * space, and we don't need to copy it.
 		 */
-		retval = open_namei(interp,0,0,&inode,NULL);
+		retval = open_namei(interp, 0, 0, &bprm.inode, NULL);
 		if (retval)
 			goto exec_error1;
 		goto restart_interp;
 	}
-	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC) ||
-		ex.a_trsize || ex.a_drsize ||
-		inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
-		retval = -ENOEXEC;
-		goto exec_error2;
-	}
-	if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
-	    (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
-		printk("N_TXTOFF < BLOCK_SIZE. Please convert binary.");
-		retval = -ENOEXEC;
-		goto exec_error2;
-	}
-	if (N_TXTOFF(ex) != BLOCK_SIZE && N_MAGIC(ex) != OMAGIC) {
-		printk("N_TXTOFF != BLOCK_SIZE. See a.out.h.");
-		retval = -ENOEXEC;
-		goto exec_error2;
-	}
 	if (!sh_bang) {
-		p = copy_strings(envc,envp,page,p,0);
-		p = copy_strings(argc,argv,page,p,0);
-		if (!p) {
+		bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0);
+		bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p,0);
+		if (!bprm.p) {
 			retval = -E2BIG;
 			goto exec_error2;
 		}
 	}
-/* OK, This is the point of no return */
-	flush_old_exec(inode, filename, e_uid, e_gid);
-	p += change_ldt(ex.a_text,page);
-	p -= MAX_ARG_PAGES*PAGE_SIZE;
-	p = (unsigned long) create_tables((char *)p,argc,envc);
-	current->brk = ex.a_bss +
-		(current->end_data = ex.a_data +
-		(current->end_code = ex.a_text));
-	current->start_stack = p;
-	current->rss = (TASK_SIZE - p + PAGE_SIZE-1) / PAGE_SIZE;
-	current->suid = current->euid = e_uid;
-	current->mmap = NULL;
-	current->sgid = current->egid = e_gid;
-	if (N_MAGIC(ex) == OMAGIC) {
-		read_exec(inode, 32, (char *) 0, ex.a_text+ex.a_data);
-		iput(inode);
-	} else if (!inode->i_op || !inode->i_op->bmap) {
-		read_exec(inode, 1024, (char *) 0, ex.a_text+ex.a_data);
-		iput(inode);
-	} else {
-		if (ex.a_text & 0xfff || ex.a_data & 0xfff)
-			printk("%s: executable not page aligned\n", current->comm);
-		current->executable = inode;
-	}
-	zeromap_page_range((ex.a_text + ex.a_data + 0xfff) & 0xfffff000,ex.a_bss, PAGE_COPY);
-	regs.eip = ex.a_entry;		/* eip, magic happens :-) */
-	regs.esp = p;			/* stack pointer */
-	if (current->flags & PF_PTRACED)
-		send_sig(SIGTRAP, current, 0);
-	return 0;
+
+	fmt = formats;
+	do {
+		int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
+		if (!fn)
+			break;
+		retval = fn(&bprm, regs);
+		if (retval == 0) {
+			iput(bprm.inode);
+			return 0;
+		}
+		fmt++;
+	} while (retval == -ENOEXEC);
 exec_error2:
-	iput(inode);
+	iput(bprm.inode);
 exec_error1:
 	for (i=0 ; i<MAX_ARG_PAGES ; i++)
-		free_page(page[i]);
+		free_page(bprm.page[i]);
 	return(retval);
 }
 
+/*
+ * sys_execve() executes a new program.
+ */
+extern "C" 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, &regs);
+	putname(filename);
+	return error;
+}
+
+/*
+ * These are  the prototypes for the  functions in the  dispatch table, as
+ * well as the  dispatch  table itself.
+ */
+
+extern int load_aout_binary(struct linux_binprm *,
+			    struct pt_regs * regs);
+extern int load_aout_library(int fd);
+
+/* Here are the actual binaries that will be accepted  */
+struct linux_binfmt formats[] = {
+	{load_aout_binary, load_aout_library},
+	{NULL, NULL}
+};
+
+/*
+ * These are the functions used to load a.out style executables and shared
+ * libraries.  There is no binary dependent code anywhere else.
+ */
+
+int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+	struct exec ex;
+	struct file * file;
+	int fd, error;
+	unsigned long p = bprm->p;
+
+	ex = *((struct exec *) bprm->buf);		/* exec-header */
+	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC) ||
+	    ex.a_trsize || ex.a_drsize ||
+	    bprm->inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
+		return -ENOEXEC;
+	}
+	if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
+	    (N_TXTOFF(ex) < bprm->inode->i_sb->s_blocksize)) {
+		printk("N_TXTOFF < BLOCK_SIZE. Please convert binary.");
+		return -ENOEXEC;
+	}
+	if (N_TXTOFF(ex) != BLOCK_SIZE && N_MAGIC(ex) != OMAGIC) {
+		printk("N_TXTOFF != BLOCK_SIZE. See a.out.h.");
+		return -ENOEXEC;
+	}
+	
+	/* OK, This is the point of no return */
+	flush_old_exec(bprm);
+	current->start_brk = current->brk = ex.a_bss +
+		(current->end_data = ex.a_data +
+		 (current->end_code = ex.a_text));
+	current->rss = 0;
+	current->suid = current->euid = bprm->e_uid;
+	current->mmap = NULL;
+	current->executable = NULL;  /* for OMAGIC files */
+	current->sgid = current->egid = bprm->e_gid;
+	if (N_MAGIC(ex) == OMAGIC) {
+		read_exec(bprm->inode, 32, (char *) 0, ex.a_text+ex.a_data);
+	} else {
+		if (ex.a_text & 0xfff || ex.a_data & 0xfff)
+			printk("%s: executable not page aligned\n", current->comm);
+		
+		fd = open_inode(bprm->inode, O_RDONLY);
+		
+		if (fd < 0)
+			return fd;
+		file = current->filp[fd];
+		if (!file->f_op || !file->f_op->mmap) {
+			sys_close(fd);
+			read_exec(bprm->inode, 1024, (char *) 0, ex.a_text+ex.a_data);
+			goto beyond_if;
+		}
+		error = do_mmap(file, 0, ex.a_text,
+				PROT_READ | PROT_EXEC,
+				MAP_FIXED | MAP_SHARED, N_TXTOFF(ex));
+		if (error != 0) {
+			sys_close(fd);
+			send_sig(SIGSEGV, current, 0);
+			return 0;
+		};
+		
+		error = do_mmap(file, ex.a_text, ex.a_data,
+				PROT_READ | PROT_WRITE | PROT_EXEC,
+				MAP_FIXED | MAP_PRIVATE, N_TXTOFF(ex) + ex.a_text);
+		sys_close(fd);
+		if (error != ex.a_text) {
+			send_sig(SIGSEGV, current, 0);
+			return 0;
+		};
+		current->executable = bprm->inode;
+		bprm->inode->i_count++;
+	}
+beyond_if:
+	zeromap_page_range((ex.a_text + ex.a_data + 0xfff) & 0xfffff000,ex.a_bss, PAGE_COPY);
+	p += change_ldt(ex.a_text,bprm->page);
+	p -= MAX_ARG_PAGES*PAGE_SIZE;
+	p = (unsigned long) create_tables((char *)p,bprm->argc,bprm->envc);
+	current->start_stack = p;
+	regs->eip = ex.a_entry;		/* eip, magic happens :-) */
+	regs->esp = p;			/* stack pointer */
+	if (current->flags & PF_PTRACED)
+		send_sig(SIGTRAP, current, 0);
+	return 0;
+}
+
+
+int load_aout_library(int fd)
+{
+        struct file * file;
+	struct exec ex;
+	struct  inode * inode;
+	unsigned int len;
+	unsigned int bss;
+	int error;
+	
+	file = current->filp[fd];
+	inode = file->f_inode;
+	
+	set_fs(KERNEL_DS);
+	if (file->f_op->read(inode, file, (char *) &ex, sizeof(ex)) != sizeof(ex)) {
+		return -EACCES;
+	}
+	set_fs(USER_DS);
+	
+	/* We come in here for the regular a.out style of shared libraries */
+	if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize ||
+	    ex.a_drsize || ex.a_entry & 0xfff ||
+	    inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
+		return -ENOEXEC;
+	}
+	if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && 
+	    (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
+		printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
+		return -ENOEXEC;
+	}
+	
+	/* Now use mmap to map the library into memory. */
+	error = do_mmap(file, ex.a_entry, ex.a_text + ex.a_data,
+			PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE,
+			N_TXTOFF(ex));
+	if (error != ex.a_entry)
+		return error;
+	len = (ex.a_text + ex.a_data + 0xfff) & 0xfffff000;
+	bss = ex.a_text + ex.a_data + ex.a_bss;
+	if (bss > len)
+		zeromap_page_range(ex.a_entry + len, bss-len, PAGE_COPY);
+	return 0;
+}
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
index adf834a..ae4d89b 100644
--- a/fs/ext2/acl.c
+++ b/fs/ext2/acl.c
@@ -12,6 +12,7 @@
 #include <linux/sched.h>
 #include <linux/ext2_fs.h>
 #include <linux/errno.h>
+#include <linux/stat.h>
 
 /*
  * ext2_permission ()
@@ -30,7 +31,7 @@
 		mode >>= 6;
 	else if (in_group_p (inode->i_gid))
 		mode >>= 3;
-	if (((mode & mask & 0007) == mask))
+	if (((mode & mask & S_IRWXO) == mask))
 		return 1;
 	else
 		return 0;
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index 7f8b1c8..be9b8b7 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -35,7 +35,7 @@
 		"rep\n\t" \
 		"stosl" \
 		: \
-		:"a" (0), "c" (size/4), "D" ((long) (addr)) \
+		:"a" (0), "c" (size / 4), "D" ((long) (addr)) \
 		:"cx", "di")
 
 static inline int find_first_zero_bit (unsigned long * addr, unsigned size)
@@ -58,7 +58,7 @@
 		shll $3,%%edi
 		addl %%edi,%%edx"
 		:"=d" (res)
-		:"c" ((size+31)>>5), "D" (addr), "b" (addr)
+		:"c" ((size + 31) >> 5), "D" (addr), "b" (addr)
 		:"ax", "bx", "cx", "di");
 	return res;
 }
@@ -239,13 +239,12 @@
 		return;
 	}
 	lock_super (sb);
-	if (block < sb->u.ext2_sb.s_first_data_block ||
-	    block >= sb->u.ext2_sb.s_blocks_count) {
+	es = sb->u.ext2_sb.s_es;
+	if (block < es->s_first_data_block || block >= es->s_blocks_count) {
 		printk ("ext2_free_block: block not in datazone\n");
 		unlock_super (sb);
 		return;
 	}
-	es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
 #ifdef EXT2FS_DEBUG
 	printk ("ext2_free_block: freeing block %d\n", block);
 #endif
@@ -253,10 +252,9 @@
 	if (bh)
 		bh->b_dirt = 0;
 	brelse (bh);
-	block_group = (block - sb->u.ext2_sb.s_first_data_block) /
+	block_group = (block - es->s_first_data_block) /
 		      EXT2_BLOCKS_PER_GROUP(sb);
-	bit = (block - sb->u.ext2_sb.s_first_data_block) %
-		EXT2_BLOCKS_PER_GROUP(sb);
+	bit = (block - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb);
 	bitmap_nr = load_block_bitmap (sb, block_group);
 	bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 	if (!bh) {
@@ -275,11 +273,11 @@
 			panic ("ext2_free_block: Group descriptor not loaded");
 		}
 		gdp = (struct ext2_group_desc *) bh2->b_data;
-		gdp[desc].bg_free_blocks_count ++;
+		gdp[desc].bg_free_blocks_count++;
 		bh2->b_dirt = 1;
 	}
 	bh->b_dirt = 1;
-	es->s_free_blocks_count ++;
+	es->s_free_blocks_count++;
 	sb->u.ext2_sb.s_sbh->b_dirt = 1;
 	sb->s_dirt = 1;
 	unlock_super (sb);
@@ -313,7 +311,7 @@
 		return 0;
 	}
 	lock_super (sb);
-	es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+	es = sb->u.ext2_sb.s_es;
 	if (es->s_free_blocks_count <= es->s_r_blocks_count && !suser()) {
 		unlock_super (sb);
 		return 0;
@@ -325,8 +323,7 @@
 	
 repeat:
 	/* First, test whether the goal block is free. */
-	i = ((goal - sb->u.ext2_sb.s_first_data_block) /
-	     EXT2_BLOCKS_PER_GROUP(sb));
+	i = ((goal - es->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(sb));
 	group_desc = i / EXT2_DESC_PER_BLOCK(sb);
 	desc = i % EXT2_DESC_PER_BLOCK(sb);
 	gdp = (struct ext2_group_desc *) 
@@ -335,7 +332,7 @@
 		panic ("ext2_new_block: Descriptor not loaded");
 	}
 	if (gdp[desc].bg_free_blocks_count > 0) {
-		j = ((goal - sb->u.ext2_sb.s_first_data_block) %
+		j = ((goal - es->s_first_data_block) %
 		       EXT2_BLOCKS_PER_GROUP(sb));
 #ifdef EXT2FS_DEBUG
 		if (j)
@@ -475,8 +472,8 @@
 #ifdef EXT2FS_DEBUG
 	printk ("ext2_new_block: found bit %d\n", j);
 #endif
-	j += i * EXT2_BLOCKS_PER_GROUP(sb) + sb->u.ext2_sb.s_first_data_block;
-	if (j >= sb->u.ext2_sb.s_blocks_count) {
+	j += i * EXT2_BLOCKS_PER_GROUP(sb) + es->s_first_data_block;
+	if (j >= es->s_blocks_count) {
 		printk ("block_group = %d,block=%d\n", i, j);
 		printk ("ext2_new_block: block >= blocks count");
 		unlock_super (sb);
@@ -495,9 +492,9 @@
 	printk("ext2_new_block: allocating block %d. "
 	       "Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
 #endif
-	gdp[desc].bg_free_blocks_count --;
+	gdp[desc].bg_free_blocks_count--;
 	sb->u.ext2_sb.s_group_desc[group_desc]->b_dirt = 1;
-	es->s_free_blocks_count --;
+	es->s_free_blocks_count--;
 	sb->u.ext2_sb.s_sbh->b_dirt = 1;
 	sb->s_dirt = 1;
 	unlock_super (sb);
@@ -506,8 +503,8 @@
 
 unsigned long ext2_count_free_blocks (struct super_block *sb)
 {
-	struct ext2_super_block * es;
 #ifdef EXT2FS_DEBUG
+	struct ext2_super_block * es;
 	unsigned long desc_count, bitmap_count, x;
 	unsigned long group_desc;
 	unsigned long desc;
@@ -516,7 +513,7 @@
 	int i;
 	
 	lock_super (sb);
-	es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+	es = sb->u.ext2_sb.s_es;
 	desc_count = 0;
 	bitmap_count = 0;
 	group_desc = 0;
@@ -545,9 +542,9 @@
 		printk ("group %d: stored = %d, counted = %d\n",
 			i, gdp[desc].bg_free_blocks_count, x);
 		bitmap_count += x;
-		desc ++;
+		desc++;
 		if (desc == EXT2_DESC_PER_BLOCK(sb)) {
-			group_desc ++;
+			group_desc++;
 			desc = 0;
 			gdp = NULL;
 		}
@@ -557,7 +554,6 @@
 	unlock_super (sb);
 	return bitmap_count;
 #else
-	es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
-	return es->s_free_blocks_count;
+	return sb->u.ext2_sb.s_es->s_free_blocks_count;
 #endif
 }
diff --git a/fs/ext2/dcache.c b/fs/ext2/dcache.c
index a74be03..4d61691 100644
--- a/fs/ext2/dcache.c
+++ b/fs/ext2/dcache.c
@@ -25,15 +25,15 @@
 	unsigned long ino;
 	char name[EXT2_NAME_LEN];
 	int len;
-	struct dir_cache_entry *queue_prev;
-	struct dir_cache_entry *queue_next;
-	struct dir_cache_entry *prev;
-	struct dir_cache_entry *next;
+	struct dir_cache_entry * queue_prev;
+	struct dir_cache_entry * queue_next;
+	struct dir_cache_entry * prev;
+	struct dir_cache_entry * next;
 };
 
-static struct dir_cache_entry *first = NULL;
-static struct dir_cache_entry *last = NULL;
-static struct dir_cache_entry *first_free = NULL;
+static struct dir_cache_entry * first = NULL;
+static struct dir_cache_entry * last = NULL;
+static struct dir_cache_entry * first_free = NULL;
 static int cache_initialized = 0;
 #ifdef EXT2FS_DEBUG_CACHE
 static int hits = 0;
@@ -46,8 +46,8 @@
 
 #define HASH_QUEUES 16
 
-static struct dir_cache_entry *queue_head[HASH_QUEUES];
-static struct dir_cache_entry *queue_tail[HASH_QUEUES];
+static struct dir_cache_entry * queue_head[HASH_QUEUES];
+static struct dir_cache_entry * queue_tail[HASH_QUEUES];
 
 #define hash(dev,dir)	((dev ^ dir) % HASH_QUEUES)
 
@@ -78,14 +78,15 @@
 /*
  * Find a name in the cache
  */
-static struct dir_cache_entry *find_name (int queue, unsigned short dev,
-					  unsigned long dir, const char *name,
-					  int len)
+static struct dir_cache_entry * find_name (int queue, unsigned short dev,
+					   unsigned long dir,
+					   const char * name, int len)
 {
-	struct dir_cache_entry *p;
+	struct dir_cache_entry * p;
 
-	for (p = queue_head[queue]; p!= NULL && (p->dev != dev ||
-	     p->dir != dir || p->len != len || strncmp (name, p->name, p->len));
+	for (p = queue_head[queue]; p != NULL && (p->dev != dev ||
+	     p->dir != dir || p->len != len ||
+	     strncmp (name, p->name, p->len) != 0);
 	     p = p->queue_next)
 		;
 	return p;
@@ -95,9 +96,9 @@
 /*
  * List the cache entries for debugging
  */
-static void show_cache (const char *func_name)
+static void show_cache (const char * func_name)
 {
-	struct dir_cache_entry *p;
+	struct dir_cache_entry * p;
 
 	printk ("%s: cache status\n", func_name);
 	for (p = first; p != NULL; p = p->next)
@@ -109,7 +110,7 @@
 /*
  * Add an entry at the beginning of the cache
  */
-static void add_to_cache (struct dir_cache_entry *p)
+static void add_to_cache (struct dir_cache_entry * p)
 {
 	p->prev = NULL;
 	p->next = first;
@@ -123,7 +124,7 @@
 /*
  * Add an entry at the beginning of a queue
  */
-static void add_to_queue (int queue, struct dir_cache_entry *p)
+static void add_to_queue (int queue, struct dir_cache_entry * p)
 {
 	p->queue_prev = NULL;
 	p->queue_next = queue_head[queue];
@@ -137,7 +138,7 @@
 /*
  * Remove an entry from the cache
  */
-static void remove_from_cache (struct dir_cache_entry *p)
+static void remove_from_cache (struct dir_cache_entry * p)
 {
 	if (p->prev)
 		p->prev->next = p->next;
@@ -152,7 +153,7 @@
 /*
  * Remove an entry from a queue
  */
-static void remove_from_queue (int queue, struct dir_cache_entry *p)
+static void remove_from_queue (int queue, struct dir_cache_entry * p)
 {
 	if (p->queue_prev)
 		p->queue_prev->queue_next = p->queue_next;
@@ -170,8 +171,8 @@
  */
 void ext2_dcache_invalidate (unsigned short dev)
 {
-	struct dir_cache_entry *p;
-	struct dir_cache_entry *p2;
+	struct dir_cache_entry * p;
+	struct dir_cache_entry * p2;
 
 	if (!cache_initialized)
 		init_cache ();
@@ -193,11 +194,11 @@
  * Lookup a directory entry in the cache
  */
 unsigned long ext2_dcache_lookup (unsigned short dev, unsigned long dir,
-				  const char *name, int len)
+				  const char * name, int len)
 {
 	char our_name[EXT2_NAME_LEN];
 	int queue;
-	struct dir_cache_entry *p;
+	struct dir_cache_entry * p;
 
 	if (!cache_initialized)
 		init_cache ();
@@ -208,7 +209,7 @@
 #ifdef EXT2FS_DEBUG_CACHE
 	printk ("dcache_lookup (%04x, %d, %s, %d)\n", dev, dir, our_name, len);
 #endif
-	queue = hash(dev, dir);
+	queue = hash (dev, dir);
 	if ((p = find_name (queue, dev, dir, our_name, len))) {
 		if (p != first) {
 			remove_from_cache (p);
@@ -219,7 +220,7 @@
 			add_to_queue (queue, p);
 		}
 #ifdef EXT2FS_DEBUG_CACHE
-		hits ++;
+		hits++;
 		printk ("dcache_lookup: %s,hit,inode=%d,hits=%d,misses=%d\n",
 			our_name, p->ino, hits, misses);
 		show_cache ("dcache_lookup");
@@ -227,7 +228,7 @@
 		return p->ino;
 	} else {
 #ifdef EXT2FS_DEBUG_CACHE
-		misses ++;
+		misses++;
 		printk ("dcache_lookup: %s,miss,hits=%d,misses=%d\n",
 			our_name, hits, misses);
 		show_cache ("dcache_lookup");
@@ -242,10 +243,10 @@
  * This function is called by ext2_lookup(), ext2_readdir()
  * and the functions which create directory entries
  */
-void ext2_dcache_add (unsigned short dev, unsigned long dir, const char *name,
+void ext2_dcache_add (unsigned short dev, unsigned long dir, const char * name,
 		      int len, int ino)
 {
-	struct dir_cache_entry *p;
+	struct dir_cache_entry * p;
 	int queue;
 
 	if (!cache_initialized)
@@ -256,7 +257,7 @@
 #endif
 	if (len > EXT2_NAME_LEN)
 		len = EXT2_NAME_LEN;
-	queue = hash(dev, dir);
+	queue = hash (dev, dir);
 	if ((p = find_name (queue, dev, dir, name, len))) {
 		p->dir = dir;
 		p->ino = ino;
@@ -303,9 +304,9 @@
  * This function is called by the functions which remove directory entries
  */
 void ext2_dcache_remove (unsigned short dev, unsigned long dir,
-			 const char *name, int len)
+			 const char * name, int len)
 {
-	struct dir_cache_entry *p;
+	struct dir_cache_entry * p;
 	int queue;
 
 	if (!cache_initialized)
@@ -315,7 +316,7 @@
 #endif
 	if (len > EXT2_NAME_LEN)
 		len = EXT2_NAME_LEN;
-	queue = hash(dev, dir);
+	queue = hash (dev, dir);
 	if ((p = find_name (queue, dev, dir, name, len))) {
 		remove_from_cache (p);
 		remove_from_queue (queue, p);
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index a828ace..4634b7e 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -104,7 +104,8 @@
 	sb = inode->i_sb;
 	while (filp->f_pos < inode->i_size) {
 		offset = filp->f_pos & (sb->s_blocksize - 1);
-		bh = ext2_bread (inode, (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb), 0, &err);
+		bh = ext2_bread (inode, (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb),
+				 0, &err);
 		if (!bh) {
 			filp->f_pos += sb->s_blocksize - offset;
 			continue;
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index a8a7f3e..ee7b273 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -44,7 +44,7 @@
 	ext2_file_write,	/* write */
 	NULL,			/* readdir - bad */
 	NULL,			/* select - default */
-	ext2_ioctl,		/* ioctl - default */
+	ext2_ioctl,		/* ioctl */
 	generic_mmap,  		/* mmap */
 	NULL,			/* no special open is needed */
 	NULL,			/* release */
@@ -228,9 +228,6 @@
 	while (written < count) {
 		bh = ext2_getblk (inode, pos / sb->s_blocksize, 1, &err);
 		if (!bh) {
-#ifdef EXT2FS_DEBUG
-			printk ("ext2_file_write: ext2_getblk returned NULL\n");
-#endif
 			if (!written)
 				written = err;
 			break;
diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c
index 3c85c4e..08513c3 100644
--- a/fs/ext2/fsync.c
+++ b/fs/ext2/fsync.c
@@ -35,7 +35,7 @@
 	if (!*block)
 		return 0;
 	tmp = *block;
-	bh = get_hash_table(inode->i_dev, *block, blocksize);
+	bh = get_hash_table (inode->i_dev, *block, blocksize);
 	if (!bh)
 		return 0;
 	if (*block != tmp) {
@@ -43,21 +43,20 @@
 		return 1;
 	}
 	if (wait && bh->b_req && !bh->b_uptodate) {
-		brelse(bh);
+		brelse (bh);
 		return -1;
 	}
-	if (wait || !bh->b_uptodate || !bh->b_dirt)
-	{
-		brelse(bh);
+	if (wait || !bh->b_uptodate || !bh->b_dirt) {
+		brelse (bh);
 		return 0;
 	}
-	ll_rw_block(WRITE, 1, &bh);
+	ll_rw_block (WRITE, 1, &bh);
 	bh->b_count--;
 	return 0;
 }
 
 static int sync_iblock (struct inode * inode, unsigned long * iblock, 
-			struct buffer_head **bh, int wait) 
+			struct buffer_head ** bh, int wait) 
 {
 	int rc, tmp;
 	
@@ -68,9 +67,9 @@
 	rc = sync_block (inode, iblock, wait);
 	if (rc)
 		return rc;
-	*bh = bread(inode->i_dev, tmp, blocksize);
+	*bh = bread (inode->i_dev, tmp, blocksize);
 	if (tmp != *iblock) {
-		brelse(*bh);
+		brelse (*bh);
 		*bh = NULL;
 		return 1;
 	}
@@ -80,7 +79,7 @@
 }
 
 
-static int sync_direct(struct inode *inode, int wait)
+static int sync_direct (struct inode * inode, int wait)
 {
 	int i;
 	int rc, err = 0;
@@ -95,7 +94,8 @@
 	return err;
 }
 
-static int sync_indirect(struct inode *inode, unsigned long *iblock, int wait)
+static int sync_indirect (struct inode * inode, unsigned long * iblock,
+			  int wait)
 {
 	int i;
 	struct buffer_head * ind_bh;
@@ -114,12 +114,12 @@
 		if (rc)
 			err = rc;
 	}
-	brelse(ind_bh);
+	brelse (ind_bh);
 	return err;
 }
 
-static int sync_dindirect(struct inode *inode, unsigned long *diblock,
-			  int wait)
+static int sync_dindirect (struct inode * inode, unsigned long * diblock,
+			   int wait)
 {
 	int i;
 	struct buffer_head * dind_bh;
@@ -138,12 +138,12 @@
 		if (rc)
 			err = rc;
 	}
-	brelse(dind_bh);
+	brelse (dind_bh);
 	return err;
 }
 
-static int sync_tindirect(struct inode *inode, unsigned long *tiblock, 
-			  int wait)
+static int sync_tindirect (struct inode * inode, unsigned long * tiblock, 
+			   int wait)
 {
 	int i;
 	struct buffer_head * tind_bh;
@@ -162,11 +162,11 @@
 		if (rc)
 			err = rc;
 	}
-	brelse(tind_bh);
+	brelse (tind_bh);
 	return err;
 }
 
-int ext2_sync_file(struct inode * inode, struct file *file)
+int ext2_sync_file (struct inode * inode, struct file * file)
 {
 	int wait, err = 0;
 
@@ -179,16 +179,16 @@
 
 	for (wait=0; wait<=1; wait++)
 	{
-		err |= sync_direct(inode, wait);
-		err |= sync_indirect(inode,
-				     inode->u.ext2_i.i_data+EXT2_IND_BLOCK,
-				     wait);
-		err |= sync_dindirect(inode,
-				      inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, 
+		err |= sync_direct (inode, wait);
+		err |= sync_indirect (inode,
+				      inode->u.ext2_i.i_data+EXT2_IND_BLOCK,
 				      wait);
-		err |= sync_tindirect(inode, 
-				      inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, 
-				      wait);
+		err |= sync_dindirect (inode,
+				       inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, 
+				       wait);
+		err |= sync_tindirect (inode, 
+				       inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, 
+				       wait);
 	}
 skip:
 	err |= ext2_sync_inode (inode);
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
index 02f768e..de0046f 100644
--- a/fs/ext2/ialloc.c
+++ b/fs/ext2/ialloc.c
@@ -221,12 +221,13 @@
 #endif
 	sb = inode->i_sb;
 	lock_super (sb);
-	if (inode->i_ino < 1 || inode->i_ino > sb->u.ext2_sb.s_inodes_count) {
+	if (inode->i_ino < 1 ||
+	    inode->i_ino > sb->u.ext2_sb.s_es->s_inodes_count) {
 		printk("free_inode: inode 0 or nonexistent inode\n");
 		unlock_super (sb);
 		return;
 	}
-	es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+	es = sb->u.ext2_sb.s_es;
 	block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb);
 	bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb);
 	bitmap_nr = load_inode_bitmap (sb, block_group);
@@ -247,14 +248,14 @@
 			panic ("ext2_free_inode: Group descriptor not loaded");
 		}
 		gdp = (struct ext2_group_desc *) bh2->b_data;
-		gdp[desc].bg_free_inodes_count ++;
+		gdp[desc].bg_free_inodes_count++;
 		if (S_ISDIR(inode->i_mode))
-			gdp[desc].bg_used_dirs_count --;
+			gdp[desc].bg_used_dirs_count--;
 		bh2->b_dirt = 1;
 		set_inode_dtime (inode, gdp, desc);
 	}
 	bh->b_dirt = 1;
-	es->s_free_inodes_count ++;
+	es->s_free_inodes_count++;
 	sb->u.ext2_sb.s_sbh->b_dirt = 1;
 	sb->s_dirt = 1;
 	unlock_super (sb);
@@ -295,10 +296,11 @@
 	brelse (bh);
 }
 
-static struct ext2_group_desc * get_group_desc(struct super_block * sb,
-					       int group)
+static struct ext2_group_desc * get_group_desc (struct super_block * sb,
+					        int group)
 {
 	struct ext2_group_desc * gdp;
+
 	if (group >= sb->u.ext2_sb.s_groups_count || group < 0 )
 		panic ("ext2: get_group_desc: Invalid group\n");
 	if (!sb->u.ext2_sb.s_group_desc[group / EXT2_DESC_PER_BLOCK(sb)])
@@ -335,7 +337,7 @@
 	inode->i_sb = sb;
 	inode->i_flags = sb->s_flags;
 	lock_super (sb);
-	es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+	es = sb->u.ext2_sb.s_es;
 repeat:
 	gdp = NULL; i=0;
 	
@@ -345,7 +347,7 @@
 /* I am not yet convinced that this next bit is necessary.
 		i = dir->u.ext2_i.i_block_group;
 		for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
-			tmp = get_group_desc(sb, i);
+			tmp = get_group_desc (sb, i);
 			if ((tmp->bg_used_dirs_count << 8) < 
 			    tmp->bg_free_inodes_count) {
 				gdp = tmp;
@@ -357,7 +359,7 @@
 */
 		if (!gdp) {
 			for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
-				tmp = get_group_desc(sb, j);
+				tmp = get_group_desc (sb, j);
 				if (tmp->bg_free_inodes_count &&
 					tmp->bg_free_inodes_count >= avefreei) {
 					if (!gdp || 
@@ -373,7 +375,7 @@
 	else 
 	{ /* Try to place the inode in it\'s parent directory */
 		i = dir->u.ext2_i.i_block_group;
-		tmp = get_group_desc(sb, i);
+		tmp = get_group_desc (sb, i);
 		if (tmp->bg_free_inodes_count)
 			gdp = tmp;
 		else
@@ -382,7 +384,7 @@
 				i += j;
 				if (i >= sb->u.ext2_sb.s_groups_count)
 					i -= sb->u.ext2_sb.s_groups_count;
-				tmp = get_group_desc(sb, i);
+				tmp = get_group_desc (sb, i);
 				if (tmp->bg_free_inodes_count) {
 					gdp = tmp;
 					break;
@@ -395,7 +397,7 @@
 			for (j = 2; j < sb->u.ext2_sb.s_groups_count; j++) {
 				if (++i >= sb->u.ext2_sb.s_groups_count)
 					i = 0;
-				tmp = get_group_desc(sb,i);
+				tmp = get_group_desc (sb,i);
 				if (tmp->bg_free_inodes_count) {
 					gdp = tmp;
 					break;
@@ -426,16 +428,18 @@
 	} else
 		goto repeat;
 	j += i * EXT2_INODES_PER_GROUP(sb) + 1;
-	if (j > sb->u.ext2_sb.s_inodes_count) {
+	if (j > es->s_inodes_count) {
 		printk ("block_group = %d,inode=%d\n", i, j);
 		printk ("ext2_new_inode: inode > inodes count");
+		unlock_super (sb);
+		iput (inode);
 		return NULL;
 	}
-	gdp->bg_free_inodes_count --;
+	gdp->bg_free_inodes_count--;
 	if (S_ISDIR(mode))
-		gdp->bg_used_dirs_count ++;
+		gdp->bg_used_dirs_count++;
 	sb->u.ext2_sb.s_group_desc[i / EXT2_DESC_PER_BLOCK(sb)]->b_dirt = 1;
-	es->s_free_inodes_count --;
+	es->s_free_inodes_count--;
 	sb->u.ext2_sb.s_sbh->b_dirt = 1;
 	sb->s_dirt = 1;
 	inode->i_mode = mode;
@@ -470,8 +474,8 @@
 
 unsigned long ext2_count_free_inodes (struct super_block *sb)
 {
-	struct ext2_super_block * es;
 #ifdef EXT2FS_DEBUG
+	struct ext2_super_block * es;
 	unsigned long desc_count, bitmap_count, x;
 	unsigned long group_desc;
 	unsigned long desc;
@@ -480,7 +484,7 @@
 	int i;
 
 	lock_super (sb);
-	es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+	es = sb->u.ext2_sb.s_es;
 	desc_count = 0;
 	bitmap_count = 0;
 	group_desc = 0;
@@ -507,9 +511,9 @@
 		printk ("group %d: stored = %d, counted = %d\n",
 			i, gdp[desc].bg_free_inodes_count, x);
 		bitmap_count += x;
-		desc ++;
+		desc++;
 		if (desc == EXT2_DESC_PER_BLOCK(sb)) {
-			group_desc ++;
+			group_desc++;
 			desc = 0;
 			gdp = NULL;
 		}
@@ -519,7 +523,6 @@
 	unlock_super (sb);
 	return desc_count;
 #else
-	es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
-	return es->s_free_inodes_count;
+	return sb->u.ext2_sb.s_es->s_free_inodes_count;
 #endif
 }
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 95bc2e6..78006de 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -37,13 +37,13 @@
 
 void ext2_put_super (struct super_block * sb)
 {
-	struct ext2_super_block * es;
 	int i;
 
 	lock_super (sb);
-	es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
-	es->s_valid = sb->u.ext2_sb.s_was_mounted_valid;
-	sb->u.ext2_sb.s_sbh->b_dirt = 1;
+	if ((sb->s_flags & MS_RDONLY) == 0) {
+		sb->u.ext2_sb.s_es->s_valid = sb->u.ext2_sb.s_was_mounted_valid;
+		sb->u.ext2_sb.s_sbh->b_dirt = 1;
+	}
 #ifndef DONT_USE_DCACHE
 	ext2_dcache_invalidate (sb->s_dev);
 #endif
@@ -57,6 +57,7 @@
 	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
 		if (sb->u.ext2_sb.s_block_bitmap[i])
 			brelse (sb->u.ext2_sb.s_block_bitmap[i]);
+	brelse (sb->u.ext2_sb.s_sbh);
 	unlock_super (sb);
 	return;
 }
@@ -127,7 +128,7 @@
 #endif
 
 	lock_super (s);
-	set_blocksize(dev, BLOCK_SIZE);
+	set_blocksize (dev, BLOCK_SIZE);
 	if (!(bh = bread (dev, 1, BLOCK_SIZE))) {
 		s->s_dev = 0;
 		unlock_super (s);
@@ -135,40 +136,59 @@
 		return NULL;
 	}
 	es = (struct ext2_super_block *) bh->b_data;
+	/* Note: s_es must be initialized s_es as soon as possible because
+	   some ext2 macro-instructions depend on its value */
+	s->u.ext2_sb.s_es = es;
 	s->s_magic = es->s_magic;
+	if (s->s_magic != EXT2_SUPER_MAGIC
+#ifdef EXT2FS_PRE_02B_COMPAT
+	   && s->s_magic != EXT2_OLD_SUPER_MAGIC
+#endif
+	   ) {
+		s->s_dev = 0;
+		unlock_super (s);
+		brelse (bh);
+		if (!silent)
+			printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n",
+				dev);
+		return NULL;
+	}
 	s->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size;
-	s->u.ext2_sb.s_log_block_size = es->s_log_block_size;
 	s->s_blocksize_bits = EXT2_BLOCK_SIZE_BITS(s);
 	if (s->s_blocksize != BLOCK_SIZE && 
 	    (s->s_blocksize == 1024 || s->s_blocksize == 2048 ||  
 	     s->s_blocksize == 4096)) {
-		brelse(bh);
-		set_blocksize(dev, s->s_blocksize);
-		bh = bread (dev, 0,  s->s_blocksize);
+		brelse (bh);
+		set_blocksize (dev, s->s_blocksize);
+		bh = bread (dev, 0, s->s_blocksize);
 		if(!bh)
 			return NULL;
-		es = (struct ext2_super_block *) (((char *)bh->b_data) + BLOCK_SIZE) ;
-	};
-	s->u.ext2_sb.s_inodes_count = es->s_inodes_count;
-	s->u.ext2_sb.s_blocks_count = es->s_blocks_count;
-	s->u.ext2_sb.s_r_blocks_count = es->s_r_blocks_count;
-	s->u.ext2_sb.s_first_data_block = es->s_first_data_block;
-	s->u.ext2_sb.s_log_frag_size = es->s_log_frag_size;
+		es = (struct ext2_super_block *) (((char *)bh->b_data) + BLOCK_SIZE);
+		s->u.ext2_sb.s_es = es;
+		if (es->s_magic != EXT2_SUPER_MAGIC) {
+			s->s_dev = 0;
+			unlock_super (s);
+			brelse (bh);
+			printk ("ext2-FS: Magic mismatch, very weird !\n");
+			return NULL;
+		}
+	}
 	s->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
-				     es->s_log_frag_size;
+				   es->s_log_frag_size;
 	if (s->u.ext2_sb.s_frag_size)
 		s->u.ext2_sb.s_frags_per_block = s->s_blocksize /
-					   s->u.ext2_sb.s_frag_size;
+						   s->u.ext2_sb.s_frag_size;
 	else
 		s->s_magic = 0;
 	s->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group;
 	s->u.ext2_sb.s_frags_per_group = es->s_frags_per_group;
 	s->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group;
 	s->u.ext2_sb.s_inodes_per_block = s->s_blocksize /
-					    sizeof (struct ext2_inode);
+					  sizeof (struct ext2_inode);
 	s->u.ext2_sb.s_desc_per_block = s->s_blocksize /
-					  sizeof (struct ext2_group_desc);
+					sizeof (struct ext2_group_desc);
 	s->u.ext2_sb.s_sbh = bh;
+	s->u.ext2_sb.s_es = es;
 	s->u.ext2_sb.s_was_mounted_valid = es->s_valid;
 	s->u.ext2_sb.s_rename_lock = 0;
 	s->u.ext2_sb.s_rename_wait = NULL;
@@ -208,8 +228,8 @@
 		unlock_super (s);
 		brelse (bh);
 		if (!silent)
-			printk("VFS: Can't find an ext2fs filesystem on dev 0x%04x.\n",
-				   dev);
+			printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n",
+				dev);
 		return NULL;
 	}
 	if (s->s_blocksize != bh->b_size) {
@@ -217,45 +237,48 @@
 		unlock_super (s);
 		brelse (bh);
 		if (!silent)
-			printk("VFS: Unsupported blocksize on dev 0x%04x.\n",
-				   dev);
+			printk ("VFS: Unsupported blocksize on dev 0x%04x.\n",
+				dev);
 		return NULL;
 	}
+
 	if (s->s_blocksize != s->u.ext2_sb.s_frag_size) {
 		s->s_dev = 0;
 		unlock_super (s);
 		brelse (bh);
-		printk ("EXT2-fs: fragsize != blocksize (not supported yet)\n");
+		printk ("EXT2-fs: fragsize %d != blocksize %d (not supported yet)\n",
+			s->u.ext2_sb.s_frag_size, s->s_blocksize);
 		return NULL;
 	}
+
 	if (!es->s_valid)
 		printk ("EXT2-fs warning: mounting unchecked file system, "
 			"running e2fsck is recommended\n");
-	s->u.ext2_sb.s_groups_count = (s->u.ext2_sb.s_blocks_count -
-				       s->u.ext2_sb.s_first_data_block +
+	s->u.ext2_sb.s_groups_count = (es->s_blocks_count -
+				       es->s_first_data_block +
 				       EXT2_BLOCKS_PER_GROUP(s) - 1) /
 				       EXT2_BLOCKS_PER_GROUP(s);
 	for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
 		s->u.ext2_sb.s_group_desc[i] = NULL;
-	bh_count = (s->u.ext2_sb.s_groups_count +
-		   EXT2_DESC_PER_BLOCK(s) - 1) /
+	bh_count = (s->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(s) - 1) /
 		   EXT2_DESC_PER_BLOCK(s);
 	if (bh_count >= EXT2_MAX_GROUP_DESC) {
 		s->s_dev = 0;
 		unlock_super (s);
 		brelse (bh);
-		printk ("EXT2-fs: Too big file system\n");
+		printk ("EXT2-fs: file system too big\n");
 		return NULL;
 	}
 	for (i = 0; i < bh_count; i++) {
-		s->u.ext2_sb.s_group_desc[i] = bread (dev, i + 2, s->s_blocksize);
+		s->u.ext2_sb.s_group_desc[i] = bread (dev, i + 2,
+						      s->s_blocksize);
 		if (!s->u.ext2_sb.s_group_desc[i]) {
 			s->s_dev = 0;
 			unlock_super (s);
 			for (j = 0; j < i; j++)
 				brelse (s->u.ext2_sb.s_group_desc[i]);
 			brelse (bh);
-			printk ("ext2_read_super: unable to read group descriptors\n");
+			printk ("ext2-FS: unable to read group descriptors\n");
 			return NULL;
 		}
 	}
@@ -271,7 +294,7 @@
 	/* set up enough so that it can read an inode */
 	s->s_dev = dev;
 	s->s_op = &ext2_sops;
-	if (!(s->s_mounted = iget(s, EXT2_ROOT_INO))) {
+	if (!(s->s_mounted = iget (s, EXT2_ROOT_INO))) {
 		s->s_dev = 0;
 		for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
 			if (s->u.ext2_sb.s_group_desc[i])
@@ -309,51 +332,13 @@
  * flags to 0.  We need to set this flag to 0 since the fs
  * may have been checked while mounted and e2fsck may have
  * set s_valid to 1 after some corrections.
- *
- * Note that this function also writes backups of the super block and
- * of the group descriptors in each group.
  */
 
-static void ext2_commit_super (struct super_block *sb,
-			       struct ext2_super_block *es)
+static void ext2_commit_super (struct super_block * sb,
+			       struct ext2_super_block * es)
 {
-	struct buffer_head * bh;
-	unsigned long block;
-	unsigned long bh_count;
-	int i, j;
-
 	es->s_wtime = CURRENT_TIME;
 	sb->u.ext2_sb.s_sbh->b_dirt = 1;
-	bh_count = (sb->u.ext2_sb.s_groups_count +
-		    EXT2_DESC_PER_BLOCK(sb) - 1) /
-		    EXT2_DESC_PER_BLOCK(sb);
-	for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
-		block = sb->u.ext2_sb.s_first_data_block +
-			i * sb->u.ext2_sb.s_blocks_per_group;
-		if (!(bh = bread (sb->s_dev, block, BLOCK_SIZE)))
-			printk ("ext2_commit_super: Unable to read backup super block for group %d\n", i);
-		else {
-#ifdef EXT2FS_DEBUG
-			printk ("ext2_commit_super: writing super block backup in group %d at block %d\n", i, block);
-#endif	
-			memcpy (bh->b_data, es, BLOCK_SIZE);
-			bh ->b_dirt = 1;
-			brelse (bh);
-		}
-		for (j = 0; j < bh_count; j++) {
-			block ++;
-#ifdef EXT2FS_DEBUG
-			printk ("ext2_commit_super: writing descriptors (block %d) backup in group %d at block %d\n", j, i, block);
-#endif	
-			if (!(bh = bread (sb->s_dev, block, sb->s_blocksize)))
-				printk ("ext2_commit_super: Unable to read backup descriptor for group %d\n", i);
-			else {
-				memcpy (bh->b_data, sb->u.ext2_sb.s_group_desc[j]->b_data, sb->s_blocksize);
-				bh ->b_dirt = 1;
-				brelse (bh);
-			}
-		}
-	}
 	sb->s_dirt = 0;
 }
 
@@ -362,7 +347,7 @@
 	struct ext2_super_block * es;
 
 	if ((sb->s_flags & MS_RDONLY) == 0) {
-		es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+		es = sb->u.ext2_sb.s_es;
 #ifdef EXT2FS_DEBUG
 		printk ("ext2_write_super: setting valid to 0\n");
 #endif
@@ -372,11 +357,11 @@
 	sb->s_dirt = 0;
 }
 
-int ext2_remount(struct super_block *sb, int *flags)
+int ext2_remount (struct super_block * sb, int * flags)
 {
 	struct ext2_super_block * es;
 
-	es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+	es = sb->u.ext2_sb.s_es;
 	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
 		return 0;
 	if (*flags & MS_RDONLY) {
@@ -387,13 +372,16 @@
 		   again. */
 		sb->s_flags |= MS_RDONLY;
 		es->s_valid = sb->u.ext2_sb.s_was_mounted_valid;
-		ext2_commit_super(sb, es);
+		ext2_commit_super (sb, es);
 	}
 	else {
 		/* Mounting a RDONLY partition read-write, so reread and
 		   store the current valid flag.  (It may have been changed 
-		   by ext2fs since we originally mounted the partition.)  */
+		   by e2fsck since we originally mounted the partition.)  */
 		sb->u.ext2_sb.s_was_mounted_valid = es->s_valid;
+		if (!es->s_valid)
+			printk ("EXT2-fs warning: remounting unchecked fs, "
+				"running e2fsck is recommended\n");
 	}
 	return 0;
 }
@@ -404,17 +392,16 @@
 
 	put_fs_long (EXT2_SUPER_MAGIC, &buf->f_type);
 	put_fs_long (sb->s_blocksize, &buf->f_bsize);
-	put_fs_long (sb->u.ext2_sb.s_blocks_count >> sb->u.ext2_sb.s_log_block_size,
-		&buf->f_blocks);
+	put_fs_long (sb->u.ext2_sb.s_es->s_blocks_count, &buf->f_blocks);
 	tmp = ext2_count_free_blocks (sb);
 	put_fs_long (tmp, &buf->f_bfree);
-	if (tmp >= sb->u.ext2_sb.s_r_blocks_count)
-		put_fs_long (tmp - sb->u.ext2_sb.s_r_blocks_count,
+	if (tmp >= sb->u.ext2_sb.s_es->s_r_blocks_count)
+		put_fs_long (tmp - sb->u.ext2_sb.s_es->s_r_blocks_count,
 			     &buf->f_bavail);
 	else
 		put_fs_long (0, &buf->f_bavail);
-	put_fs_long (sb->u.ext2_sb.s_inodes_count, &buf->f_files);
-	put_fs_long (ext2_count_free_inodes(sb), &buf->f_ffree);
+	put_fs_long (sb->u.ext2_sb.s_es->s_inodes_count, &buf->f_files);
+	put_fs_long (ext2_count_free_inodes (sb), &buf->f_ffree);
 	put_fs_long (EXT2_NAME_LEN, &buf->f_namelen);
 	/* Don't know what value to put in buf->f_fsid */
 }
@@ -517,7 +504,7 @@
 	printk ("ext2 inode_getblk: hint = %d,", goal);
 #endif
 	if (!goal) {
-		for (tmp = nr-1; tmp>=0; tmp--) {
+		for (tmp = nr - 1; tmp >= 0; tmp--) {
 			if (inode->u.ext2_i.i_data[tmp]) {
 				goal = inode->u.ext2_i.i_data[tmp];
 				break;
@@ -525,8 +512,8 @@
 		}
 		if (!goal)
 			goal = (inode->u.ext2_i.i_block_group * 
-				EXT2_BLOCKS_PER_GROUP(inode->i_sb))
-				+ inode->i_sb->u.ext2_sb.s_first_data_block;
+				EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
+			       inode->i_sb->u.ext2_sb.s_es->s_first_data_block;
 	}
 
 #ifdef EXT2FS_DEBUG
@@ -592,7 +579,7 @@
 	if (inode->u.ext2_i.i_next_alloc_block == new_block)
 		goal = inode->u.ext2_i.i_next_alloc_goal;
 	if (!goal) {
-		for (tmp = nr-1; tmp>=0; tmp--) {
+		for (tmp = nr - 1; tmp >= 0; tmp--) {
 			if (((unsigned long *) bh->b_data)[tmp]) {
 				goal = ((unsigned long *)bh->b_data)[tmp];
 				break;
@@ -714,8 +701,8 @@
 
 	if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
 	     inode->i_ino != EXT2_ACL_DATA_INO && inode->i_ino < EXT2_FIRST_INO) ||
-	    inode->i_ino > inode->i_sb->u.ext2_sb.s_inodes_count) {
-		printk ("ext2_read_inode: bad inode number of dev %0x04: %d\n",
+	    inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
+		printk ("ext2_read_inode: bad inode number on dev %0x04: %d\n",
 			inode->i_dev, inode->i_ino);
 		return;
 	}
@@ -790,7 +777,7 @@
 	struct ext2_group_desc * gdp;
 
 	if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino < EXT2_FIRST_INO) ||
-	    inode->i_ino > inode->i_sb->u.ext2_sb.s_inodes_count) {
+	    inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
 		printk ("ext2_write_inode: bad inode number of dev %0x04: %d\n",
 			inode->i_dev, inode->i_ino);
 		return 0;
@@ -840,7 +827,7 @@
 void ext2_write_inode (struct inode * inode)
 {
 	struct buffer_head * bh;
-	bh = ext2_update_inode(inode);
+	bh = ext2_update_inode (inode);
 	brelse (bh);
 }
 
@@ -849,11 +836,11 @@
 	int err = 0;
 	struct buffer_head *bh;
 
-	bh = ext2_update_inode(inode);
+	bh = ext2_update_inode (inode);
 	if (bh && bh->b_dirt)
 	{
-		ll_rw_block(WRITE, 1, &bh);
-		wait_on_buffer(bh);
+		ll_rw_block (WRITE, 1, &bh);
+		wait_on_buffer (bh);
 		if (bh->b_req && !bh->b_uptodate)
 		{
 			printk ("IO error syncing ext2 inode [%04x:%08x]\n",
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index b0edb54..49e06cc 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -27,10 +27,6 @@
 /* #define NO_TRUNCATE */
 	
 /*
- * ok, we cannot use strncmp, as the name is not in our data space.
- * Thus we'll have to use ext2_match. No big problem. ext2_match also makes
- * some sanity tests.
- *
  * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
  */
 static int ext2_match (int len, const char * const name,
@@ -261,6 +257,7 @@
 			for (i = 0; i < namelen ; i++)
 				de->name[i] = name [i];
 			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+			dir->i_dirt = 1;
 			bh->b_dirt = 1;
 			*res_dir = de;
 			*err = 0;
@@ -325,7 +322,7 @@
 	inode->i_dirt = 1;
 	bh = ext2_add_entry (dir, name, len, &de, &err);
 	if (!bh) {
-		inode->i_nlink --;
+		inode->i_nlink--;
 		inode->i_dirt = 1;
 		iput (inode);
 		iput (dir);
@@ -388,7 +385,7 @@
 	inode->i_dirt = 1;
 	bh = ext2_add_entry (dir, name, len, &de, &err);
 	if (!bh) {
-		inode->i_nlink --;
+		inode->i_nlink--;
 		inode->i_dirt = 1;
 		iput (inode);
 		iput (dir);
@@ -436,7 +433,7 @@
 	dir_block = ext2_bread (inode, 0, 1, &err);
 	if (!dir_block) {
 		iput (dir);
-		inode->i_nlink --;
+		inode->i_nlink--;
 		inode->i_dirt = 1;
 		iput (inode);
 		return err;
@@ -455,7 +452,7 @@
 	inode->i_nlink = 2;
 	dir_block->b_dirt = 1;
 	brelse (dir_block);
-	inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask);
+	inode->i_mode = S_IFDIR | (mode & S_IRWXUGO & ~current->umask);
 	if (dir->i_mode & S_ISGID)
 		inode->i_mode |= S_ISGID;
 	inode->i_dirt = 1;
@@ -472,7 +469,7 @@
 			 de->inode);
 #endif
 	bh->b_dirt = 1;
-	dir->i_nlink ++;
+	dir->i_nlink++;
 	dir->i_dirt = 1;
 	iput (dir);
 	iput (inode);
@@ -596,7 +593,7 @@
 	bh->b_dirt = 1;
 	inode->i_nlink = 0;
 	inode->i_dirt = 1;
-	dir->i_nlink --;
+	dir->i_nlink--;
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_dirt = 1;
 end_rmdir:
@@ -651,7 +648,7 @@
 	bh->b_dirt = 1;
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_dirt = 1;
-	inode->i_nlink --;
+	inode->i_nlink--;
 	inode->i_dirt = 1;
 	inode->i_ctime = CURRENT_TIME;
 	retval = 0;
@@ -677,7 +674,7 @@
 		iput (dir);
 		return -ENOSPC;
 	}
-	inode->i_mode = S_IFLNK | 0777;
+	inode->i_mode = S_IFLNK | S_IRWXUGO;
 	inode->i_op = &ext2_symlink_inode_operations;
 	for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
 	     symname [l]; l++)
@@ -689,7 +686,7 @@
 		name_block = ext2_bread (inode, 0, 1, &err);
 		if (!name_block) {
 			iput (dir);
-			inode->i_nlink --;
+			inode->i_nlink--;
 			inode->i_dirt = 1;
 			iput (inode);
 			return err;
@@ -702,7 +699,7 @@
 #endif
 	}
 	i = 0;
-	while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname ++)))
+	while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname++)))
 		link[i++] = c;
 	link[i] = 0;
 	if (name_block) {
@@ -713,7 +710,7 @@
 	inode->i_dirt = 1;
 	bh = ext2_find_entry (dir, name, len, &de);
 	if (bh) {
-		inode->i_nlink --;
+		inode->i_nlink--;
 		inode->i_dirt = 1;
 		iput (inode);
 		brelse (bh);
@@ -722,7 +719,7 @@
 	}
 	bh = ext2_add_entry (dir, name, len, &de, &err);
 	if (!bh) {
-		inode->i_nlink --;
+		inode->i_nlink--;
 		inode->i_dirt = 1;
 		iput (inode);
 		iput (dir);
@@ -778,7 +775,7 @@
 	bh->b_dirt = 1;
 	brelse (bh);
 	iput (dir);
-	oldinode->i_nlink ++;
+	oldinode->i_nlink++;
 	oldinode->i_ctime = CURRENT_TIME;
 	oldinode->i_dirt = 1;
 	iput (oldinode);
@@ -790,7 +787,7 @@
 	int ino;
 	int result;
 
-	new_inode->i_count ++;
+	new_inode->i_count++;
 	result = 0;
 	for (;;) {
 		if (new_inode == old_inode) {
@@ -937,7 +934,7 @@
 	if (retval)
 		goto end_rename;
 	if (new_inode) {
-		new_inode->i_nlink --;
+		new_inode->i_nlink--;
 		new_inode->i_dirt = 1;
 	}
 	old_bh->b_dirt = 1;
@@ -945,8 +942,8 @@
 	if (dir_bh) {
 		PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
 		dir_bh->b_dirt = 1;
-		old_dir->i_nlink --;
-		new_dir->i_nlink ++;
+		old_dir->i_nlink--;
+		new_dir->i_nlink++;
 		old_dir->i_dirt = 1;
 		new_dir->i_dirt = 1;
 	}
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index 2c255eb..e802842 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -38,7 +38,7 @@
 	NULL,			/* rmdir */
 	NULL,			/* mknod */
 	NULL,			/* rename */
-	ext2_readlink,	/* readlink */
+	ext2_readlink,		/* readlink */
 	ext2_follow_link,	/* follow_link */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
@@ -55,7 +55,7 @@
 	*res_inode = NULL;
 	if (!dir) {
 		dir = current->root;
-		dir->i_count ++;
+		dir->i_count++;
 	}
 	if (!inode) {
 		iput (dir);
@@ -80,9 +80,9 @@
 		link = bh->b_data;
 	} else
 		link = (char *) inode->u.ext2_i.i_data;
-	current->link_count ++;
+	current->link_count++;
 	error = open_namei (link, flag, mode, res_inode, dir);
-	current->link_count --;
+	current->link_count--;
 	iput (inode);
 	if (bh)
 		brelse (bh);
@@ -114,7 +114,7 @@
 		link = (char *) inode->u.ext2_i.i_data;
 	i = 0;
 	while (i < buflen && (c = link[i])) {
-		i ++;
+		i++;
 		put_fs_byte (c, buffer++);
 	}
 	iput (inode);
diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c
index 0028845..92fc244 100644
--- a/fs/ext2/truncate.c
+++ b/fs/ext2/truncate.c
@@ -161,7 +161,7 @@
 		return 0;
 	}
 repeat:
-	for (i = dindirect_block ; i < addr_per_block ; i ++) {
+	for (i = dindirect_block ; i < addr_per_block ; i++) {
 		if (i < 0)
 			i = 0;
 		if (i < dindirect_block)
@@ -218,7 +218,7 @@
 		return 0;
 	}
 repeat:
-	for (i = tindirect_block ; i < addr_per_block ; i ++) {
+	for (i = tindirect_block ; i < addr_per_block ; i++) {
 		if (i < 0)
 			i = 0;
 		if (i < tindirect_block)
diff --git a/fs/fifo.c b/fs/fifo.c
index 2e63550..c899f8a 100644
--- a/fs/fifo.c
+++ b/fs/fifo.c
@@ -99,7 +99,7 @@
 	}
 	if (retval || PIPE_BASE(*inode))
 		return retval;
-	page = get_free_page(GFP_KERNEL);
+	page = __get_free_page(GFP_KERNEL);
 	if (PIPE_BASE(*inode)) {
 		free_page(page);
 		return 0;
diff --git a/fs/file_table.c b/fs/file_table.c
index e23d9da..8542dd9 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -42,26 +42,21 @@
 
 void grow_files(void)
 {
-	unsigned long page;
 	struct file * file;
 	int i;
 
-	page = get_free_page(GFP_BUFFER);
-	if (!page)
+	file = (struct file*) __get_free_page(GFP_BUFFER);
+
+	if (!file)
 		return;
-	file = (struct file *) page;
-	for (i=0; i < (PAGE_SIZE / sizeof(struct file)); i++, file++)
-	{
-		if (!first_file)
-		{
-			file->f_next = file;
-			file->f_prev = file;
-			first_file = file;
-		}
-		else
-			insert_file_free(file);
-	}
-	nr_files += i;
+
+	nr_files+=i= PAGE_SIZE/sizeof(struct file);
+
+	if (!first_file)
+		file->f_next = file->f_prev = first_file = file++, i--;
+
+	for (; i ; i--)
+		insert_file_free(file++);
 }
 
 unsigned long file_table_init(unsigned long start, unsigned long end)
diff --git a/fs/inode.c b/fs/inode.c
index 1c0f8b1..fcd1825 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -17,7 +17,7 @@
 static struct wait_queue * inode_wait = NULL;
 static int nr_inodes = 0, nr_free_inodes = 0;
 
-static inline int const hashfn(dev_t dev, int i)
+static inline int const hashfn(dev_t dev, unsigned int i)
 {
 	return (dev ^ i) % NR_IHASH;
 }
@@ -84,24 +84,21 @@
 
 void grow_inodes(void)
 {
-	unsigned long page;
 	struct inode * inode;
 	int i;
 
-	page = get_free_page(GFP_BUFFER);
-	if (!page)
+	if(!(inode = (struct inode*) get_free_page(GFP_KERNEL)))
 		return;
-	inode = (struct inode *) page;
-	for (i=0; i < (PAGE_SIZE / sizeof(struct inode)); i++, inode++) {
-		if (!first_inode) {
-			inode->i_next = inode;
-			inode->i_prev = inode;
-			first_inode = inode;
-		} else
-			insert_inode_free(inode);
-	}
+
+	i=PAGE_SIZE / sizeof(struct inode);
 	nr_inodes += i;
 	nr_free_inodes += i;
+
+	if (!first_inode)
+		inode->i_next = inode->i_prev = first_inode = inode++, i--;
+
+	for ( ; i ; i-- )
+		insert_inode_free(inode++);
 }
 
 unsigned long inode_init(unsigned long start, unsigned long end)
@@ -400,7 +397,7 @@
 
 	if (!(inode = get_empty_inode()))
 		return NULL;
-	if (!(PIPE_BASE(*inode) = (char *) get_free_page(GFP_USER))) {
+	if (!(PIPE_BASE(*inode) = (char*) __get_free_page(GFP_USER))) {
 		iput(inode);
 		return NULL;
 	}
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 23310cd..7c9777e 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -18,7 +18,15 @@
 #include <asm/segment.h>
 #include <linux/errno.h>
 
+#if defined(CONFIG_BLK_DEV_SR)
 extern int check_cdrom_media_change(int, int);
+#endif
+#if defined(CONFIG_CDU31A)
+extern int check_cdu31a_media_change(int, int);
+#endif
+#if defined(CONFIG_MCD)
+extern int check_mcd_media_change(int, int);
+#endif
 
 #ifdef LEAK_CHECK
 static int check_malloc = 0;
@@ -254,11 +262,27 @@
 		printk("get root inode failed\n");
 		return NULL;
 	}
+#if defined(CONFIG_BLK_DEV_SR)
 	if(MAJOR(s->s_dev) == 11) {
-		/* Chech this one more time. */
+		/* Check this one more time. */
 		if(check_cdrom_media_change(s->s_dev, 0))
 		  goto out;
 	};
+#endif
+#if defined(CONFIG_CDU31A)
+	if(MAJOR(s->s_dev) == 15) {
+		/* Check this one more time. */
+		if(check_cdu31a_media_change(s->s_dev, 0))
+		  goto out;
+	};
+#endif
+#if defined(CONFIG_MCD)
+	if(MAJOR(s->s_dev) == 23) {
+		/* Check this one more time. */
+		if(check_mcd_media_change(s->s_dev, 0))
+		  goto out;
+	};
+#endif
 	return s;
  out: /* Kick out for various error conditions */
 	brelse(bh);
@@ -321,15 +345,15 @@
 		raw_inode = ((struct iso_directory_record *) pnt);
 	};
 
-	inode->i_mode = 0444; /* Everybody gets to read the file. */
+	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 = 0555 | S_IFDIR;
+		inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
 		inode->i_nlink = 2; /* There are always at least 2.  It is
 				       hard to figure out what is correct*/
 	} else {
-		inode->i_mode = 0444; /* Everybody gets to read the file. */
+		inode->i_mode = S_IRUGO; /* 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 */
@@ -337,7 +361,7 @@
 			if(raw_inode->name[i]=='.' || raw_inode->name[i]==';')
 				break;
 		if(i == raw_inode->name_len[0] || raw_inode->name[i] == ';') 
-			inode->i_mode |= 0111; /* execute permission */
+			inode->i_mode |= S_IXUGO; /* execute permission */
 	};
 	inode->i_uid = 0;
 	inode->i_gid = 0;
diff --git a/fs/locks.c b/fs/locks.c
index 43dc61e..3a5e4b6 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -20,7 +20,7 @@
 #include <linux/stat.h>
 #include <linux/fcntl.h>
 
-#define OFFSET_MAX	0x7fffffff	/* FIXME: move elsewhere? */
+#define OFFSET_MAX	((off_t)0x7fffffff)	/* FIXME: move elsewhere? */
 
 static int copy_flock(struct file *filp, struct file_lock *fl, struct flock *l);
 static int conflict(struct file_lock *caller_fl, struct file_lock *sys_fl);
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 717cbf6..206e5ec 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -14,6 +14,7 @@
 
 #include <asm/system.h>
 #include <asm/segment.h>
+#include <asm/bitops.h>
 
 void minix_put_inode(struct inode *inode)
 {
@@ -116,8 +117,8 @@
 		printk("MINIX-fs: bad superblock or unable to read bitmaps\n");
 		return NULL;
 	}
-	s->u.minix_sb.s_imap[0]->b_data[0] |= 1;
-	s->u.minix_sb.s_zmap[0]->b_data[0] |= 1;
+	set_bit(0,s->u.minix_sb.s_imap[0]->b_data);
+	set_bit(0,s->u.minix_sb.s_zmap[0]->b_data);
 	/* set up enough so that it can read an inode */
 	s->s_dev = dev;
 	s->s_op = &minix_sops;
diff --git a/fs/msdos/inode.c b/fs/msdos/inode.c
index 9289e4b..1739403 100644
--- a/fs/msdos/inode.c
+++ b/fs/msdos/inode.c
@@ -315,7 +315,7 @@
 	inode->i_uid = MSDOS_SB(inode->i_sb)->fs_uid;
 	inode->i_gid = MSDOS_SB(inode->i_sb)->fs_gid;
 	if (inode->i_ino == MSDOS_ROOT_INO) {
-		inode->i_mode = (0777 & ~MSDOS_SB(inode->i_sb)->fs_umask) |
+		inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(inode->i_sb)->fs_umask) |
 		    S_IFDIR;
 		inode->i_op = &msdos_dir_inode_operations;
 		inode->i_nlink = msdos_subdirs(inode)+2;
@@ -339,7 +339,7 @@
 	raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
 	    [inode->i_ino & (MSDOS_DPB-1)];
 	if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) {
-		inode->i_mode = MSDOS_MKMODE(raw_entry->attr,0777 &
+		inode->i_mode = MSDOS_MKMODE(raw_entry->attr,S_IRWXUGO &
 		    ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFDIR;
 		inode->i_op = &msdos_dir_inode_operations;
 		MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
@@ -363,7 +363,7 @@
 	}
 	else {
 		inode->i_mode = MSDOS_MKMODE(raw_entry->attr,(IS_NOEXEC(inode)
-		    ? 0666 : 0777) & ~MSDOS_SB(inode->i_sb)->fs_umask) |
+		    ? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~MSDOS_SB(inode->i_sb)->fs_umask) |
 		    S_IFREG;
 		inode->i_op = MSDOS_CAN_BMAP(MSDOS_SB(inode->i_sb)) ? 
 		    &msdos_file_inode_operations :
@@ -436,10 +436,10 @@
 		error = -EPERM;
 	}
 	if (IS_NOEXEC(inode) && !S_ISDIR(inode->i_mode))
-		inode->i_mode &= S_IFMT | 0666;
-	else inode->i_mode |= 0111;
+		inode->i_mode &= S_IFMT | S_IRUGO | S_IWUGO;
+	else inode->i_mode |= S_IXUGO;
 	inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
-	    & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IRUSR) >> 6)*0111)) &
+	    & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
 	    ~MSDOS_SB(inode->i_sb)->fs_umask;
 	return MSDOS_SB(inode->i_sb)->quiet ? 0 : error;
 }
diff --git a/fs/msdos/misc.c b/fs/msdos/misc.c
index 2eb2abd..edd704f 100644
--- a/fs/msdos/misc.c
+++ b/fs/msdos/misc.c
@@ -15,7 +15,7 @@
 /* Well-known binary file extensions */
 
 static char bin_extensions[] =
-  "EXECOMAPPSYSOVLOBJLIB"	/* program code */
+  "EXECOMBINAPPSYSDRVOVLOVROBJLIBDLLPIF"	/* program code */
   "ARCZIPLHALZHZOOTARZ  ARJ"	/* common archivers */
   "TZ TAZTZPTPZ"		/* abbreviations of tar.Z and tar.zip */
   "GIFBMPTIFGL JPGPCX"		/* graphics */
@@ -141,7 +141,7 @@
 #endif
 	last = 0;
 	if ((current = MSDOS_I(inode)->i_start) != 0) {
-		cache_lookup(inode,0x7fffffff,&last,&current);
+		cache_lookup(inode,INT_MAX,&last,&current);
 		while (current && current != -1)
 			if (!(current = fat_access(inode->i_sb,
 			    last = current,-1))) {
diff --git a/fs/namei.c b/fs/namei.c
index c864777..6c8945e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -44,8 +44,7 @@
 	c = get_fs_byte(filename++);
 	if (!c)
 		return -ENOENT;
-	page = __get_free_page(GFP_KERNEL);
-	if (!page)
+	if(!(page = __get_free_page(GFP_KERNEL)))
 		return -ENOMEM;
 	*result = tmp = (char *) page;
 	while (--i) {
@@ -283,7 +282,7 @@
 	struct inode * dir, *inode;
 	struct task_struct ** p;
 
-	mode &= 07777 & ~current->umask;
+	mode &= S_IALLUGO & ~current->umask;
 	mode |= S_IFREG;
 	error = dir_namei(pathname,&namelen,&basename,base,&dir);
 	if (error)
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index f4fdd2f..c51d7f7 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -15,7 +15,7 @@
 	$(AS) -o $*.o $<
 
 OBJS=	proc.o sock.o inode.o file.o dir.o \
-	symlink.o 
+	symlink.o mmap.o
 
 nfs.o: $(OBJS)
 	$(LD) -r -o nfs.o $(OBJS)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 4f5f0c0..f4e567d 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -477,7 +477,7 @@
 		iput(dir);
 		return -ENAMETOOLONG;
 	}
-	sattr.mode = S_IFLNK | 0777; /* SunOS 4.1.2 crashes without this! */
+	sattr.mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */
 	sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 	error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index cbf27b3..a77d0e4 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -19,6 +19,8 @@
 
 static int nfs_file_read(struct inode *, struct file *, char *, int);
 static int nfs_file_write(struct inode *, struct file *, char *, int);
+extern int nfs_mmap(struct inode * inode, struct file * file,
+	      unsigned long addr, size_t len, int prot, unsigned long off);
 
 static struct file_operations nfs_file_operations = {
 	NULL,			/* lseek - default */
@@ -27,7 +29,7 @@
 	NULL,			/* readdir - bad */
 	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
-	NULL,			/* mmap */
+	nfs_mmap,		/* mmap */
 	NULL,			/* no special open is needed */
 	NULL,			/* release */
 	NULL			/* fsync */
diff --git a/fs/nfs/mmap.c b/fs/nfs/mmap.c
new file mode 100644
index 0000000..e1b13fb
--- /dev/null
+++ b/fs/nfs/mmap.c
@@ -0,0 +1,159 @@
+/*
+ *	fs/nfs/mmap.c	by Jon Tombs 15 Aug 1993
+ *
+ * This code is from
+ *	linux/mm/mmap.c which was written by obz, Linus and Eric
+ * and
+ *	linux/mm/memory.c  by Linus Torvalds and others
+ *
+ *	Copyright (C) 1993
+ *
+ */
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/nfs_fs.h>
+
+extern int share_page(struct vm_area_struct * area, struct task_struct * tsk,
+	struct inode * inode, unsigned long address, unsigned long error_code,
+	unsigned long newpage);
+
+extern unsigned long put_page(struct task_struct * tsk,unsigned long page,
+	unsigned long address,int prot);
+
+static void nfs_file_mmap_nopage(int error_code, struct vm_area_struct * area,
+				unsigned long address);
+
+extern void file_mmap_free(struct vm_area_struct * area);
+extern int file_mmap_share(struct vm_area_struct * from, struct vm_area_struct * to,
+				unsigned long address);
+
+struct vm_operations_struct nfs_file_mmap = {
+	NULL,			/* open */
+	file_mmap_free,		/* close */
+	nfs_file_mmap_nopage,	/* nopage */
+	NULL,			/* wppage */
+	file_mmap_share,	/* share */
+};
+
+
+/* This is used for a general mmap of a nfs file */
+int nfs_mmap(struct inode * inode, struct file * file,
+	unsigned long addr, size_t len, int prot, unsigned long off)
+{
+	struct vm_area_struct * mpnt;
+
+	if (off & (inode->i_sb->s_blocksize - 1))
+		return -EINVAL;
+	if (len > high_memory || off > high_memory - len) /* avoid overflow */
+		return -ENXIO;
+	if (get_limit(USER_DS) != TASK_SIZE)
+		return -EINVAL;
+	if (!inode->i_sb || !S_ISREG(inode->i_mode))
+		return -EACCES;
+	if (!IS_RDONLY(inode)) {
+		inode->i_atime = CURRENT_TIME;
+		inode->i_dirt = 1;
+	}
+
+	mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
+	if (!mpnt)
+		return -ENOMEM;
+
+	unmap_page_range(addr, len);
+	mpnt->vm_task = current;
+	mpnt->vm_start = addr;
+	mpnt->vm_end = addr + len;
+	mpnt->vm_page_prot = prot;
+	mpnt->vm_share = NULL;
+	mpnt->vm_inode = inode;
+	inode->i_count++;
+	mpnt->vm_offset = off;
+	mpnt->vm_ops = &nfs_file_mmap;
+	mpnt->vm_next = current->mmap;
+	current->mmap = mpnt;
+#if 0
+	printk("VFS: Loaded mmap at %08x - %08x\n",
+		mpnt->vm_start,	mpnt->vm_end);
+#endif
+	return 0;
+}
+
+
+static void nfs_file_mmap_nopage(int error_code, struct vm_area_struct * area,
+				unsigned long address)
+{
+	struct inode * inode = area->vm_inode;
+	unsigned int clear;
+	unsigned long page;
+	unsigned long tmp;
+	int n;
+	int i;
+	int pos;
+	struct nfs_fattr fattr;
+
+	address &= PAGE_MASK;
+	pos = address - area->vm_start + area->vm_offset;
+
+	page = get_free_page(GFP_KERNEL);
+	if (share_page(area, area->vm_task, inode, address, error_code, page)) {
+		++area->vm_task->min_flt;
+		return;
+	}
+
+	++area->vm_task->maj_flt;
+	if (!page) {
+		oom(current);
+		put_page(area->vm_task, BAD_PAGE, address, PAGE_PRIVATE);
+		return;
+	}
+
+	clear = 0;
+	if (address + PAGE_SIZE > area->vm_end) {
+		clear = address + PAGE_SIZE - area->vm_end;
+	}
+
+	n = NFS_SERVER(inode)->rsize; /* what we can read in one go */
+
+	for (i = 0; i < (PAGE_SIZE - clear); i += n) {
+		int hunk, result;
+
+		hunk = PAGE_SIZE - i;
+		if (hunk > n)
+			hunk = n;
+		result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
+			pos, hunk, (char *) (page + i), &fattr);
+		if (result < 0)
+			break;
+		pos += result;
+		if (result < n) {
+			i += result;
+			break;
+		}
+	}
+
+#ifdef doweneedthishere
+	nfs_refresh_inode(inode, &fattr);
+#endif
+
+	if (!(error_code & PAGE_RW)) {
+		if (share_page(area, area->vm_task, inode, address, error_code, page))
+			return;
+	}
+
+	tmp = page + PAGE_SIZE;
+	while (clear--) {
+		*(char *)--tmp = 0;
+	}
+	if (put_page(area->vm_task,page,address,area->vm_page_prot))
+		return;
+	free_page(page);
+	oom(current);
+}
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index df5bfc4..a9f699c 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -33,6 +33,11 @@
 #define PRINTK if (0) printk
 #endif
 
+#define PREP_PAGE_RPC(code)	\
+  if (!(p0 = (int*)__get_free_page(GFP_KERNEL)))\
+	return NFSERR_IO;\
+  p=nfs_rpc_header(p0,code)
+
 static int *nfs_rpc_header(int *p, int procedure);
 static int *nfs_rpc_verify(int *p);
 static int nfs_stat_to_errno(int stat);
@@ -168,8 +173,7 @@
 	int status;
 
 	PRINTK("NFS call  getattr\n");
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_GETATTR);
+	PREP_PAGE_RPC(NFSPROC_GETATTR);
 	p = xdr_encode_fhandle(p, fhandle);
 	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
 		free_page((long) p0);
@@ -194,8 +198,7 @@
 	int status;
 
 	PRINTK("NFS call  setattr\n");
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_SETATTR);
+	PREP_PAGE_RPC(NFSPROC_SETATTR);
 	p = xdr_encode_fhandle(p, fhandle);
 	p = xdr_encode_sattr(p, sattr);
 	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
@@ -225,8 +228,7 @@
 	if (!strcmp(name, "xyzzy"))
 		proc_debug = 1 - proc_debug;
 #endif
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_LOOKUP);
+	PREP_PAGE_RPC(NFSPROC_LOOKUP);
 	p = xdr_encode_fhandle(p, dir);
 	p = xdr_encode_string(p, name);
 	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
@@ -253,8 +255,7 @@
 	int status;
 
 	PRINTK("NFS call  readlink\n");
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_READLINK);
+	PREP_PAGE_RPC(NFSPROC_READLINK);
 	p = xdr_encode_fhandle(p, fhandle);
 	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
 		free_page((long) p0);
@@ -284,8 +285,7 @@
 	int len = 0; /* = 0 is for gcc */
 
 	PRINTK("NFS call  read %d @ %d\n", count, offset);
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_READ);
+	PREP_PAGE_RPC(NFSPROC_READ);
 	p = xdr_encode_fhandle(p, fhandle);
 	*p++ = htonl(offset);
 	*p++ = htonl(count);
@@ -318,8 +318,7 @@
 	int status;
 
 	PRINTK("NFS call  write %d @ %d\n", count, offset);
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_WRITE);
+	PREP_PAGE_RPC(NFSPROC_WRITE);
 	p = xdr_encode_fhandle(p, fhandle);
 	*p++ = htonl(offset); /* traditional, could be any value */
 	*p++ = htonl(offset);
@@ -349,8 +348,7 @@
 	int status;
 
 	PRINTK("NFS call  create %s\n", name);
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_CREATE);
+	PREP_PAGE_RPC(NFSPROC_CREATE);
 	p = xdr_encode_fhandle(p, dir);
 	p = xdr_encode_string(p, name);
 	p = xdr_encode_sattr(p, sattr);
@@ -377,8 +375,7 @@
 	int status;
 
 	PRINTK("NFS call  remove %s\n", name);
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_REMOVE);
+	PREP_PAGE_RPC(NFSPROC_REMOVE);
 	p = xdr_encode_fhandle(p, dir);
 	p = xdr_encode_string(p, name);
 	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
@@ -404,8 +401,7 @@
 	int status;
 
 	PRINTK("NFS call  rename %s -> %s\n", old_name, new_name);
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_RENAME);
+	PREP_PAGE_RPC(NFSPROC_RENAME);
 	p = xdr_encode_fhandle(p, old_dir);
 	p = xdr_encode_string(p, old_name);
 	p = xdr_encode_fhandle(p, new_dir);
@@ -432,8 +428,7 @@
 	int status;
 
 	PRINTK("NFS call  link %s\n", name);
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_LINK);
+	PREP_PAGE_RPC(NFSPROC_LINK);
 	p = xdr_encode_fhandle(p, fhandle);
 	p = xdr_encode_fhandle(p, dir);
 	p = xdr_encode_string(p, name);
@@ -459,8 +454,7 @@
 	int status;
 
 	PRINTK("NFS call  symlink %s -> %s\n", name, path);
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_SYMLINK);
+	PREP_PAGE_RPC(NFSPROC_SYMLINK);
 	p = xdr_encode_fhandle(p, dir);
 	p = xdr_encode_string(p, name);
 	p = xdr_encode_string(p, path);
@@ -488,8 +482,7 @@
 	int status;
 
 	PRINTK("NFS call  mkdir %s\n", name);
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_MKDIR);
+	PREP_PAGE_RPC(NFSPROC_MKDIR);
 	p = xdr_encode_fhandle(p, dir);
 	p = xdr_encode_string(p, name);
 	p = xdr_encode_sattr(p, sattr);
@@ -516,8 +509,7 @@
 	int status;
 
 	PRINTK("NFS call  rmdir %s\n", name);
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_RMDIR);
+	PREP_PAGE_RPC(NFSPROC_RMDIR);
 	p = xdr_encode_fhandle(p, dir);
 	p = xdr_encode_string(p, name);
 	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
@@ -546,8 +538,7 @@
 
 	PRINTK("NFS call  readdir %d @ %d\n", count, cookie);
 	size = server->rsize;
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_READDIR);
+	PREP_PAGE_RPC(NFSPROC_READDIR);
 	p = xdr_encode_fhandle(p, fhandle);
 	*p++ = htonl(cookie);
 	*p++ = htonl(size);
@@ -588,8 +579,7 @@
 	int status;
 
 	PRINTK("NFS call  statfs\n");
-	p = p0 = (int *) get_free_page(GFP_KERNEL);
-	p = nfs_rpc_header(p, NFSPROC_STATFS);
+	PREP_PAGE_RPC(NFSPROC_STATFS);
 	p = xdr_encode_fhandle(p, fhandle);
 	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
 		free_page((long) p0);
diff --git a/fs/nfs/sock.c b/fs/nfs/sock.c
index 2f97999..ec64064 100644
--- a/fs/nfs/sock.c
+++ b/fs/nfs/sock.c
@@ -120,7 +120,7 @@
 			remove_wait_queue(entry.wait_address, &entry.wait);
 		current->state = TASK_RUNNING;
 		addrlen = 0;
-		result = sock->ops->recvfrom(sock, (void *) start, 4096, 1, 0,
+		result = sock->ops->recvfrom(sock, (void *) start, PAGE_SIZE, 1, 0,
 			NULL, &addrlen);
 		if (result < 0) {
 			if (result == -EAGAIN) {
diff --git a/fs/open.c b/fs/open.c
index 914a546..2fb689e 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -153,24 +153,24 @@
 
 /*
  * XXX we should use the real ids for checking _all_ components of the
- * path.  Now we only use them for the final compenent of the path.
+ * path.  Now we only use them for the final component of the path.
  */
 extern "C" int sys_access(const char * filename,int mode)
 {
 	struct inode * inode;
 	int res, i_mode;
 
-	if (mode != (mode & 0007))	/* where's F_OK, X_OK, W_OK, R_OK? */
+	if (mode != (mode & S_IRWXO))	/* where's F_OK, X_OK, W_OK, R_OK? */
 		return -EINVAL;
 	res = namei(filename,&inode);
 	if (res)
 		return res;
 	i_mode = inode->i_mode;
-	res = i_mode & 0777;
+	res = i_mode & S_IRWXUGO;
 	if (current->uid == inode->i_uid)
-		res >>= 6;
+		res >>= 6;		/* needs cleaning? */
 	else if (in_group_p(inode->i_gid))
-		res >>= 3;
+		res >>= 3;		/* needs cleaning? */
 	iput(inode);
 	if ((res & mode) == mode)
 		return 0;
@@ -184,7 +184,7 @@
 	 * decomposing the path would be racy.
 	 */
 	if ((!current->uid) &&
-	    (S_ISDIR(i_mode) || !(mode & 1) || (i_mode & 0111)))
+	    (S_ISDIR(i_mode) || !(mode & S_IXOTH) || (i_mode & S_IXUGO)))
 		return 0;
 	return -EACCES;
 }
@@ -244,7 +244,7 @@
 		return -EPERM;
 	if (IS_RDONLY(inode))
 		return -EROFS;
-	inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
+	inode->i_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	if (!suser() && !in_group_p(inode->i_gid))
 		inode->i_mode &= ~S_ISGID;
 	inode->i_ctime = CURRENT_TIME;
@@ -268,7 +268,7 @@
 		iput(inode);
 		return -EROFS;
 	}
-	inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
+	inode->i_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	if (!suser() && !in_group_p(inode->i_gid))
 		inode->i_mode &= ~S_ISGID;
 	inode->i_ctime = CURRENT_TIME;
@@ -350,11 +350,10 @@
  * for the internal routines (ie open_namei()/follow_link() etc). 00 is
  * used by symlinks.
  */
-extern "C" int sys_open(const char * filename,int flags,int mode)
+int do_open(const char * filename,int flags,int mode)
 {
 	struct inode * inode;
 	struct file * f;
-	char * tmp;
 	int flag,error,fd;
 
 	for(fd=0 ; fd<NR_OPEN ; fd++)
@@ -373,11 +372,7 @@
 		flag++;
 	if (flag & (O_TRUNC | O_CREAT))
 		flag |= 2;
-	error = getname(filename,&tmp);
-	if (!error) {
-		error = open_namei(tmp,flag,mode,&inode,NULL);
-		putname(tmp);
-	}
+	error = open_namei(filename,flag,mode,&inode,NULL);
 	if (error) {
 		current->filp[fd]=NULL;
 		f->f_count--;
@@ -414,6 +409,19 @@
 	return (fd);
 }
 
+extern "C" int sys_open(const char * filename,int flags,int mode)
+{
+	char * tmp;
+	int error;
+
+	error = getname(filename, &tmp);
+	if (error)
+		return error;
+	error = do_open(tmp,flags,mode);
+	putname(tmp);
+	return error;
+}
+
 extern "C" int sys_creat(const char * pathname, int mode)
 {
 	return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
diff --git a/fs/proc/array.c b/fs/proc/array.c
index bc43cac..fe1d4e6 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -89,18 +89,16 @@
 
 	if (!p || !*p || ptr >= TASK_SIZE)
 		return 0;
-	page = (*p)->tss.cr3;
-	page += (ptr >> 20) & 0xffc;
+	page = *PAGE_DIR_OFFSET((*p)->tss.cr3,ptr);
+	if (!(page & 1))
+		return 0;
+	page &= PAGE_MASK;
+	page += PAGE_PTR(ptr);
 	page = *(unsigned long *) page;
 	if (!(page & 1))
 		return 0;
-	page &= 0xfffff000;
-	page += (ptr >> 10) & 0xffc;
-	page = *(unsigned long *) page;
-	if (!(page & 1))
-		return 0;
-	page &= 0xfffff000;
-	page += ptr & 0xfff;
+	page &= PAGE_MASK;
+	page += ptr & ~PAGE_MASK;
 	return page;
 }
 
@@ -128,7 +126,7 @@
 			start++;
 			if (start >= end)
 				return result;
-		} while (!(addr & 0xfff));
+		} while (!(addr & ~PAGE_MASK));
 	}
 }
 
@@ -196,7 +194,7 @@
 	if (vsize) {
 		eip = KSTK_EIP(vsize);
 		esp = KSTK_ESP(vsize);
-		vsize = (*p)->brk + 4095;
+		vsize = (*p)->brk + PAGE_SIZE-1;
 		if (esp)
 			vsize += TASK_SIZE - esp;
 	}
@@ -266,14 +264,14 @@
 		return 0;
 	tpag = (*p)->end_code / PAGE_SIZE;
 	if ((*p)->state != TASK_ZOMBIE) {
-	  pagedir = (unsigned long *)((*p)->tss.cr3 + ((*p)->start_code >> 20));
+	  pagedir = PAGE_DIR_OFFSET((*p)->tss.cr3,(*p)->start_code);
 	  for (i = 0; i < 0x300; ++i) {
 	    if ((ptbl = pagedir[i]) == 0) {
-	      tpag -= 1024;
+	      tpag -= PTRS_PER_PAGE;
 	      continue;
 	    }
-	    buf = (unsigned long *)(ptbl & 0xfffff000);
-	    for (pte = buf; pte < (buf + 1024); ++pte) {
+	    buf = (unsigned long *)(ptbl & PAGE_MASK);
+	    for (pte = buf; pte < (buf + PTRS_PER_PAGE); ++pte) {
 	      if (*pte != 0) {
 		++size;
 		if (*pte & 1) {
@@ -290,7 +288,7 @@
 		      --drs;
 		  }
 		  map_nr = MAP_NR(*pte);
-		  if (map_nr < (high_memory / 4096) && mem_map[map_nr] > 1)
+		  if (map_nr < (high_memory / PAGE_SIZE) && mem_map[map_nr] > 1)
 		    ++share;
 		}
 	      }
@@ -311,8 +309,7 @@
 
 	if (count < 0)
 		return -EINVAL;
-	page = (char *) get_free_page(GFP_KERNEL);
-	if (!page)
+	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
 		return -ENOMEM;
 	type = inode->i_ino;
 	pid = type >> 16;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index cb2353f..97cbe2a 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -61,7 +61,7 @@
 void proc_statfs(struct super_block *sb, struct statfs *buf)
 {
 	put_fs_long(PROC_SUPER_MAGIC, &buf->f_type);
-	put_fs_long(1024, &buf->f_bsize);
+	put_fs_long(PAGE_SIZE/sizeof(long), &buf->f_bsize);
 	put_fs_long(0, &buf->f_blocks);
 	put_fs_long(0, &buf->f_bfree);
 	put_fs_long(0, &buf->f_bavail);
@@ -94,7 +94,7 @@
 	if (!p || i >= NR_TASKS)
 		return;
 	if (ino == PROC_ROOT_INO) {
-		inode->i_mode = S_IFDIR | 0555;
+		inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
 		inode->i_nlink = 2;
 		for (i = 1 ; i < NR_TASKS ; i++)
 			if (task[i])
@@ -103,23 +103,23 @@
 		return;
 	}
 	if ((ino >= 128) && (ino <= 160)) { /* files within /proc/net */
-		inode->i_mode = S_IFREG | 0444;
+		inode->i_mode = S_IFREG | S_IRUGO;
 		inode->i_op = &proc_net_inode_operations;
 		return;
 	}
 	if (!pid) {
 		switch (ino) {
 			case 5:
-				inode->i_mode = S_IFREG | 0444;
+				inode->i_mode = S_IFREG | S_IRUGO;
 				inode->i_op = &proc_kmsg_inode_operations;
 				break;
 			case 8: /* for the net directory */
-				inode->i_mode = S_IFDIR | 0555;
+				inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
 				inode->i_nlink = 2;
 				inode->i_op = &proc_net_inode_operations;
 				break;
 			default:
-				inode->i_mode = S_IFREG | 0444;
+				inode->i_mode = S_IFREG | S_IRUGO;
 				inode->i_op = &proc_array_inode_operations;
 				break;
 		}
@@ -131,23 +131,23 @@
 	switch (ino) {
 		case 2:
 			inode->i_nlink = 4;
-			inode->i_mode = S_IFDIR | 0555;
+			inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
 			inode->i_op = &proc_base_inode_operations;
 			return;
 		case 3:
 			inode->i_op = &proc_mem_inode_operations;
-			inode->i_mode = S_IFREG | 0600;
+			inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
 			return;
 		case 4:
 		case 5:
 		case 6:
 			inode->i_op = &proc_link_inode_operations;
 			inode->i_size = 64;
-			inode->i_mode = S_IFLNK | 0700;
+			inode->i_mode = S_IFLNK | S_IRWXU;
 			return;
 		case 7:
 		case 8:
-			inode->i_mode = S_IFDIR | 0500;
+			inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR;
 			inode->i_op = &proc_fd_inode_operations;
 			inode->i_nlink = 2;
 			return;
@@ -155,7 +155,7 @@
 		case 10:
 		case 11:
 		case 12:
-			inode->i_mode = S_IFREG | 0444;
+			inode->i_mode = S_IFREG | S_IRUGO;
 			inode->i_op = &proc_array_inode_operations;
 			return;
 	}
@@ -166,7 +166,7 @@
 				return;
 			inode->i_op = &proc_link_inode_operations;
 			inode->i_size = 64;
-			inode->i_mode = S_IFLNK | 0700;
+			inode->i_mode = S_IFLNK | S_IRWXU;
 			return;
 		case 2:
 			ino &= 0xff;
@@ -181,7 +181,7 @@
 			}
 			inode->i_op = &proc_link_inode_operations;
 			inode->i_size = 64;
-			inode->i_mode = S_IFLNK | 0700;
+			inode->i_mode = S_IFLNK | S_IRWXU;
 			return;
 	}
 	return;
diff --git a/fs/proc/mem.c b/fs/proc/mem.c
index 368e28f..28fd4be 100644
--- a/fs/proc/mem.c
+++ b/fs/proc/mem.c
@@ -24,7 +24,7 @@
 {
 	unsigned long addr, pid, cr3;
 	char *tmp;
-	unsigned long pde, pte, page;
+	unsigned long pte, page;
 	int i;
 
 	if (count < 0)
@@ -44,18 +44,17 @@
 	while (count > 0) {
 		if (current->signal & ~current->blocked)
 			break;
-		pde = cr3 + (addr >> 20 & 0xffc);
-		pte = *(unsigned long *) pde;
+		pte = *PAGE_DIR_OFFSET(cr3,addr);
 		if (!(pte & PAGE_PRESENT))
 			break;
-		pte &= 0xfffff000;
-		pte += (addr >> 10) & 0xffc;
+		pte &= PAGE_MASK;
+		pte += PAGE_PTR(addr);
 		page = *(unsigned long *) pte;
 		if (!(page & 1))
 			break;
-		page &= 0xfffff000;
-		page += addr & 0xfff;
-		i = 4096-(addr & 0xfff);
+		page &= PAGE_MASK;
+		page += addr & ~PAGE_MASK;
+		i = PAGE_SIZE-(addr & ~PAGE_MASK);
 		if (i > count)
 			i = count;
 		memcpy_tofs(tmp,(void *) page,i);
@@ -73,7 +72,7 @@
 {
 	unsigned long addr, pid, cr3;
 	char *tmp;
-	unsigned long pde, pte, page;
+	unsigned long pte, page;
 	int i;
 
 	if (count < 0)
@@ -93,12 +92,11 @@
 	while (count > 0) {
 		if (current->signal & ~current->blocked)
 			break;
-		pde = cr3 + (addr >> 20 & 0xffc);
-		pte = *(unsigned long *) pde;
+		pte = *PAGE_DIR_OFFSET(cr3,addr);
 		if (!(pte & PAGE_PRESENT))
 			break;
-		pte &= 0xfffff000;
-		pte += (addr >> 10) & 0xffc;
+		pte &= PAGE_MASK;
+		pte += PAGE_PTR(addr);
 		page = *(unsigned long *) pte;
 		if (!(page & PAGE_PRESENT))
 			break;
@@ -106,9 +104,9 @@
 			do_wp_page(0,addr,current,0);
 			continue;
 		}
-		page &= 0xfffff000;
-		page += addr & 0xfff;
-		i = 4096-(addr & 0xfff);
+		page &= PAGE_MASK;
+		page += addr & ~PAGE_MASK;
+		i = PAGE_SIZE-(addr & ~PAGE_MASK);
 		if (i > count)
 			i = count;
 		memcpy_fromfs((void *) page,tmp,i);
diff --git a/fs/proc/net.c b/fs/proc/net.c
index 466163d..9972dde 100644
--- a/fs/proc/net.c
+++ b/fs/proc/net.c
@@ -164,8 +164,7 @@
 
 	if (count < 0)
 		return -EINVAL;
-	page = (char *) get_free_page(GFP_KERNEL);
-	if (!page)
+	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
 		return -ENOMEM;
 	ino = inode->i_ino;
 	switch (ino) {
diff --git a/fs/select.c b/fs/select.c
index 0311289..61066ef 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -104,8 +104,7 @@
 	}
 end_check:
 	n = max + 1;
-	entry = (struct select_table_entry *) __get_free_page(GFP_KERNEL);
-	if (!entry)
+	if(!(entry = (struct select_table_entry*) __get_free_page(GFP_KERNEL)))
 		return -ENOMEM;
 	FD_ZERO(res_in);
 	FD_ZERO(res_out);
@@ -219,7 +218,7 @@
 	if ((i = get_fd_set(n, inp, &in)) ||
 	    (i = get_fd_set(n, outp, &out)) ||
 	    (i = get_fd_set(n, exp, &ex))) return i;
-	timeout = 0xffffffff;
+	timeout = ~0UL;
 	if (tvp) {
 		i = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
 		if (i)
diff --git a/fs/super.c b/fs/super.c
index 420d3b7..fbc6c5b 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -176,7 +176,7 @@
 		memset(unnamed_dev_in_use, 0, sizeof(unnamed_dev_in_use));
 		unnamed_dev_in_use[0] = 1; /* minor 0 (nodev) is special */
 	}
-	for (i = 0; i < 256; i++) {
+	for (i = 0; i < sizeof unnamed_dev_in_use/sizeof unnamed_dev_in_use[0]; i++) {
 		if (!unnamed_dev_in_use[i]) {
 			unnamed_dev_in_use[i] = 1;
 			return (UNNAMED_MAJOR << 8) | i;
@@ -254,15 +254,18 @@
 	if (!suser())
 		return -EPERM;
 	retval = namei(name,&inode);
-	if (retval)
-		return retval;
+	if (retval) {
+		retval = lnamei(name,&inode);
+		if (retval)
+			return retval;
+	}
 	if (S_ISBLK(inode->i_mode)) {
 		dev = inode->i_rdev;
 		if (IS_NODEV(inode)) {
 			iput(inode);
 			return -EACCES;
 		}
-	} else if (S_ISDIR(inode->i_mode)) {
+	} else {
 		if (!inode || !inode->i_sb || inode != inode->i_sb->s_mounted) {
 			iput(inode);
 			return -EINVAL;
@@ -272,9 +275,6 @@
 		memset(&dummy_inode, 0, sizeof(dummy_inode));
 		dummy_inode.i_rdev = dev;
 		inode = &dummy_inode;
-	} else {
-		iput(inode);
-		return -EINVAL;
 	}
 	if (MAJOR(dev) >= MAX_BLKDEV) {
 		iput(inode);
@@ -382,7 +382,7 @@
  * Flags is a 16-bit value that allows up to 16 non-fs dependent flags to
  * be given to the mount() call (ie: read-only, no-dev, no-suid etc).
  *
- * data is a (void *) that can point to any structure up to 4095 bytes, which
+ * data is a (void *) that can point to any structure up to PAGE_SIZE-1 bytes, which
  * can contain arbitrary fs-dependent information (or be NULL).
  *
  * NOTE! As old versions of mount() didn't use this setup, the flags has to have
@@ -458,10 +458,13 @@
 				iput(inode);
 				return -EFAULT;
 			}
-			page = get_free_page(GFP_KERNEL);
+			if (!(page = __get_free_page(GFP_KERNEL))) {
+				iput(inode);
+				return -ENOMEM;
+			}
 			i = TASK_SIZE - (unsigned long) data;
-			if (i < 0 || i > 4095)
-				i = 4095;
+			if ((unsigned long) i >= PAGE_SIZE)
+				i = PAGE_SIZE-1;
 			memcpy_fromfs((void *) page,data,i);
 		}
 	}
diff --git a/fs/xiafs/namei.c b/fs/xiafs/namei.c
index 2f2839f..761d88d 100644
--- a/fs/xiafs/namei.c
+++ b/fs/xiafs/namei.c
@@ -373,7 +373,7 @@
     inode->i_nlink = 2;
     dir_block->b_dirt = 1;
     brelse(dir_block);
-    inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask);
+    inode->i_mode = S_IFDIR | (mode & S_IRWXUGO & ~current->umask);
     if (dir->i_mode & S_ISGID)
         inode->i_mode |= S_ISGID;
     inode->i_dirt = 1;
@@ -594,7 +594,7 @@
         iput(dir);
 	return -ENOSPC;
     }
-    inode->i_mode = S_IFLNK | 0777;
+    inode->i_mode = S_IFLNK | S_IRWXUGO;
     inode->i_op = &xiafs_symlink_inode_operations;
     name_block = xiafs_bread(inode,0,1);
     if (!name_block) {
diff --git a/include/asm/bitops.h b/include/asm/bitops.h
index f8191b9..41b4f29 100644
--- a/include/asm/bitops.h
+++ b/include/asm/bitops.h
@@ -1,4 +1,5 @@
 #ifndef _ASM_BITOPS_H
+#define _ASM_BITOPS_H
 /*
  * Copyright 1992, Linus Torvalds.
  */
@@ -18,7 +19,7 @@
 struct __dummy { unsigned long a[100]; };
 #define ADDR (*(struct __dummy *) addr)
 
-extern inline int set_bit(int nr, void * addr)
+extern __inline__ int set_bit(int nr, void * addr)
 {
 	int oldbit;
 
@@ -28,7 +29,7 @@
 	return oldbit;
 }
 
-extern inline int clear_bit(int nr, void * addr)
+extern __inline__ int clear_bit(int nr, void * addr)
 {
 	int oldbit;
 
@@ -42,7 +43,7 @@
  * This routine doesn't need to be atomic, but it's faster to code it
  * this way.
  */
-extern inline int test_bit(int nr, void * addr)
+extern __inline__ int test_bit(int nr, void * addr)
 {
 	int oldbit;
 
@@ -68,7 +69,7 @@
  * C language equivalents written by Theodore Ts'o, 9/26/92
  */
 
-extern inline int set_bit(int nr,int * addr)
+extern __inline__ int set_bit(int nr,int * addr)
 {
 	int	mask, retval;
 
@@ -81,7 +82,7 @@
 	return retval;
 }
 
-extern inline int clear_bit(int nr, int * addr)
+extern __inline__ int clear_bit(int nr, int * addr)
 {
 	int	mask, retval;
 
@@ -94,7 +95,7 @@
 	return retval;
 }
 
-extern inline int test_bit(int nr, int * addr)
+extern __inline__ int test_bit(int nr, int * addr)
 {
 	int	mask;
 
diff --git a/include/asm/segment.h b/include/asm/segment.h
index 5e471f9..4e8ef17 100644
--- a/include/asm/segment.h
+++ b/include/asm/segment.h
@@ -1,3 +1,6 @@
+#ifndef _ASM_SEGMENT_H
+#define _ASM_SEGMENT_H
+
 static inline unsigned char get_fs_byte(const char * addr)
 {
 	register unsigned char _v;
@@ -164,3 +167,4 @@
 	__asm__ __volatile__("mov %w0,%%fs": /* no output */ :"r" (val));
 }
 
+#endif /* _ASM_SEGMENT_H */
diff --git a/include/asm/system.h b/include/asm/system.h
index 7888381..b88e28a 100644
--- a/include/asm/system.h
+++ b/include/asm/system.h
@@ -76,13 +76,13 @@
 	_set_gate(a,12,3,addr)
 
 #define _set_seg_desc(gate_addr,type,dpl,base,limit) {\
-	*(gate_addr) = ((base) & 0xff000000) | \
+	*((gate_addr)+1) = ((base) & 0xff000000) | \
 		(((base) & 0x00ff0000)>>16) | \
 		((limit) & 0xf0000) | \
 		((dpl)<<13) | \
 		(0x00408000) | \
 		((type)<<8); \
-	*((gate_addr)+1) = (((base) & 0x0000ffff)<<16) | \
+	*(gate_addr) = (((base) & 0x0000ffff)<<16) | \
 		((limit) & 0x0ffff); }
 
 #define _set_tssldt_desc(n,addr,limit,type) \
@@ -100,6 +100,8 @@
 	)
 
 #define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),235,"0x89")
-#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),23,"0x82")
+#define set_ldt_desc(n,addr,size) \
+	_set_tssldt_desc(((char *) (n)),((int)(addr)),((size << 3) - 1),"0x82")
+
 
 #endif
diff --git a/include/linux/a.out.h b/include/linux/a.out.h
index 69bf01f..2c6b8c8 100644
--- a/include/linux/a.out.h
+++ b/include/linux/a.out.h
@@ -134,7 +134,7 @@
 #endif
 
 #ifdef linux
-#define PAGE_SIZE	4096
+#include <linux/page.h>
 #define SEGMENT_SIZE	1024
 #endif
 
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
new file mode 100644
index 0000000..35823c8
--- /dev/null
+++ b/include/linux/binfmts.h
@@ -0,0 +1,36 @@
+#ifndef _LINUX_BINFMTS_H
+#define _LINUX_BINFMTS_H
+
+/*
+ * MAX_ARG_PAGES defines the number of pages allocated for arguments
+ * and envelope for the new program. 32 should suffice, this gives
+ * a maximum env+arg of 128kB !
+ */
+#define MAX_ARG_PAGES 32
+
+/*
+ * This structure is used to hold the arguments that are used when loading binaries.
+ */
+struct linux_binprm{
+  char buf[128];
+  unsigned long page[MAX_ARG_PAGES];
+  unsigned long p;
+  struct inode * inode;
+  int e_uid, e_gid;
+  int argc, envc;
+  char * filename;	   /* Name of binary */
+};
+
+/* This structure defines the functions that are used to load the binary formats that
+ * linux accepts. */
+
+struct linux_binfmt{
+  int (*load_binary)(struct linux_binprm *, struct  pt_regs * regs);
+  int (*load_shlib)(int fd);
+};
+
+extern struct linux_binfmt formats[];
+
+
+
+#endif
diff --git a/include/linux/busmouse.h b/include/linux/busmouse.h
index 2cd45b8..33c6ec7 100644
--- a/include/linux/busmouse.h
+++ b/include/linux/busmouse.h
@@ -83,8 +83,8 @@
 
  
 struct mouse_status {
-	char		buttons;
-	char		latch_buttons;
+	unsigned char	buttons;
+	unsigned char	latch_buttons;
 	int		dx;
 	int		dy;	
 	int 		present;
diff --git a/include/linux/cdu31a.h b/include/linux/cdu31a.h
new file mode 100644
index 0000000..56ad5ad
--- /dev/null
+++ b/include/linux/cdu31a.h
@@ -0,0 +1,313 @@
+/*
+ * Definitions for a Sony interface CDROM drive.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com)
+ *
+ *  Copyright (C) 1993  Corey Minyard
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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.
+ *
+ */
+
+/*
+ * Offsets (from the base address) and bits for the various write registers
+ * of the drive.
+ */
+#define SONY_CMD_REG_OFFSET	0
+#define SONY_PARAM_REG_OFFSET	1
+#define SONY_WRITE_REG_OFFSET	2
+#define SONY_CONTROL_REG_OFFSET	3
+#	define SONY_ATTN_CLR_BIT	0x01
+#	define SONY_RES_RDY_CLR_BIT	0x02
+#	define SONY_DATA_RDY_CLR_BIT	0x04
+#	define SONY_ATTN_INT_EN_BIT	0x08
+#	define SONY_RES_RDY_INT_EN_BIT	0x10
+#	define SONY_DATA_RDY_INT_EN_BIT	0x20
+#	define SONY_PARAM_CLR_BIT	0x40
+#	define SONY_DRIVE_RESET_BIT	0x80
+
+/*
+ * Offsets (from the base address) and bits for the various read registers
+ * of the drive.
+ */
+#define SONY_STATUS_REG_OFFSET	0
+#	define SONY_ATTN_BIT		0x01
+#	define SONY_RES_RDY_BIT		0x02
+#	define SONY_DATA_RDY_BIT	0x04
+#	define SONY_ATTN_INT_ST_BIT	0x08
+#	define SONY_RES_RDY_INT_ST_BIT	0x10
+#	define SONY_DATA_RDY_INT_ST_BIT	0x20
+#	define SONY_DATA_REQUEST_BIT	0x40
+#	define SONY_BUSY_BIT		0x80
+#define SONY_RESULT_REG_OFFSET	1
+#define SONY_READ_REG_OFFSET	2
+#define SONY_FIFOST_REG_OFFSET	3
+#	define SONY_PARAM_WRITE_RDY_BIT	0x01
+#	define SONY_PARAM_REG_EMPTY_BIT	0x02
+#	define SONY_RES_REG_NOT_EMP_BIT	0x04
+#	define SONY_RES_REG_FULL_BIT	0x08
+
+#define LOG_START_OFFSET	150	/* Offset of first logical sector */
+
+#define SONY_DETECT_TIMEOUT	80	/* Maximum amount of time
+					   that drive detection code
+					   will wait for response
+					   from drive (in 1/100th's
+					   of seconds). */
+ 
+#define SONY_JIFFIES_TIMEOUT	500	/* Maximum number of times the
+					   drive will wait/try for an
+					   operation */
+#define SONY_RESET_TIMEOUT	100	/* Maximum number of times the
+					   drive will wait/try a reset
+					   operation */
+#define SONY_READY_RETRIES	20000	/* How many times to retry a
+					   spin waiting for a register
+					   to come ready */
+
+#define MAX_CDU31A_RETRIES	3	/* How many times to retry an
+					   operation */
+
+/* Commands to request or set drive control parameters and disc information */
+#define SONY_REQ_DRIVE_CONFIG_CMD	0x00    /* Returns s_sony_drive_config */
+#define SONY_REQ_DRIVE_MODE_CMD		0x01
+#define SONY_REQ_DRIVE_PARAM_CMD	0x02
+#define SONY_REQ_MECH_STATUS_CMD	0x03
+#define SONY_REQ_AUDIO_STATUS_CMD	0x04
+#define SONY_SET_DRIVE_PARAM_CMD	0x10
+#define SONY_REQ_TOC_DATA_CMD		0x20    /* Returns s_sony_toc */
+#define SONY_REQ_SUBCODE_ADDRESS_CMD	0x21    /* Returns s_sony_subcode */
+#define SONY_REQ_UPC_EAN_CMD		0x22
+#define SONY_REQ_ISRC_CMD		0x23
+#define SONY_REQ_TOC_DATA_SPEC_CMD	0x24
+
+/* Commands to request information from the drive */
+#define SONY_READ_TOC_CMD		0x30
+#define SONY_SEEK_CMD			0x31
+#define SONY_READ_CMD			0x32
+#define SONY_READ_BLKERR_STAT_CMD	0x34
+#define SONY_ABORT_CMD			0x35
+#define SONY_READ_TOC_SPEC_CMD		0x36
+
+/* Commands to control audio */
+#define SONY_AUDIO_PLAYBACK_CMD		0x40
+#define SONY_AUDIO_STOP_CMD		0x41
+#define SONY_AUDIO_SCAN_CMD		0x42
+
+/* Miscellaneous control commands */
+#define SONY_EJECT_CMD			0x50
+#define SONY_SPIN_UP_CMD		0x51
+#define SONY_SPIN_DOWN_CMD		0x52
+
+/* Diagnostic commands */
+#define SONY_WRITE_BUFFER_CMD		0x60
+#define SONY_READ_BUFFER_CMD		0x61
+#define SONY_DIAGNOSTICS_CMD		0x62
+
+
+/*
+ * The following are command paramters for the set drive parameter command
+ */
+#define SONY_SD_DECODE_PARAM		0x00
+#define SONY_SD_INTERFACE_PARAM		0x01
+#define SONY_SD_BUFFERING_PARAM		0x02
+#define SONY_SD_AUDIO_PARAM		0x03
+#define SONY_SD_AUDIO_VOLUME		0x04
+#define SONY_SD_MECH_CONTROL		0x05
+#define SONY_SD_AUTO_SPIN_DOWN_TIME	0x06
+
+/*
+ * The following extract information from the drive configuration about
+ * the drive itself.
+ */
+#define SONY_HWC_GET_LOAD_MECH(c)	(c.hw_config[0] & 0x03)
+#define SONY_HWC_EJECT(c)		(c.hw_config[0] & 0x04)
+#define SONY_HWC_LED_SUPPORT(c)		(c.hw_config[0] & 0x08)
+#define SONY_HWC_GET_BUF_MEM_SIZE(c)	((c.hw_config[0] & 0xc0) >> 6)
+#define SONY_HWC_AUDIO_PLAYBACK(c)	(c.hw_config[1] & 0x01)
+#define SONY_HWC_ELECTRIC_VOLUME(c)	(c.hw_config[1] & 0x02)
+#define SONY_HWC_ELECTRIC_VOLUME_CTL(c)	(c.hw_config[1] & 0x04)
+
+#define SONY_HWC_CADDY_LOAD_MECH	0x00
+#define SONY_HWC_TRAY_LOAD_MECH		0x01
+#define SONY_HWC_POPUP_LOAD_MECH	0x02
+#define SONY_HWC_UNKWN_LOAD_MECH	0x03
+
+#define SONY_HWC_8KB_BUFFER		0x00
+#define SONY_HWC_32KB_BUFFER		0x01
+#define SONY_HWC_64KB_BUFFER		0x02
+#define SONY_HWC_UNKWN_BUFFER		0x03
+
+/*
+ * This is the complete status returned from the drive configuration request
+ * command.
+ */
+struct s_sony_drive_config
+{
+   unsigned char exec_status[2];
+   char vendor_id[8];
+   char product_id[16];
+   char product_rev_level[8];
+   unsigned char hw_config[2];
+};
+
+/* The following is returned from the request subcode address command */
+struct s_sony_subcode
+{
+   unsigned char exec_status[2];
+   unsigned char address	:4;
+   unsigned char control	:4;
+   unsigned char track_num;
+   unsigned char index_num;
+   unsigned char rel_msf[3];
+   unsigned char reserved1;
+   unsigned char abs_msf[3];
+};
+
+/*
+ * The following is returned from the request TOC (Table Of Contents) command.
+ * (last_track_num-first_track_num+1) values are valid in tracks.
+ */
+struct s_sony_toc
+{
+   unsigned char exec_status[2];
+   unsigned char address0	:4;
+   unsigned char control0	:4;
+   unsigned char point0;
+   unsigned char first_track_num;
+   unsigned char disk_type;
+   unsigned char dummy0;
+   unsigned char address1	:4;
+   unsigned char control1	:4;
+   unsigned char point1;
+   unsigned char last_track_num;
+   unsigned char dummy1;
+   unsigned char dummy2;
+   unsigned char address2	:4;
+   unsigned char control2	:4;
+   unsigned char point2;
+   unsigned char lead_out_start_msf[3];
+   struct
+   {
+      unsigned char address	:4;
+      unsigned char control	:4;
+      unsigned char track;
+      unsigned char track_start_msf[3];
+   } tracks[100];
+
+   unsigned int lead_out_start_lba;
+};
+
+
+/*
+ * The following are errors returned from the drive.
+ */
+
+/* Command error group */
+#define SONY_ILL_CMD_ERR		0x10
+#define SONY_ILL_PARAM_ERR		0x11
+
+/* Mechanism group */
+#define SONY_NOT_LOAD_ERR		0x20
+#define SONY_NO_DISK_ERR		0x21
+#define SONY_NOT_SPIN_ERR		0x22
+#define SONY_SPIN_ERR			0x23
+#define SONY_SPINDLE_SERVO_ERR		0x25
+#define SONY_FOCUS_SERVO_ERR		0x26
+#define SONY_EJECT_MECH_ERR		0x29
+#define SONY_AUDIO_PLAYING_ERR		0x2a
+#define SONY_EMERGENCY_EJECT_ERR	0x2c
+
+/* Seek error group */
+#define SONY_FOCUS_ERR			0x30
+#define SONY_FRAME_SYNC_ERR		0x31
+#define SONY_SUBCODE_ADDR_ERR		0x32
+#define SONY_BLOCK_SYNC_ERR		0x33
+#define SONY_HEADER_ADDR_ERR		0x34
+
+/* Read error group */
+#define SONY_ILL_TRACK_R_ERR		0x40
+#define SONY_MODE_0_R_ERR		0x41
+#define SONY_ILL_MODE_R_ERR		0x42
+#define SONY_ILL_BLOCK_SIZE_R_ERR	0x43
+#define SONY_MODE_R_ERR			0x44
+#define SONY_FORM_R_ERR			0x45
+#define SONY_LEAD_OUT_R_ERR		0x46
+#define SONY_BUFFER_OVERRUN_R_ERR	0x47
+
+/* Data error group */
+#define SONY_UNREC_CIRC_ERR		0x53
+#define SONY_UNREC_LECC_ERR		0x57
+
+/* Subcode error group */
+#define SONY_NO_TOC_ERR			0x60
+#define SONY_SUBCODE_DATA_NVAL_ERR	0x61
+#define SONY_FOCUS_ON_TOC_READ_ERR	0x63
+#define SONY_FRAME_SYNC_ON_TOC_READ_ERR	0x64
+#define SONY_TOC_DATA_ERR		0x65
+
+/* Hardware failure group */
+#define SONY_HW_FAILURE_ERR		0x70
+#define SONY_LEAD_IN_A_ERR		0x91
+#define SONY_LEAD_OUT_A_ERR		0x92
+#define SONY_DATA_TRACK_A_ERR		0x93
+
+/*
+ * The following are returned from the Read With Block Error Status command.
+ * They are not errors but information (Errors from the 0x5x group above may
+ * also be returned
+ */
+#define SONY_NO_CIRC_ERR_BLK_STAT	0x50
+#define SONY_NO_LECC_ERR_BLK_STAT	0x54
+#define SONY_RECOV_LECC_ERR_BLK_STAT	0x55
+#define SONY_NO_ERR_DETECTION_STAT	0x59
+
+/* 
+ * The following is not an error returned by the drive, but by the code
+ * that talks to the drive.  It is returned because of a timeout.
+ */
+#define SONY_TIMEOUT_OP_ERR		0x01
+#define SONY_SIGNAL_OP_ERR		0x02
+
+
+/*
+ * The following are attention code for asyncronous events from the drive.
+ */
+
+/* Standard attention group */
+#define SONY_EMER_EJECT_ATTN		0x2c
+#define SONY_HW_FAILURE_ATTN		0x70
+#define SONY_MECH_LOADED_ATTN		0x80
+#define SONY_EJECT_PUSHED_ATTN		0x81
+
+/* Audio attention group */
+#define SONY_AUDIO_PLAY_DONE_ATTN	0x90
+#define SONY_LEAD_IN_ERR_ATTN	 	0x91
+#define SONY_LEAD_OUT_ERR_ATTN	 	0x92
+#define SONY_DATA_TRACK_ERR_ATTN	0x93
+#define SONY_AUDIO_PLAYBACK_ERR_ATTN	0x94
+
+/* Auto spin up group */
+#define SONY_SPIN_UP_COMPLETE_ATTN	0x24
+#define SONY_SPINDLE_SERVO_ERR_ATTN	0x25
+#define SONY_FOCUS_SERVO_ERR_ATTN	0x26
+#define SONY_TOC_READ_DONE_ATTN		0x62
+#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN	0x63
+#define SONY_SYNC_ON_TOC_READ_ERR_ATTN	0x65
+
+/* Auto eject group */
+#define SONY_SPIN_DOWN_COMPLETE_ATTN	0x27
+#define SONY_EJECT_COMPLETE_ATTN	0x28
+#define SONY_EJECT_MECH_ERR_ATTN	0x29
diff --git a/include/linux/config.h b/include/linux/config.h
index b21ca68..81fe479 100644
--- a/include/linux/config.h
+++ b/include/linux/config.h
@@ -13,9 +13,7 @@
 #define UTS_NODENAME "(none)"	/* set by sethostname() */
 #endif
 
-#ifdef CONFIG_M486
-#define UTS_MACHINE "i486"	/* hardware type */
-#else
+#ifndef UTS_MACHINE
 #define UTS_MACHINE "i386"	/* hardware type */
 #endif
 
diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h
index 93d458c..00c9806 100644
--- a/include/linux/ext2_fs.h
+++ b/include/linux/ext2_fs.h
@@ -28,8 +28,8 @@
 /*
  * The second extended file system version
  */
-#define EXT2FS_DATE	"93/06/06"
-#define EXT2FS_VERSION	"0.3a"
+#define EXT2FS_DATE		"93/08/05"
+#define EXT2FS_VERSION		"0.3c"
 
 /*
  * Special inodes numbers
@@ -38,6 +38,7 @@
 #define EXT2_ROOT_INO		 2	/* Root inode */
 #define EXT2_ACL_IDX_INO	 3	/* ACL inode */
 #define EXT2_ACL_DATA_INO	 4	/* ACL inode */
+#define EXT2_BOOT_LOADER_INO	 5	/* Boot loader inode */
 #define EXT2_FIRST_INO		11	/* First non reserved inode */
 
 /*
@@ -65,7 +66,7 @@
 #define EXT2_ACLE_PER_BLOCK(s)		(EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry))
 #define	EXT2_ADDR_PER_BLOCK(s)		(EXT2_BLOCK_SIZE(s) / sizeof (unsigned long))
 #ifdef __KERNEL__
-# define EXT2_BLOCK_SIZE_BITS(s)	((s)->u.ext2_sb.s_log_block_size + 10)
+# define EXT2_BLOCK_SIZE_BITS(s)	((s)->u.ext2_sb.s_es->s_log_block_size + 10)
 #else
 # define EXT2_BLOCK_SIZE_BITS(s)	((s)->s_log_block_size + 10)
 #endif
@@ -98,12 +99,12 @@
 
 struct ext2_acl_entry	/* Access Control List Entry */
 {
-	unsigned long acle_size;
+	unsigned long  acle_size;
 	unsigned short acle_perms;	/* Access permissions */
 	unsigned short acle_type;	/* Type of entry */
 	unsigned short acle_tag;	/* User or group identity */
 	unsigned short acle_pad1;
-	unsigned long acle_next;	/* Pointer on next entry for the */
+	unsigned long  acle_next;	/* Pointer on next entry for the */
 					/* same inode or on next free entry */
 };
 
@@ -112,23 +113,23 @@
  */
 struct ext2_old_group_desc
 {
-	unsigned long bg_block_bitmap;		/* Blocks bitmap block */
-	unsigned long bg_inode_bitmap;		/* Inodes bitmap block */
-	unsigned long bg_inode_table;		/* Inodes table block */
+	unsigned long  bg_block_bitmap;		/* Blocks bitmap block */
+	unsigned long  bg_inode_bitmap;		/* Inodes bitmap block */
+	unsigned long  bg_inode_table;		/* Inodes table block */
 	unsigned short bg_free_blocks_count;	/* Free blocks count */
 	unsigned short bg_free_inodes_count;	/* Free inodes count */
 };
 
 struct ext2_group_desc
 {
-	unsigned long bg_block_bitmap;		/* Blocks bitmap block */
-	unsigned long bg_inode_bitmap;		/* Inodes bitmap block */
-	unsigned long bg_inode_table;		/* Inodes table block */
+	unsigned long  bg_block_bitmap;		/* Blocks bitmap block */
+	unsigned long  bg_inode_bitmap;		/* Inodes bitmap block */
+	unsigned long  bg_inode_table;		/* Inodes table block */
 	unsigned short bg_free_blocks_count;	/* Free blocks count */
 	unsigned short bg_free_inodes_count;	/* Free inodes count */
 	unsigned short bg_used_dirs_count;	/* Directories count */
 	unsigned short bg_pad;
-	unsigned long bg_reserved[3];
+	unsigned long  bg_reserved[3];
 };
 
 /*
@@ -147,11 +148,11 @@
 /*
  * Constants relative to the data blocks
  */
-#define	EXT2_NDIR_BLOCKS	12
-#define	EXT2_IND_BLOCK		EXT2_NDIR_BLOCKS
-#define	EXT2_DIND_BLOCK		(EXT2_IND_BLOCK + 1)
-#define	EXT2_TIND_BLOCK		(EXT2_DIND_BLOCK + 1)
-#define	EXT2_N_BLOCKS		(EXT2_TIND_BLOCK + 1)
+#define	EXT2_NDIR_BLOCKS		12
+#define	EXT2_IND_BLOCK			EXT2_NDIR_BLOCKS
+#define	EXT2_DIND_BLOCK			(EXT2_IND_BLOCK + 1)
+#define	EXT2_TIND_BLOCK			(EXT2_DIND_BLOCK + 1)
+#define	EXT2_N_BLOCKS			(EXT2_TIND_BLOCK + 1)
 
 /*
  * Structure of an inode on the disk
@@ -159,50 +160,50 @@
 struct ext2_inode {
 	unsigned short i_mode;		/* File mode */
 	unsigned short i_uid;		/* Owner Uid */
-	unsigned long i_size;		/* Size in bytes */
-	unsigned long i_atime;		/* Access time */
-	unsigned long i_ctime;		/* Creation time */
-	unsigned long i_mtime;		/* Modification time */
-	unsigned long i_dtime;		/* Deletion Time */
+	unsigned long  i_size;		/* Size in bytes */
+	unsigned long  i_atime;		/* Access time */
+	unsigned long  i_ctime;		/* Creation time */
+	unsigned long  i_mtime;		/* Modification time */
+	unsigned long  i_dtime;		/* Deletion Time */
 	unsigned short i_gid;		/* Group Id */
 	unsigned short i_links_count;	/* Links count */
-	unsigned long i_blocks;		/* Blocks count */
-	unsigned long i_flags;		/* File flags */
-	unsigned long i_reserved1;
-	unsigned long i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
-	unsigned long i_version;	/* File version (for NFS) */
-	unsigned long i_file_acl;	/* File ACL */
-	unsigned long i_dir_acl;	/* Directory ACL */
-	unsigned long i_faddr;		/* Fragment address */
-	unsigned char i_frag;		/* Fragment number */
-	unsigned char i_fsize;		/* Fragment size */
+	unsigned long  i_blocks;	/* Blocks count */
+	unsigned long  i_flags;		/* File flags */
+	unsigned long  i_reserved1;
+	unsigned long  i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+	unsigned long  i_version;	/* File version (for NFS) */
+	unsigned long  i_file_acl;	/* File ACL */
+	unsigned long  i_dir_acl;	/* Directory ACL */
+	unsigned long  i_faddr;		/* Fragment address */
+	unsigned char  i_frag;		/* Fragment number */
+	unsigned char  i_fsize;		/* Fragment size */
 	unsigned short i_pad1;
-	unsigned long i_reserved2[2];
+	unsigned long  i_reserved2[2];
 };
 
 /*
  * Structure of the super block
  */
 struct ext2_super_block {
-	unsigned long s_inodes_count;	/* Inodes count */
-	unsigned long s_blocks_count;	/* Blocks count */
-	unsigned long s_r_blocks_count;	/* Reserved blocks count */
-	unsigned long s_free_blocks_count;/* Free blocks count */
-	unsigned long s_free_inodes_count;/* Free inodes count */
-	unsigned long s_first_data_block;/* First Data Block */
-	unsigned long s_log_block_size;	/* Block size */
-	long s_log_frag_size;		/* Fragment size */
-	unsigned long s_blocks_per_group;/* # Blocks per group */
-	unsigned long s_frags_per_group;/* # Fragments per group */
-	unsigned long s_inodes_per_group;/* # Inodes per group */
-	unsigned long s_mtime;		/* Mount time */
-	unsigned long s_wtime;		/* Write time */
-	unsigned long s_pad;		/* Padding to get the magic signature*/
+	unsigned long  s_inodes_count;	/* Inodes count */
+	unsigned long  s_blocks_count;	/* Blocks count */
+	unsigned long  s_r_blocks_count;/* Reserved blocks count */
+	unsigned long  s_free_blocks_count;/* Free blocks count */
+	unsigned long  s_free_inodes_count;/* Free inodes count */
+	unsigned long  s_first_data_block;/* First Data Block */
+	unsigned long  s_log_block_size;/* Block size */
+	long           s_log_frag_size;	/* Fragment size */
+	unsigned long  s_blocks_per_group;/* # Blocks per group */
+	unsigned long  s_frags_per_group;/* # Fragments per group */
+	unsigned long  s_inodes_per_group;/* # Inodes per group */
+	unsigned long  s_mtime;		/* Mount time */
+	unsigned long  s_wtime;		/* Write time */
+	unsigned long  s_pad;		/* Padding to get the magic signature*/
 					/* at the same offset as in the */
 					/* previous ext fs */
 	unsigned short s_magic;		/* Magic signature */
 	unsigned short s_valid;		/* Flag */
-	unsigned long s_reserved[243];	/* Padding to the end of the block */
+	unsigned long  s_reserved[243];	/* Padding to the end of the block */
 };
 
 /*
@@ -211,10 +212,10 @@
 #define EXT2_NAME_LEN 255
 
 struct ext2_dir_entry {
-	unsigned long inode;		/* Inode number */
-	unsigned short rec_len;		/* Directory entry length */
-	unsigned short name_len;	/* Name length */
-	char name[EXT2_NAME_LEN];	/* File name */
+	unsigned long  inode;			/* Inode number */
+	unsigned short rec_len;			/* Directory entry length */
+	unsigned short name_len;		/* Name length */
+	char           name[EXT2_NAME_LEN];	/* File name */
 };
 
 /*
@@ -227,6 +228,7 @@
 #define EXT2_DIR_REC_LEN(name_len)	(((name_len) + 8 + EXT2_DIR_ROUND) & \
 					 ~EXT2_DIR_ROUND)
 
+#ifdef __KERNEL__
 /*
  * Function prototypes
  */
@@ -261,7 +263,7 @@
 extern int ext2_write (struct inode *, struct file *, char *, int);
 
 /* fsync.c */
-extern int ext2_sync_file(struct inode *, struct file *);
+extern int ext2_sync_file (struct inode *, struct file *);
 
 /* ialloc.c */
 extern struct inode * ext2_new_inode (const struct inode *, int);
@@ -320,3 +322,5 @@
 extern struct inode_operations ext2_symlink_inode_operations;
 
 #endif
+
+#endif
diff --git a/include/linux/ext2_fs_i.h b/include/linux/ext2_fs_i.h
index c3bb5c1..ffb1ba6 100644
--- a/include/linux/ext2_fs_i.h
+++ b/include/linux/ext2_fs_i.h
@@ -5,19 +5,19 @@
  * second extended file system inode data in memory
  */
 struct ext2_inode_info {
-	unsigned long i_data[15];
-	unsigned long i_flags;
-	unsigned long i_faddr;
-	unsigned char i_frag;
-	unsigned char i_fsize;
+	unsigned long  i_data[15];
+	unsigned long  i_flags;
+	unsigned long  i_faddr;
+	unsigned char  i_frag;
+	unsigned char  i_fsize;
 	unsigned short i_pad1;
-	unsigned long i_file_acl;
-	unsigned long i_dir_acl;
-	unsigned long i_dtime;
-	unsigned long i_version;
-	unsigned long i_block_group;
-	unsigned long i_next_alloc_block;
-	unsigned long i_next_alloc_goal;
+	unsigned long  i_file_acl;
+	unsigned long  i_dir_acl;
+	unsigned long  i_dtime;
+	unsigned long  i_version;
+	unsigned long  i_block_group;
+	unsigned long  i_next_alloc_block;
+	unsigned long  i_next_alloc_goal;
 };
 
 #endif
diff --git a/include/linux/ext2_fs_sb.h b/include/linux/ext2_fs_sb.h
index 59870b1..f004e14 100644
--- a/include/linux/ext2_fs_sb.h
+++ b/include/linux/ext2_fs_sb.h
@@ -8,12 +8,6 @@
  * second extended-fs super-block data in memory
  */
 struct ext2_sb_info {
-	unsigned long s_inodes_count;	/* Inodes count */
-	unsigned long s_blocks_count;	/* Blocks count */
-	unsigned long s_r_blocks_count;	/* Reserved blocks count */
-	unsigned long s_first_data_block;/* First data block */
-	unsigned long s_log_block_size;	/* Log of block size */
-	long s_log_frag_size;		/* Log of fragment size */
 	unsigned long s_frag_size;	/* Size of a fragment in bytes */
 	unsigned long s_frags_per_block;/* Number of fragments per block */
 	unsigned long s_inodes_per_block;/* Number of inodes per block */
@@ -23,6 +17,7 @@
 	unsigned long s_desc_per_block;	/* Number of group descriptors per block */
 	unsigned long s_groups_count;	/* Number of groups in the fs */
 	struct buffer_head * s_sbh;	/* Buffer containing the super block */
+	struct ext2_super_block * s_es;	/* Pointer to the super block in the buffer */
 	struct buffer_head * s_group_desc[EXT2_MAX_GROUP_DESC];
 	unsigned short s_loaded_inode_bitmaps;
 	unsigned short s_loaded_block_bitmaps;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index e25ceab..dada0e8 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -28,7 +28,7 @@
 #define NR_SUPER 32
 #define NR_HASH 997
 #define NR_IHASH 131
-#define NR_FILE_LOCKS 32
+#define NR_FILE_LOCKS 64
 #define BLOCK_SIZE 1024
 #define BLOCK_SIZE_BITS 10
 #define MAX_CHRDEV 32
@@ -124,6 +124,12 @@
 #define BLKROGET 4702 /* get read-only status (0 = read_write) */
 #define BLKRRPART 4703 /* re-read partition table */
 #define BLKGETSIZE 4704 /* return device size */
+#define BLKFLSBUF 4705 /* flush buffer cache */
+
+/* These are a few other constants  only used by scsi  devices */
+
+#define SCSI_IOCTL_GET_IDLUN 0x5382
+
 
 #define BMAP_IOCTL 1	/* obsolete - kept for compatibility */
 #define FIBMAP	   1	/* bmap access */
diff --git a/include/linux/ioctl.h b/include/linux/ioctl.h
index 9ecbca4..ec03d79 100644
--- a/include/linux/ioctl.h
+++ b/include/linux/ioctl.h
@@ -1,4 +1,4 @@
-/* $Id: ioctl.h,v 1.2 1992/11/18 01:31:16 root Exp root $
+/* $Id: ioctl.h,v 1.5 1993/07/19 21:53:50 root Exp root $
  *
  * linux/ioctl.h for Linux by H.H. Bergman.
  */
@@ -8,11 +8,13 @@
 
 
 /* ioctl command encoding: 32 bits total, command in lower 16 bits,
- * size of the parameter structure in the upper 14 bits.
- * Encoding size in ioctl request is useful for catching old versions
+ * size of the parameter structure in the lower 14 bits of the
+ * upper 16 bits.
+ * Encoding the size of the parameter structure in the ioctl request
+ * is useful for catching programs compiled with old versions
  * and to avoid overwriting user space outside the user buffer area.
- * The highest 2 bits are reserved.
- * NOTE: This limits the max blocksize to 16kB -1 !
+ * The highest 2 bits are reserved for indicating the ``access mode''.
+ * NOTE: This limits the max parameter size to 16kB -1 !
  */
 
 #define IOC_VOID	0x00000000	/* param in size field */
@@ -24,17 +26,22 @@
 #define IOCCMD_MASK	0x0000ffff	/* command code */
 #define IOCCMD_SHIFT	0
 
-#define _IO(c,d)	(IOC_VOID | ((d)<<16) | c) /* param encoded */
-/* use _IOXX(magic, subcode, arg_t) where arg_t is the type of the
+
+/* _IO(magic, subcode); size field is zero and the 
+ * subcode determines the command.
+ */
+#define _IO(c,d)	(IOC_VOID | ((c)<<8) | (d)) /* param encoded */
+
+/* _IOXX(magic, subcode, arg_t); where arg_t is the type of the
  * (last) argument field in the ioctl call, if present.
  */
 #define _IOW(c,d,t)	(IOC_IN | ((sizeof(t)<<16) & IOCSIZE_MASK) | \
-				  (c<<8) | d)
+				  ((c)<<8) | (d))
 #define _IOR(c,d,t)	(IOC_OUT | ((sizeof(t)<<16) & IOCSIZE_MASK) | \
-				   (c<<8) | d)
+				   ((c)<<8) | (d))
 /* WR rather than RW to avoid conflict with stdio.h */
 #define _IOWR(c,d,t)	(IOC_INOUT | ((sizeof(t)<<16) & IOCSIZE_MASK) | \
-				     (c<<8) | d)
+				     ((c)<<8) | (d))
 
 #endif /* _LINUX_IOCTL_H */
 
diff --git a/include/linux/kd.h b/include/linux/kd.h
index 9ef695a..2f8c740 100644
--- a/include/linux/kd.h
+++ b/include/linux/kd.h
@@ -176,4 +176,11 @@
 #define KDGKBENT	0x4B46	/* gets one entry in translation table */
 #define KDSKBENT	0x4B47	/* sets one entry in translation table */
 
+struct kbsentry {
+	u_char kb_func;
+	u_char kb_string[512];	/* FUNC_BUFSIZE from keyboard.h */
+};
+#define KDGKBSENT	0x4B48	/* gets one function key string entry */
+#define KDSKBSENT	0x4B49	/* sets one function key string entry */
+
 #endif /* _LINUX_KD_H */
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index c0a86d1..69b1899 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -9,6 +9,11 @@
 
 #include <linux/config.h>
 
+#define INT_MAX		((int)(~0U>>1))
+#define UINT_MAX	(~0U)
+#define LONG_MAX	((long)(~0UL>>1))
+#define ULONG_MAX	(~0UL)
+
 #define VERIFY_READ 0
 #define VERIFY_WRITE 1
 
diff --git a/include/linux/keyboard.h b/include/linux/keyboard.h
index 51e2aee..5151bd3 100644
--- a/include/linux/keyboard.h
+++ b/include/linux/keyboard.h
@@ -5,31 +5,6 @@
 #define set_leds() mark_bh(KEYBOARD_BH)
 
 /*
- * Global flags: things that don't change between virtual consoles.
- * This includes things like "key-down" flags - if the shift key is
- * down when you change a console, it's down in both.
- *
- * Note that the KG_CAPSLOCK flags is NOT the flag that decides if
- * capslock is on or not: it's just a flag about the key being
- * physically down. The actual capslock status is in the local flags.
- */
-extern unsigned long kbd_flags;
-
-/*
- * These are the hardcoded global flags - use the numbers beyond 16
- * for non-standard or keyboard-dependent flags
- */
-#define KG_LSHIFT	0
-#define KG_RSHIFT	1
-#define KG_LCTRL	2
-#define KG_RCTRL	3
-#define KG_LALT		4
-#define KG_RALT		5	/* doesn't exist, but.. */
-#define KG_LALTGR	6	/* doesn't exist, but.. */
-#define KG_RALTGR	7
-#define KG_CAPSLOCK	8
-
-/*
  * "dead" keys - prefix key values that are valid only for the next
  * character code (sticky shift, E0/E1 special scancodes, diacriticals)
  */
@@ -72,26 +47,6 @@
 
 extern unsigned long kbd_init(unsigned long);
 
-extern inline int kbd_flag(int flag)
-{
-	return kbd_flags & (1 << flag);
-}
-
-extern inline void set_kbd_flag(int flag)
-{
-	kbd_flags |= 1 << flag;
-}
-
-extern inline void clr_kbd_flag(int flag)
-{
-	kbd_flags &= ~(1 << flag);
-}
-
-extern inline void chg_kbd_flag(int flag)
-{
-	kbd_flags ^= 1 << flag;
-}
-
 extern inline int kbd_dead(int flag)
 {
 	return kbd_prev_dead_keys & (1 << flag);
@@ -132,12 +87,17 @@
 	kbd->flags ^= 1 << flag;
 }
 
-#define NR_KEYS 112
-#define NR_KEYMAPS 3
+#define NR_KEYS 128
+#define NR_KEYMAPS 16
 extern const int NR_TYPES;
 extern const int max_vals[];
 extern unsigned short key_map[NR_KEYMAPS][NR_KEYS];
 
+#define NR_FUNC 32
+#define FUNC_BUFSIZE 512
+extern char func_buf[FUNC_BUFSIZE];
+extern char *func_table[NR_FUNC];
+
 #define KT_LATIN	0	/* we depend on this being zero */
 #define KT_FN		1
 #define KT_SPEC		2
@@ -146,6 +106,8 @@
 #define KT_CONS		5
 #define KT_CUR		6
 #define KT_SHIFT	7
+#define KT_META		8
+#define KT_ASCII	9
 
 #define K(t,v)		(((t)<<8)|(v))
 #define KTYP(x)		((x) >> 8)
@@ -188,6 +150,10 @@
 #define K_CAPS		K(KT_SPEC,7)
 #define K_NUM		K(KT_SPEC,8)
 #define K_HOLD		K(KT_SPEC,9)
+#define K_SCROLLFORW	K(KT_SPEC,10)
+#define K_SCROLLBACK	K(KT_SPEC,11)
+#define K_BOOT		K(KT_SPEC,12)
+#define K_CAPSON	K(KT_SPEC,13)
 
 #define K_P0		K(KT_PAD,0)
 #define K_P1		K(KT_PAD,1)
@@ -218,16 +184,29 @@
 #define K_RIGHT		K(KT_CUR,2)
 #define K_UP		K(KT_CUR,3)
 
-#define K_LSHIFT	K(KT_SHIFT,KG_LSHIFT)
-#define K_RSHIFT	K(KT_SHIFT,KG_RSHIFT)
-#define K_LCTRL		K(KT_SHIFT,KG_LCTRL)
-#define K_RCTRL		K(KT_SHIFT,KG_RCTRL)
-#define K_LALT		K(KT_SHIFT,KG_LALT)
-#define K_RALT		K(KT_SHIFT,KG_RALT)
-#define K_LALTGR	K(KT_SHIFT,KG_LALTGR)
-#define K_RALTGR	K(KT_SHIFT,KG_RALTGR)
+#define KG_SHIFT	0
+#define KG_CTRL		2
+#define KG_ALT		3
+#define KG_ALTGR	1
 
-#define K_ALT		K_LALT
-#define K_ALTGR		K_RALTGR
+#define K_SHIFT		K(KT_SHIFT,KG_SHIFT)
+#define K_CTRL		K(KT_SHIFT,KG_CTRL)
+#define K_ALT		K(KT_SHIFT,KG_ALT)
+#define K_ALTGR		K(KT_SHIFT,KG_ALTGR)
+
+#define NR_SHIFT	16
+
+#define K_CAPSSHIFT	K(KT_SHIFT,NR_SHIFT)
+
+#define K_ASC0		K(KT_ASCII,0)
+#define K_ASC1		K(KT_ASCII,1)
+#define K_ASC2		K(KT_ASCII,2)
+#define K_ASC3		K(KT_ASCII,3)
+#define K_ASC4		K(KT_ASCII,4)
+#define K_ASC5		K(KT_ASCII,5)
+#define K_ASC6		K(KT_ASCII,6)
+#define K_ASC7		K(KT_ASCII,7)
+#define K_ASC8		K(KT_ASCII,8)
+#define K_ASC9		K(KT_ASCII,9)
 
 #endif
diff --git a/include/linux/ldt.h b/include/linux/ldt.h
new file mode 100644
index 0000000..7ab13e8
--- /dev/null
+++ b/include/linux/ldt.h
@@ -0,0 +1,28 @@
+/*
+ * ldt.h
+ *
+ * Definitions of structures used with the modify_ldt system call.
+ */
+#ifndef _LINUX_LDT_H
+#define _LINUX_LDT_H
+
+struct modify_ldt_ldt_s {
+	unsigned int  entry_number;
+	unsigned long base_addr;
+	unsigned int  limit;
+	unsigned int  seg_32bit:1;
+	unsigned int  contents:2;
+	unsigned int  read_exec_only:1;
+	unsigned int  limit_in_pages:1;
+};
+
+#define MODIFY_LDT_CONTENTS_DATA	0
+#define MODIFY_LDT_CONTENTS_STACK	1
+#define MODIFY_LDT_CONTENTS_CODE	2
+
+extern int get_ldt(void *buffer);
+extern int set_ldt_entry(int entry, unsigned long base, unsigned int limit,
+			 int seg_32bit_flag, int contents, int read_only_flag,
+			 int limit_in_pages_flag);
+
+#endif
diff --git a/include/linux/mcd.h b/include/linux/mcd.h
new file mode 100644
index 0000000..4736f2a
--- /dev/null
+++ b/include/linux/mcd.h
@@ -0,0 +1,101 @@
+/*
+ * Definitions for a Mitsumi CD-ROM interface
+ *
+ *	Copyright (C) 1992  Martin Harriss
+ *
+ *	martin@bdsi.com
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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.
+ *
+ */
+
+/* *** change this to set the I/O port address */
+#define MCDPORT(x)		(0x320 + (x))
+
+/* *** change this to set the interrupt number */
+#define MCD_INTR_NR		11
+
+
+
+
+
+/* status bits */
+
+#define MST_CMD_CHECK		0x01		/* command error */
+#define MST_BUSY		0x02		/* now playing */
+#define MST_READ_ERR		0x04		/* read error */
+#define MST_DSK_TYPE		0x08
+#define MST_SERVO_CHECK		0x10
+#define MST_DSK_CHG		0x20		/* disk removed or changed */
+#define MST_READY		0x40		/* disk in the drive */
+#define MST_DOOR_OPEN		0x80		/* door is open */
+
+/* flag bits */
+
+#define MFL_DATA		0x02		/* data available */
+#define MFL_STATUS		0x04		/* status available */
+
+/* commands */
+
+#define MCMD_GET_DISK_INFO	0x10		/* read info from disk */
+#define MCMD_GET_Q_CHANNEL	0x20		/* read info from q channel */
+#define MCMD_GET_STATUS		0x40
+#define MCMD_SET_MODE		0x50
+#define MCMD_SOFT_RESET		0x60
+#define MCMD_STOP		0x70		/* stop play */
+#define MCMD_CONFIG_DRIVE	0x90
+#define MCMD_SET_VOLUME		0xAE		/* set audio level */
+#define MCMD_PLAY_READ		0xC0		/* play or read data */
+#define MCMD_GET_VERSION  	0xDC
+
+/* borrowed from hd.c */
+
+#define READ_DATA(port, buf, nr) \
+__asm__("cld;rep;insb": :"d" (port),"D" (buf),"c" (nr):"cx","di")
+
+#define SET_TIMER(func, jifs) \
+	((timer_table[MCD_TIMER].expires = jiffies + jifs), \
+	(timer_table[MCD_TIMER].fn = func), \
+	(timer_active |= 1<<MCD_TIMER))
+
+#define CLEAR_TIMER		timer_active &= ~(1<<MCD_TIMER)
+
+#define MAX_TRACKS		104
+
+struct msf {
+	unsigned char	min;
+	unsigned char	sec;
+	unsigned char	frame;
+};
+
+struct mcd_Play_msf {
+	struct msf	start;
+	struct msf	end;
+};
+
+struct mcd_DiskInfo {
+	unsigned char	first;
+	unsigned char	last;
+	struct msf	diskLength;
+	struct msf	firstTrack;
+};
+
+struct mcd_Toc {
+	unsigned char	ctrl_addr;
+	unsigned char	track;
+	unsigned char	pointIndex;
+	struct msf	trackTime;
+	struct msf	diskTime;
+};
diff --git a/include/linux/mm.h b/include/linux/mm.h
index a225b29..ff66453 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1,9 +1,7 @@
 #ifndef _LINUX_MM_H
 #define _LINUX_MM_H
 
-#define PAGE_SIZE 4096
-#define PAGE_SHIFT 12
-
+#include <linux/page.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
 
diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h
index 3e3197e..756abaa 100644
--- a/include/linux/msdos_fs.h
+++ b/include/linux/msdos_fs.h
@@ -103,11 +103,11 @@
 
 /* Convert attribute bits and a mask to the UNIX mode. */
 
-#define MSDOS_MKMODE(a,m) (m & (a & ATTR_RO ? 0555 : 0777))
+#define MSDOS_MKMODE(a,m) (m & (a & ATTR_RO ? S_IRUGO|S_IXUGO : S_IRWXUGO))
 
 /* Convert the UNIX mode to MS-DOS attribute bits. */
 
-#define MSDOS_MKATTR(m) ((m & 0200) ? ATTR_NONE : ATTR_RO)
+#define MSDOS_MKATTR(m) ((m & S_IWUGO) ? ATTR_NONE : ATTR_RO)
 
 
 static inline struct buffer_head *msdos_sread(int dev,int sector,void **start)
diff --git a/include/linux/mtio.h b/include/linux/mtio.h
index 990f830..446667e 100644
--- a/include/linux/mtio.h
+++ b/include/linux/mtio.h
@@ -48,6 +48,8 @@
 #define MTSETDENSITY 21	/* set tape density (SCSI) */
 #define MTSEEK	22	/* seek to block (Tandberg, etc.) */
 #define MTTELL	23	/* tell block (Tandber, etc.) */
+#define MTSETDRVBUFFER 24 /* set the drive buffering according to SCSI-2 */
+			/* ordinary buffered operation with code 1 */
 
 
 /* structure for MTIOCGET - mag tape get status command */
diff --git a/include/linux/nfs.h b/include/linux/nfs.h
index 60bc293..cc290ef 100644
--- a/include/linux/nfs.h
+++ b/include/linux/nfs.h
@@ -8,7 +8,7 @@
 #define NFS_MAXGROUPS 16
 #define NFS_FHSIZE 32
 #define NFS_COOKIESIZE 4
-#define NFS_FIFO_DEV -1
+#define NFS_FIFO_DEV (-1)
 #define NFSMODE_FMT 0170000
 #define NFSMODE_DIR 0040000
 #define NFSMODE_CHR 0020000
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 1c5db08..367a9fb 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -116,4 +116,9 @@
 
 extern struct inode_operations nfs_symlink_inode_operations;
 
+/* linux/fs/nfs/mmap.c */
+
+extern int nfs_mmap(struct inode * inode, struct file * file,
+               unsigned long addr, size_t len, int prot, unsigned long off);
+
 #endif
diff --git a/include/linux/page.h b/include/linux/page.h
new file mode 100644
index 0000000..97796bd
--- /dev/null
+++ b/include/linux/page.h
@@ -0,0 +1,34 @@
+#ifndef _LINUX_PAGE_H
+#define _LINUX_PAGE_H
+
+			/* PAGE_SHIFT determines the page size */
+#define PAGE_SHIFT			12
+#define PAGE_SIZE			((unsigned long)1<<PAGE_SHIFT)
+
+#ifdef __KERNEL__
+
+			/* number of bits that fit into a memory pointer */
+#define BITS_PER_PTR			(8*sizeof(unsigned long))
+			/* to mask away the intra-page address bits */
+#define PAGE_MASK			(~(PAGE_SIZE-1))
+			/* to align the pointer to the (next) page boundary */
+#define PAGE_ALIGN(addr)		(((addr)+PAGE_SIZE-1)&PAGE_MASK)
+			/* to align the pointer to a pointer address */
+#define PTR_MASK			(~(sizeof(void*)-1))
+
+					/* sizeof(void*)==1<<SIZEOF_PTR_LOG2 */
+					/* 64-bit machines, beware!  SRB. */
+#define SIZEOF_PTR_LOG2			2
+
+			/* to find an entry in a page-table-directory */
+#define PAGE_DIR_OFFSET(base,address)	((unsigned long*)((base)+\
+  ((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)*2&PTR_MASK&~PAGE_MASK)))
+			/* to find an entry in a page-table */
+#define PAGE_PTR(address)		\
+  ((unsigned long)(address)>>PAGE_SHIFT-SIZEOF_PTR_LOG2&PTR_MASK&~PAGE_MASK)
+			/* the no. of pointers that fit on a page */
+#define PTRS_PER_PAGE			(PAGE_SIZE/sizeof(void*))
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_PAGE_H */
diff --git a/include/linux/param.h b/include/linux/param.h
index ec03686..c634b1e 100644
--- a/include/linux/param.h
+++ b/include/linux/param.h
@@ -12,7 +12,7 @@
 #endif
 
 #ifndef NOGROUP
-#define NOGROUP		-1
+#define NOGROUP		(-1)
 #endif
 
 #define MAXHOSTNAMELEN	64	/* max length of hostname */
diff --git a/include/linux/resource.h b/include/linux/resource.h
index 6626c33..86c15c7 100644
--- a/include/linux/resource.h
+++ b/include/linux/resource.h
@@ -13,8 +13,8 @@
  * structure will lose.  This reduces the chances of that happening.
  */
 #define	RUSAGE_SELF	0
-#define	RUSAGE_CHILDREN	-1
-#define RUSAGE_BOTH	-2		/* sys_wait4() uses this */
+#define	RUSAGE_CHILDREN	(-1)
+#define RUSAGE_BOTH	(-2)		/* sys_wait4() uses this */
 
 struct	rusage {
 	struct timeval ru_utime;	/* user time used */
@@ -54,14 +54,14 @@
 
 #define RLIM_NLIMITS	6
 
-#define RLIM_INFINITY	0x7fffffff
+#define RLIM_INFINITY	LONG_MAX
 
 struct rlimit {
 	int	rlim_cur;
 	int	rlim_max;
 };
 
-#define	PRIO_MIN	-99
+#define	PRIO_MIN	(-99)
 #define	PRIO_MAX	14
 
 #define	PRIO_PROCESS	0
diff --git a/include/linux/sched.h b/include/linux/sched.h
index cf6473a..bda86e8 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1,6 +1,8 @@
 #ifndef _LINUX_SCHED_H
 #define _LINUX_SCHED_H
 
+#define NEW_SWAP
+
 /*
  * define DEBUG if you want the wait-queues to have some extra
  * debugging code. It's not normally used, but might catch some
@@ -165,7 +167,7 @@
 	int elf_executable:1;
 	int dumpable:1;
 	int swappable:1;
-	unsigned long start_code,end_code,end_data,brk,start_stack,start_mmap;
+	unsigned long start_code,end_code,end_data,start_brk,brk,start_stack,start_mmap;
 	unsigned long arg_start, arg_end, env_start, env_end;
 	long pid,pgrp,session,leader;
 	int	groups[NGROUPS];
@@ -206,10 +208,17 @@
 	struct sem_undo *semun;
 	struct file * filp[NR_OPEN];
 	fd_set close_on_exec;
-/* ldt for this task - not currently used */
-	struct desc_struct ldt[32];
+/* ldt for this task - used by Wine.  If NULL, default_ldt is used */
+	struct desc_struct *ldt;
 /* tss for this task */
 	struct tss_struct tss;
+#ifdef NEW_SWAP
+	unsigned long old_maj_flt;	/* old value of maj_flt */
+	unsigned long dec_flt;		/* page fault count of the last time */
+	unsigned long swap_cnt;		/* number of pages to swap on next pass */
+	short swap_table;		/* current page table */
+	short swap_page;		/* current page */
+#endif NEW_SWAP
 };
 
 /*
@@ -236,7 +245,7 @@
 /* schedlink */	&init_task,&init_task, \
 /* signals */	{{ 0, },}, \
 /* stack */	0,0, \
-/* ec,brk... */	0,0,0,0,0,0,0,0,0,0,0, \
+/* ec,brk... */	0,0,0,0,0,0,0,0,0,0,0,0, \
 /* argv.. */	0,0,0,0, \
 /* pid etc.. */	0,0,0,0, \
 /* suppl grps*/ {NOGROUP,}, \
@@ -244,9 +253,9 @@
 /* uid etc */	0,0,0,0,0,0, \
 /* timeout */	0,0,0,0,0,0,0,0,0,0,0,0, \
 /* min_flt */	0,0,0,0, \
-/* rlimits */   { {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff},  \
-		  {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \
-		  {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}}, \
+/* rlimits */   { {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX},  \
+		  {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX},  \
+		  {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}}, \
 /* math */	0, \
 /* rss */	2, \
 /* comm */	"swapper", \
@@ -255,9 +264,7 @@
 /* ipc */	NULL, NULL, \
 /* filp */	{NULL,}, \
 /* cloe */	{{ 0, }}, \
-		{ \
-/* ldt */		{0,0}, \
-		}, \
+/* ldt */	NULL, \
 /*tss*/	{0,0, \
 	 sizeof(init_kernel_stack) + (long) &init_kernel_stack, KERNEL_DS, 0, \
 	 0,0,0,0,0,0, \
@@ -266,7 +273,7 @@
 	 USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0, \
 	 _LDT(0),0, \
 	 0, 0x8000, \
-/* ioperm */ 	{0xffffffff, }, \
+/* ioperm */ 	{~0, }, \
 	 _TSS(0), \
 /* 387 state */	{ { 0, }, } \
 	} \
@@ -280,8 +287,11 @@
 extern unsigned long startup_time;
 extern int jiffies_offset;
 extern int need_resched;
+
 extern int hard_math;
+extern int x86;
 extern int ignore_irq13;
+extern int wp_works_ok;
 
 #define CURRENT_TIME (startup_time+(jiffies+jiffies_offset)/HZ)
 
@@ -345,8 +355,6 @@
 	 "c" (tsk) \
 	:"cx")
 
-#define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000)
-
 #define _set_base(addr,base) \
 __asm__("movw %%dx,%0\n\t" \
 	"rorl $16,%%edx\n\t" \
@@ -483,24 +491,39 @@
 	return __limit+1;
 }
 
-#define REMOVE_LINKS(p) \
+#define REMOVE_LINKS(p) do { unsigned long flags; \
+	save_flags(flags) ; cli(); \
 	(p)->next_task->prev_task = (p)->prev_task; \
 	(p)->prev_task->next_task = (p)->next_task; \
+	restore_flags(flags); \
 	if ((p)->p_osptr) \
 		(p)->p_osptr->p_ysptr = (p)->p_ysptr; \
 	if ((p)->p_ysptr) \
 		(p)->p_ysptr->p_osptr = (p)->p_osptr; \
 	else \
-		(p)->p_pptr->p_cptr = (p)->p_osptr
+		(p)->p_pptr->p_cptr = (p)->p_osptr; \
+	} while (0)
 
-#define SET_LINKS(p) \
+#define SET_LINKS(p) do { unsigned long flags; \
+	save_flags(flags); cli(); \
 	(p)->next_task = &init_task; \
 	(p)->prev_task = init_task.prev_task; \
 	init_task.prev_task->next_task = (p); \
 	init_task.prev_task = (p); \
+	restore_flags(flags); \
 	(p)->p_ysptr = NULL; \
 	if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \
 		(p)->p_osptr->p_ysptr = p; \
-	(p)->p_pptr->p_cptr = p
+	(p)->p_pptr->p_cptr = p; \
+	} while (0)
+
+#define for_each_task(p) \
+	for (p = &init_task ; (p = p->next_task) != &init_task ; )
+
+/*
+ * This is the ldt that every process will get unless we need
+ * something other than this.
+ */
+extern struct desc_struct default_ldt;
 
 #endif
diff --git a/include/linux/serial.h b/include/linux/serial.h
index db5fd47..18244b1 100644
--- a/include/linux/serial.h
+++ b/include/linux/serial.h
@@ -15,12 +15,15 @@
  *
  * For definitions of the flags field, see tty.h
  */
+#ifndef _LINUX_SERIAL_H
+#define _LINUX_SERIAL_H
 
 struct async_struct {
 	int			baud_base;
 	int			port;
 	int			irq;
 	int			flags; 		/* defined in tty.h */
+	int			hub6;		/* HUB6 plus one */
 	int			type; 		/* UART type */
 	struct tty_struct 	*tty;
 	int			read_status_mask;
@@ -29,11 +32,14 @@
 	int			custom_divisor;
 	int			x_char;	/* xon/xoff characater */
 	int			close_delay;
+	int			IER; 	/* Interrupt Enable Register */
 	int			event;
 	int			line;
 	int			count;	    /* # of fd on device */
 	int			blocked_open; /* # of blocked opens */
-	struct wait_queue *open_wait;
+	long			session; /* Session of opening process */
+	long			pgrp; /* pgrp of opening process */
+	struct wait_queue	*open_wait;
 	struct async_struct	*next_port; /* For the linked list */
 	struct async_struct	*prev_port;
 };
@@ -150,3 +156,5 @@
 #define UART_MSR_DDSR	0x02	/* Delta DSR */
 #define UART_MSR_DCTS	0x01	/* Delta CTS */
 #define UART_MSR_ANY_DELTA 0x0F	/* Any of the delta bits! */
+
+#endif /* _LINUX_SERIAL_H */
diff --git a/include/linux/shm.h b/include/linux/shm.h
index cd6c767..f61d6d6 100644
--- a/include/linux/shm.h
+++ b/include/linux/shm.h
@@ -34,13 +34,41 @@
     int shmall;	
 };
 
-#define SHMMAX 0x400000	 /* <= 4M */          /* max shared seg size (bytes) */
-#define SHMMIN 1	 /* really PAGE_SIZE */  /* min shared seg size (bytes)*/
-#define SHMMNI 128       /* <= 4096 */        /* max num of segs system wide */
-#define SHMALL 0x10000 /* <= SHMMAX*SHMMNI/PAGE_SIZE */  /* max shm system wide (pages) */
-#define	SHMLBA 0x1000    /* = PAGE_SIZE */   /*  attach addr multiple */
-#define SHMSEG SHMMNI    /* <= SHMMNI */    /* max shared segs per process */
+#define SHM_RANGE_START	0x40000000
+#define SHM_RANGE_END	0x60000000
 
+				/* _SHM_ID_BITS is a variable you can adjust to */
+				/* tune the kernel.  It determines the value of */
+				/* SHMMNI, which specifies the maximum no. of */
+				/* shared segments (system wide).  SRB. */
+#define _SHM_ID_BITS	7		/* keep as low as possible */
+					/* a static array is declared */
+					/* using SHMMNI */
+
+#define __SHM_IDX_BITS	(BITS_PER_PTR-2-SHM_IDX_SHIFT)
+
+/* !!!!!!!?????
+ * Why reserve the two (2) high bits of the signature (shm_sgn) field?
+ * Since, as far as I can see, only the high bit is used (SHM_READ_ONLY).
+ *						SRB.
+ */
+
+#define _SHM_IDX_BITS	(__SHM_IDX_BITS+PAGE_SHIFT>=BITS_PER_PTR?\
+ BITS_PER_PTR-PAGE_SHIFT-1:__SHM_IDX_BITS)	/* sanity check */
+
+/* not present page table entry format bit 0 is 0, low byte defined in mm.h */
+#define SHM_ID_SHIFT	8
+#define SHM_ID_MASK	((1<<_SHM_ID_BITS)-1)
+#define SHM_IDX_SHIFT	(SHM_ID_SHIFT+_SHM_ID_BITS)
+#define SHM_IDX_MASK	((1<<_SHM_IDX_BITS)-1)
+#define SHM_READ_ONLY	(1<<BITS_PER_PTR-1)
+
+#define SHMMAX (1<<PAGE_SHIFT+_SHM_IDX_BITS)	/* max shared seg size (bytes) */
+#define SHMMIN 1	 /* really PAGE_SIZE */	/* min shared seg size (bytes)*/
+#define SHMMNI (1<<_SHM_ID_BITS)		/* max num of segs system wide */
+#define SHMALL (1<<_SHM_IDX_BITS+_SHM_ID_BITS)	/* max shm system wide (pages) */
+#define	SHMLBA PAGE_SIZE			/* attach addr a multiple of this */
+#define SHMSEG SHMMNI				/* max shared segs per process */
 
 #ifdef __KERNEL__
 
@@ -74,13 +102,6 @@
 	struct shm_desc *seg_next;    /* next attach for segment */
 };
 
-/* not present page table entry format bit 0 is 0, high byte defined in mm.h */
-#define SHM_IDX_SHIFT 20
-#define SHM_IDX_MASK  0x3FF
-#define SHM_ID_SHIFT  8
-#define SHM_ID_MASK   0xFFF
-#define SHM_READ_ONLY 0x80000000
-
 #endif /* __KERNEL__ */
 
 #endif /* _LINUX_SHM_H_ */
diff --git a/include/linux/stat.h b/include/linux/stat.h
index 3d8b4a9..86fbd82 100644
--- a/include/linux/stat.h
+++ b/include/linux/stat.h
@@ -1,7 +1,7 @@
 #ifndef _LINUX_STAT_H
 #define _LINUX_STAT_H
 
-#ifndef __NOT_KERNEL
+#ifdef __KERNEL__
 
 struct old_stat {
 	unsigned short st_dev;
@@ -77,4 +77,12 @@
 #define S_IWOTH 00002
 #define S_IXOTH 00001
 
+#ifdef __KERNEL__
+#define S_IRWXUGO	(S_IRWXU|S_IRWXG|S_IRWXO)
+#define S_IALLUGO	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
+#define S_IRUGO		(S_IRUSR|S_IRGRP|S_IROTH)
+#define S_IWUGO		(S_IWUSR|S_IWGRP|S_IWOTH)
+#define S_IXUGO		(S_IXUSR|S_IXGRP|S_IXOTH)
+#endif
+
 #endif
diff --git a/include/linux/sys.h b/include/linux/sys.h
index 94635ee..7ff8045 100644
--- a/include/linux/sys.h
+++ b/include/linux/sys.h
@@ -129,6 +129,7 @@
 extern int sys_setdomainname();
 extern int sys_olduname();
 extern int sys_old_syscall();
+extern int sys_modify_ldt();
 
 /*
  * These are system calls that will be removed at some time
@@ -167,7 +168,7 @@
 sys_syslog, sys_setitimer, sys_getitimer, sys_newstat, sys_newlstat,
 sys_newfstat, sys_uname, sys_iopl, sys_vhangup, sys_idle, sys_vm86,
 sys_wait4, sys_swapoff, sys_sysinfo, sys_ipc, sys_fsync, sys_sigreturn,
-sys_clone, sys_setdomainname, sys_newuname};
+sys_clone, sys_setdomainname, sys_newuname, sys_modify_ldt};
 
 }
 
diff --git a/include/linux/termios.h b/include/linux/termios.h
index 5dcc243..8fd2bde 100644
--- a/include/linux/termios.h
+++ b/include/linux/termios.h
@@ -47,6 +47,8 @@
 #define TIOCSERCONFIG	0x5453
 #define TIOCSERGWILD	0x5454
 #define TIOCSERSWILD	0x5455
+#define TIOCGLCKTRMIOS	0x5456
+#define TIOCSLCKTRMIOS	0x5457
 
 /* Used for packet mode */
 #define TIOCPKT_FLUSHREAD	 1
diff --git a/include/linux/timer.h b/include/linux/timer.h
index dcca17f..4cf602f 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -26,6 +26,8 @@
  * COPRO_TIMER		387 timeout for buggy hardware..
  *
  * TAPE_QIC02_TIMER	timer for QIC-02 tape driver (it's not hardcoded)
+ *
+ * MCD_TIMER		Mitsumi CD-ROM Timer
  */
 
 #define BLANK_TIMER	0
@@ -40,6 +42,7 @@
 #define COPRO_TIMER	21
 
 #define TAPE_QIC02_TIMER	22	/* hhb */
+#define MCD_TIMER	23
 
 struct timer_struct {
 	unsigned long expires;
@@ -70,6 +73,6 @@
 };
 
 extern void add_timer(struct timer_list * timer);
-extern void del_timer(struct timer_list * timer);
+extern int  del_timer(struct timer_list * timer);
 
 #endif
diff --git a/include/linux/tty.h b/include/linux/tty.h
index f735fc9..5f43cb3 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -80,7 +80,8 @@
 	int	baud_base;
 	char	close_delay;
 	char	reserved_char[3];
-	int	reserved[6];
+	int	hub6;
+	int	reserved[5];
 };
 
 /*
@@ -99,6 +100,7 @@
 #define ASYNC_HUP_NOTIFY 0x0001 /* Notify blocked open on hangups */
 #define ASYNC_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
 #define ASYNC_SAK	0x0004	/* Secure Attention Key (Orange book) */
+#define ASYNC_TERMIOS_RESTORE 0x0008 /* Restore termios when dialin unblocks */
 
 #define ASYNC_SPD_MASK	0x0030
 #define ASYNC_SPD_HI	0x0010	/* Use 56000 instead of 38400 bps */
@@ -107,8 +109,13 @@
 
 #define ASYNC_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
 #define ASYNC_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ASYNC_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ASYNC_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
+#define ASYNC_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
 
-#define ASYNC_FLAGS	0x00F7	/* Possible legal async flags */
+#define ASYNC_FLAGS	0x0FFF	/* Possible legal async flags */
+#define ASYNC_USR_MASK 0x0430	/* Legal flags that non-privileged
+				 * users can set or reset */
 
 /* Internal flags used only by kernel/chr_drv/serial.c */
 #define ASYNC_INITIALIZED	0x80000000 /* Serial port was initialized */
@@ -223,6 +230,8 @@
 		    unsigned int cmd, unsigned long arg);
 	void (*throttle)(struct tty_struct * tty, int status);
 	void (*set_termios)(struct tty_struct *tty, struct termios * old);
+	void (*stop)(struct tty_struct *tty);
+	void (*start)(struct tty_struct *tty);
 	struct tty_struct *link;
 	unsigned char *write_data_ptr;
 	int write_data_cnt;
@@ -318,6 +327,8 @@
 extern void tty_read_flush(struct tty_struct *);
 
 extern struct tty_struct *tty_table[];
+extern struct termios *tty_termios[];
+extern struct termios *termios_locked[];
 extern int tty_check_write[];
 extern struct tty_struct * redirect;
 extern struct tty_ldisc ldiscs[];
diff --git a/include/linux/unistd.h b/include/linux/unistd.h
index 1056a21..dbb0295 100644
--- a/include/linux/unistd.h
+++ b/include/linux/unistd.h
@@ -129,6 +129,7 @@
 #define __NR_clone		120
 #define __NR_setdomainname	121
 #define __NR_uname		122
+#define __NR_modify_ldt		123
 
 extern int errno;
 
diff --git a/init/main.c b/init/main.c
index ed00c52..6d0b17b 100644
--- a/init/main.c
+++ b/init/main.c
@@ -22,11 +22,14 @@
 #include <linux/fs.h>
 #include <linux/ctype.h>
 #include <linux/delay.h>
+#include <linux/utsname.h>
 
 extern unsigned long * prof_buffer;
 extern unsigned long prof_len;
 extern char edata, end;
 extern char *linux_banner;
+extern "C" void lcall7(void);
+struct desc_struct default_ldt;
 
 /*
  * we need this inline - forking from kernel space will result
@@ -61,7 +64,7 @@
 
 static char printbuf[1024];
 
-extern char empty_zero_page[4096];
+extern char empty_zero_page[PAGE_SIZE];
 extern int vsprintf(char *,const char *,va_list);
 extern void init(void);
 extern void init_IRQ(void);
@@ -332,12 +335,13 @@
  * Interrupts are still disabled. Do necessary setups, then
  * enable them
  */
+	set_call_gate(&default_ldt,lcall7);
  	ROOT_DEV = ORIG_ROOT_DEV;
  	drive_info = DRIVE_INFO;
  	screen_info = SCREEN_INFO;
 	aux_device_present = AUX_DEVICE_INFO;
 	memory_end = (1<<20) + (EXT_MEM_K<<10);
-	memory_end &= 0xfffff000;
+	memory_end &= PAGE_MASK;
 	ramdisk_size = RAMDISK_SIZE;
 	strcpy(command_line,COMMAND_LINE);
 #ifdef CONFIG_MAX_16M
@@ -348,13 +352,12 @@
 		root_mountflags |= MS_RDONLY;
 	if ((unsigned long)&end >= (1024*1024)) {
 		memory_start = (unsigned long) &end;
-		low_memory_start = 4096;
+		low_memory_start = PAGE_SIZE;
 	} else {
 		memory_start = 1024*1024;
 		low_memory_start = (unsigned long) &end;
 	}
-	low_memory_start += 0xfff;
-	low_memory_start &= 0xfffff000;
+	low_memory_start = PAGE_ALIGN(low_memory_start);
 	memory_start = paging_init(memory_start,memory_end);
 	trap_init();
 	init_IRQ();
@@ -409,6 +412,13 @@
 			printk("Ok, fpu using %s error reporting.\n",
 				ignore_irq13?"exception 16":"irq13");
 	}
+#ifndef CONFIG_MATH_EMULATION
+	else {
+		printk("No coprocessor found and no math emulation present.\n");
+		printk("Giving up.\n");
+		for (;;) ;
+	}
+#endif
 	move_to_user_mode();
 	if (!fork())		/* we count on this going ok */
 		init();
@@ -445,7 +455,9 @@
 	(void) dup(0);
 	(void) dup(0);
 
+	system_utsname.machine[1] = '0' + x86;
 	printf(linux_banner);
+
 	execve("/etc/init",argv_init,envp_init);
 	execve("/bin/init",argv_init,envp_init);
 	execve("/sbin/init",argv_init,envp_init);
diff --git a/ipc/msg.c b/ipc/msg.c
index 2becdde..b8f5f12 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -7,6 +7,7 @@
 #include <asm/segment.h>
 #include <linux/sched.h>
 #include <linux/msg.h>
+#include <linux/stat.h>
 
 extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
 
@@ -59,7 +60,7 @@
  slept:
 	if (ipcp->seq != (msqid / MSGMNI)) 
 		return -EIDRM;
-	if (ipcperms(ipcp, 0222)) 
+	if (ipcperms(ipcp, S_IWUGO)) 
 		return -EACCES;
 	
 	if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
@@ -137,7 +138,7 @@
 	while (!nmsg) {
 		if(ipcp->seq != msqid / MSGMNI)
 			return -EIDRM;
-		if (ipcperms (ipcp, 0444))
+		if (ipcperms (ipcp, S_IRUGO))
 			return -EACCES;
 		if (msgtyp == 0) 
 			nmsg = msq->msg_first;
@@ -243,7 +244,7 @@
 		return -ENOMEM;
 	}
 	ipcp = &msq->msg_perm;
-	ipcp->mode = (msgflg & 0x01FF);
+	ipcp->mode = (msgflg & S_IRWXUGO);
 	ipcp->key = key;
 	ipcp->cuid = ipcp->uid = current->euid;
 	ipcp->gid = ipcp->cgid = current->egid;
@@ -359,7 +360,7 @@
 		msq = msgque[msqid];
 		if (msq == IPC_UNUSED || msq == IPC_NOID)
 			return -EINVAL;
-		if (ipcperms (&msq->msg_perm, 0444))
+		if (ipcperms (&msq->msg_perm, S_IRUGO))
 			return -EACCES;
 		id = msqid + msq->msg_perm.seq * MSGMNI; 
 		memcpy_tofs (buf, msq, sizeof(*msq));
@@ -388,7 +389,7 @@
 
 	switch (cmd) {
 	case IPC_STAT:
-		if (ipcperms (ipcp, 0444))
+		if (ipcperms (ipcp, S_IRUGO))
 			return -EACCES;
 		memcpy_tofs (buf, msq, sizeof (*msq));
 		return 0;
@@ -406,8 +407,8 @@
 		msq->msg_qbytes = tbuf.msg_qbytes;
 		ipcp->uid = tbuf.msg_perm.uid;
 		ipcp->gid =  tbuf.msg_perm.gid;
-		ipcp->mode = (ipcp->mode & ~0x1FF) | 
-			(0x1FF & tbuf.msg_perm.mode);
+		ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | 
+			(S_IRWXUGO & tbuf.msg_perm.mode);
 		msq->msg_ctime = CURRENT_TIME;
 		break;
 	default:
diff --git a/ipc/sem.c b/ipc/sem.c
index 7795843..a258716 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -9,6 +9,7 @@
 #include <linux/sched.h>
 #include <linux/sem.h>
 #include <linux/ipc.h>
+#include <linux/stat.h>
 
 extern int ipcperms (struct ipc_perm *ipcp, short semflg);
 static int newary (key_t, int, int);
@@ -79,7 +80,7 @@
 	memset (sma, 0, size);
 	sma->sem_base = (struct sem *) &sma[1];
 	ipcp = &sma->sem_perm;
-	ipcp->mode = (semflg & 0x01FF);
+	ipcp->mode = (semflg & S_IRWXUGO);
 	ipcp->key = key;
 	ipcp->cuid = ipcp->uid = current->euid;
 	ipcp->gid = ipcp->cgid = current->egid;
@@ -198,7 +199,7 @@
 		sma = semary[semid];
 		if (sma == IPC_UNUSED || sma == IPC_NOID)
 			return -EINVAL;
-		if (ipcperms (&sma->sem_perm, 0444))
+		if (ipcperms (&sma->sem_perm, S_IRUGO))
 			return -EACCES;
 		id = semid + sma->sem_perm.seq * SEMMNI; 
 		memcpy_tofs (buf, sma, sizeof(*sma));
@@ -223,7 +224,7 @@
 	case GETNCNT:
 	case GETZCNT:
 	case GETALL:
-		if (ipcperms (ipcp, 0444))
+		if (ipcperms (ipcp, S_IRUGO))
 			return -EACCES;
 		switch (cmd) {
 		case GETVAL : return curr->semval; 
@@ -283,14 +284,14 @@
 	
 	switch (cmd) {
 	case GETALL:
-		if (ipcperms (ipcp, 0444))
+		if (ipcperms (ipcp, S_IRUGO))
 			return -EACCES;
 		for (i=0; i< sma->sem_nsems; i++)
 			sem_io[i] = sma->sem_base[i].semval;
 		memcpy_tofs (array, sem_io, nsems*sizeof(ushort));
 		break;
 	case SETVAL:
-		if (ipcperms (ipcp, 0222))
+		if (ipcperms (ipcp, S_IWUGO))
 			return -EACCES;
 		for (un = sma->undo; un; un = un->id_next)
 			if (semnum == un->sem_num)
@@ -307,19 +308,19 @@
 		    current->euid == ipcp->uid) {
 			ipcp->uid = tbuf.sem_perm.uid;
 			ipcp->gid = tbuf.sem_perm.gid;
-			ipcp->mode = (ipcp->mode & ~0777)
-				| (tbuf.sem_perm.mode & 0777);
+			ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
+				| (tbuf.sem_perm.mode & S_IRWXUGO);
 			sma->sem_ctime = CURRENT_TIME;
 			return 0;
 		}
 		return -EPERM;
 	case IPC_STAT:
-		if (ipcperms (ipcp, 0444))
+		if (ipcperms (ipcp, S_IRUGO))
 			return -EACCES;
 		memcpy_tofs (buf, sma, sizeof (*sma));
 		break;
 	case SETALL:
-		if (ipcperms (ipcp, 0222))
+		if (ipcperms (ipcp, S_IWUGO))
 			return -EACCES;
 		for (i=0; i<nsems; i++) 
 			sma->sem_base[i].semval = sem_io[i];
@@ -368,7 +369,7 @@
 				semncnt ++;
 		}
 	}
-	if (ipcperms(&sma->sem_perm, alter ? 0222 : 0444))
+	if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
 		return -EACCES;
 	/* 
 	 * ensure every sop with undo gets an undo structure 
diff --git a/ipc/shm.c b/ipc/shm.c
index fe590a4..2e2c1b3 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -2,7 +2,7 @@
  * linux/ipc/shm.c
  * Copyright (C) 1992, 1993 Krishna Balasubramanian 
  *         Many improvements/fixes by Bruno Haible.
- * assume user segments start at 0x00
+ * assume user segments start at 0x0
  */
 
 #include <linux/errno.h>
@@ -10,6 +10,7 @@
 #include <linux/sched.h>
 #include <linux/ipc.h> 
 #include <linux/shm.h>
+#include <linux/stat.h>
 
 extern int ipcperms (struct ipc_perm *ipcp, short semflg);
 extern unsigned int get_swap_page(void);
@@ -99,7 +100,7 @@
 	for (i=0; i< numpages; shp->shm_pages[i++] = 0);
 	shm_tot += numpages;
 	shp->shm_perm.key = key;
-	shp->shm_perm.mode = (shmflg & 0777);
+	shp->shm_perm.mode = (shmflg & S_IRWXUGO);
 	shp->shm_perm.cuid = shp->shm_perm.uid = current->euid;
 	shp->shm_perm.cgid = shp->shm_perm.gid = current->egid;
 	shp->shm_perm.seq = shm_seq;
@@ -177,7 +178,7 @@
 		if (!(page = shp->shm_pages[i]))
 			continue;
 		if (page & 1) {
-			free_page (page & ~0xfff);
+			free_page (page & PAGE_MASK);
 			shm_rss--;
 		} else {
 			swap_free (page);
@@ -252,7 +253,7 @@
 		shp = shm_segs[shmid];
 		if (shp == IPC_UNUSED || shp == IPC_NOID)
 			return -EINVAL;
-		if (ipcperms (&shp->shm_perm, 0444))
+		if (ipcperms (&shp->shm_perm, S_IRUGO))
 			return -EACCES;
 		id = shmid + shp->shm_perm.seq * SHMMNI; 
 		memcpy_tofs (buf, shp, sizeof(*shp));
@@ -285,7 +286,7 @@
 		ipcp->mode |= SHM_LOCKED;
 		break;
 	case IPC_STAT:
-		if (ipcperms (ipcp, 0444))
+		if (ipcperms (ipcp, S_IRUGO))
 			return -EACCES;
 		if (!buf)
 			return -EFAULT;
@@ -299,8 +300,8 @@
 		    current->euid == shp->shm_perm.cuid) {
 			ipcp->uid = tbuf.shm_perm.uid;
 			ipcp->gid = tbuf.shm_perm.gid;
-			ipcp->mode = (ipcp->mode & ~0777)
-				| (tbuf.shm_perm.mode & 0777);
+			ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
+				| (tbuf.shm_perm.mode & S_IRWXUGO);
 			shp->shm_ctime = CURRENT_TIME;
 			break;
 		}
@@ -334,16 +335,16 @@
 	
 	/* check that the range is unmapped and has page_tables */
 	for (tmp = shmd->start; tmp < shmd->end; tmp += PAGE_SIZE) { 
-		page_table = (ulong *) (page_dir + ((tmp >> 20) & 0xffc));
+		page_table = PAGE_DIR_OFFSET(page_dir,tmp);
 		if (*page_table & PAGE_PRESENT) {
-			page_table = (ulong *) (0xfffff000 & *page_table);
-			page_table += ((tmp >> PAGE_SHIFT) & 0x3ff);
+			page_table = (ulong *) (PAGE_MASK & *page_table);
+			page_table += ((tmp >> PAGE_SHIFT) & PTRS_PER_PAGE-1);
 			if (*page_table) {
 				if (!remap)
 					return -EINVAL;
 				if (*page_table & PAGE_PRESENT) {
 					--current->rss;
-					free_page (*page_table & ~0xfff);
+					free_page (*page_table & PAGE_MASK);
 				}
 				else
 					swap_free (*page_table);
@@ -352,11 +353,11 @@
 			continue;
 		}  
 	      {
-		unsigned long new_pt = get_free_page(GFP_KERNEL);
-		if (!new_pt)
+		unsigned long new_pt;
+		if(!(new_pt = get_free_page(GFP_KERNEL)))	/* clearing needed?  SRB. */
 			return -ENOMEM;
 		*page_table = new_pt | PAGE_TABLE;
-		tmp = ((tmp + (PAGE_SIZE << 10) - 1) & 0xff400000) -PAGE_SIZE;
+		tmp |= ((PAGE_SIZE << 10) - PAGE_SIZE);
 	}}
 	if (invalid)
 		invalidate();
@@ -365,9 +366,9 @@
 	shm_sgn = shmd->shm_sgn;
 	for (tmp = shmd->start; tmp < shmd->end; tmp += PAGE_SIZE, 
 	     shm_sgn += (1 << SHM_IDX_SHIFT)) { 
-		page_table = (ulong *) (page_dir + ((tmp >> 20) & 0xffc));
-		page_table = (ulong *) (0xfffff000 & *page_table);
-		page_table += (tmp >> PAGE_SHIFT) & 0x3ff;
+		page_table = PAGE_DIR_OFFSET(page_dir,tmp);
+		page_table = (ulong *) (PAGE_MASK & *page_table);
+		page_table += (tmp >> PAGE_SHIFT) & PTRS_PER_PAGE-1;
 		*page_table = shm_sgn;
 	}
 	return 0;
@@ -393,9 +394,6 @@
 	if (shp == IPC_UNUSED || shp == IPC_NOID)
 		return -EINVAL;
 
-#define SHM_RANGE_END 0x60000000
-#define SHM_RANGE_START 0x40000000
-
 	if (!(addr = (ulong) shmaddr)) {
 		if (shmflg & SHM_REMAP)
 			return -EINVAL;
@@ -407,7 +405,7 @@
 			if (addr >= shmd->start)
 				addr = shmd->start;
 		}
-		addr = (addr - shp->shm_segsz) & ~0xfff;
+		addr = (addr - shp->shm_segsz) & PAGE_MASK;
 	} else if (addr & (SHMLBA-1)) {
 		if (shmflg & SHM_RND) 
 			addr &= ~(SHMLBA-1);       /* round down */
@@ -425,7 +423,7 @@
 				return -EINVAL;
 		}
 
-	if (ipcperms(&shp->shm_perm, shmflg & SHM_RDONLY ? 0444 : 0666))
+	if (ipcperms(&shp->shm_perm, shmflg & SHM_RDONLY ? S_IRUGO : S_IRUGO|S_IWUGO))
 		return -EACCES;
 	if (shp->shm_perm.seq != shmid / SHMMNI) 
 		return -EIDRM;
@@ -556,7 +554,7 @@
 	}
 	p2->shm = new_desc;
 	for (shmd = new_desc; shmd; shmd = shmd->task_next) {
-		id = (shmd->shm_sgn >> SHM_ID_SHIFT) & 0xfff;
+		id = (shmd->shm_sgn >> SHM_ID_SHIFT) & SHM_ID_MASK;
 		shp = shm_segs[id];
 		if (shp == IPC_UNUSED) {
 			printk("shm_fork: unused id=%d PANIC\n", id);
@@ -598,8 +596,7 @@
 	}
 
 	if (!(shp->shm_pages[idx] & PAGE_PRESENT)) {
-		page = get_free_page(GFP_KERNEL);
-		if (!page) {
+		if(!(page = __get_free_page(GFP_KERNEL))) {
 			oom(current);
 			*ptent = BAD_PAGE | PAGE_ACCESSED | 7;
 			return;
@@ -692,15 +689,15 @@
 			printk ("shm_swap: too large idx=%d id=%d PANIC\n",idx, id);
 			continue;
 		}
-		pte = (ulong *) (shmd->task->tss.cr3 + ((tmp>>20) & 0xffc));
+		pte = PAGE_DIR_OFFSET(shmd->task->tss.cr3,tmp);
 		if (!(*pte & 1)) { 
 			printk("shm_swap: bad pgtbl! id=%d start=%x idx=%d\n", 
 					id, shmd->start, idx);
 			*pte = 0;
 			continue;
 		} 
-		pte = (ulong *) (0xfffff000 & *pte);
-		pte += ((tmp >> PAGE_SHIFT) & 0x3ff);
+		pte = (ulong *) (PAGE_MASK & *pte);
+		pte += ((tmp >> PAGE_SHIFT) & PTRS_PER_PAGE-1);
 		tmp = *pte;
 		if (!(tmp & PAGE_PRESENT))
 			continue;
@@ -717,7 +714,7 @@
 
 	if (mem_map[MAP_NR(page)] != 1) 
 		goto check_table;
-	page &= ~0xfff;
+	page &= PAGE_MASK;
 	shp->shm_pages[idx] = swap_nr;
 	if (invalid)
 		invalidate();
diff --git a/ipc/util.c b/ipc/util.c
index 700d137..46dc00d 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -10,6 +10,7 @@
 #include <linux/sem.h>
 #include <linux/msg.h>
 #include <linux/shm.h>
+#include <linux/stat.h>
 
 void ipc_init (void);
 extern "C" int sys_ipc (uint call, int first, int second, int third, void *ptr); 
@@ -45,16 +46,19 @@
  */
 int ipcperms (struct ipc_perm *ipcp, short flag)
 {
-	int i, perm = 0007, euid = current->euid, egid;
+	int i; mode_t perm; uid_t euid; int egid;
 	
 	if (suser())
 		return 0;
+
+	perm = S_IRWXO; euid = current->euid;
+
 	if (euid == ipcp->cuid || euid == ipcp->uid) 
-		perm = 0700;
+		perm = S_IRWXU;
 	else {
 		for (i = 0; (egid = current->groups[i]) != NOGROUP; i++)
 			if ((egid == ipcp->cgid) || (egid == ipcp->gid)) { 
-				perm = 0070; 
+				perm = S_IRWXG; 
 				break;
 			}
 	}
diff --git a/kernel/FPU-emu/README b/kernel/FPU-emu/README
index f34b3ff..a000e4f 100644
--- a/kernel/FPU-emu/README
+++ b/kernel/FPU-emu/README
@@ -29,24 +29,23 @@
 math emulator by Linus Torvalds.
 
 My target FPU for wm-FPU-emu is that described in the Intel486
-Programmer's Reference Manual (1992 edition). Numerous facets of the
-functioning of the FPU are not well covered in the Reference Manual;
-in the absence of clear details I have made guesses about the most
-reasonable behaviour. Recently, this situation has improved because
-I now have some access to the results produced by a real 80486 FPU.
+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. 
-See "Limitations" later in this file for a partial list of some
-differences.  I believe that the missing features are never used by
-normal C or FORTRAN programs. 
-
+wm-FPU-emu does not implement all of the behaviour of the 80486 FPU.
+See "Limitations" later in this file for a list of some differences.
 
 Please report bugs, etc to me at:
        apm233m@vaxc.cc.monash.edu.au
 
 
 --Bill Metzenthen
-  May 1993
+  July 1993
 
 
 ----------------------- Internals of wm-FPU-emu -----------------------
@@ -66,6 +65,12 @@
 (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
@@ -85,27 +90,53 @@
 ----------------------- Limitations of wm-FPU-emu -----------------------
 
 There are a number of differences between the current wm-FPU-emu
-(version beta 1.4) and the 80486 FPU (apart from bugs). Some of the
+(version beta 1.5) and the 80486 FPU (apart from bugs). Some of the
 more important differences are listed below:
 
+Segment overrides don't do anything yet.
+
 All internal computations are performed at 64 bit or higher precision
-and rounded etc as required by the PC bits of the FPU control word.
-Under the crt0 version for Linux current at March 1993, the FPU PC
-bits specify 53 bits precision.
+and the results rounded etc as required by the PC bits of the FPU
+control word.  Under the crt0 version for Linux current at June 1993,
+the FPU PC bits specify 64 bits precision.
 
 The precision flag (PE of the FPU status word) and the Roundup flag
-(C1 of the status word) are now partially implemented. Does anyone
-write code which uses these features?
+(C1 of the status word) are now implemented. Does anyone write code
+which uses these features? 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.
 
-The functions which load/store the FPU state are partially implemented,
-but the implementation should be sufficient for handling FPU errors etc
-in 32 bit protected mode.
+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.
 
-The implementation of the exception mechanism is flawed for unmasked
-interrupts.
+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.
 
-Detection of certain conditions, such as denormal operands, is not yet
-complete.
+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.
 
 ----------------------- Performance of wm-FPU-emu -----------------------
 
@@ -186,7 +217,8 @@
 in the last column.
 
 
-Function      Tested x range            Worst result (bits)         Turbo C
+Function      Tested x range            Worst result                Turbo C
+                                        (relative bits)
 
 sqrt(x)       1 .. 2                    64.1                         63.2
 atan(x)       1e-10 .. 200              62.6                         62.8
@@ -213,31 +245,64 @@
 pass the 'paranoia' tests for 'double' variables when precision
 control is set to 64 bits).
 
+For version 1.5, the accuracy of fprem and fprem1 has been improved.
+These functions now produce exact results. 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 consquence, 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 ftan 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.
+
 ------------------------- Contributors -------------------------------
 
 A number of people have contributed to the development of the
-emulator, often by just reporting bugs, sometimes with a suggested
-fix, and a few kind people have provided me with access in one way or
-another to an 80486 machine. Contributors include (to those people who
-I have forgotten, please excuse me):
+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
+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
+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
+Dan Russel, russed@rpi.edu
+Daniel Carosone, danielce@ee.mu.oz.au
 cae@jpmorgan.com
-Hamish Coleman t933093@minyos.xx.rmit.oz.au
+Hamish Coleman, t933093@minyos.xx.rmit.oz.au
+Bruce Evans, bde@kralizec.zeta.org.au
 
 ...and numerous others who responded to my request for help with
 a real 80486.
diff --git a/kernel/FPU-emu/errors.c b/kernel/FPU-emu/errors.c
index b933594..d7619ce 100644
--- a/kernel/FPU-emu/errors.c
+++ b/kernel/FPU-emu/errors.c
@@ -68,26 +68,24 @@
   RE_ENTRANT_CHECK_OFF
   byte1 = get_fs_byte((unsigned char *) FPU_ORIG_EIP);
   FPU_modrm = get_fs_byte(1 + (unsigned char *) FPU_ORIG_EIP);
+  partial_status = status_word();
 
 #ifdef DEBUGGING
-if ( status_word & SW_Backward )    printk("SW: backward compatibility\n");
-if ( status_word & SW_C3 )          printk("SW: condition bit 3\n");
-if ( status_word & SW_C2 )          printk("SW: condition bit 2\n");
-if ( status_word & SW_C1 )          printk("SW: condition bit 1\n");
-if ( status_word & SW_C0 )          printk("SW: condition bit 0\n");
-if ( status_word & SW_Summary )     printk("SW: exception summary\n");
-if ( status_word & SW_Stack_Fault ) printk("SW: stack fault\n");
-if ( status_word & SW_Precision )   printk("SW: loss of precision\n");
-if ( status_word & SW_Underflow )   printk("SW: underflow\n");
-if ( status_word & SW_Overflow )    printk("SW: overflow\n");
-if ( status_word & SW_Zero_Div )    printk("SW: divide by zero\n");
-if ( status_word & SW_Denorm_Op )   printk("SW: denormalized operand\n");
-if ( status_word & SW_Invalid )     printk("SW: invalid operation\n");
+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
 
-  status_word = status_word & ~SW_Top;
-  status_word |= (top&7) << SW_Top_Shift;
-
   printk("At %p: %02x ", FPU_ORIG_EIP, byte1);
   if (FPU_modrm >= 0300)
     printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
@@ -96,15 +94,15 @@
 	   (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
 
   printk(" SW: b=%d st=%d es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
-	 status_word & 0x8000 ? 1 : 0,   /* busy */
-	 (status_word & 0x3800) >> 11,   /* stack top pointer */
-	 status_word & 0x80 ? 1 : 0,     /* Error summary status */
-	 status_word & 0x40 ? 1 : 0,     /* Stack flag */
-	 status_word & SW_C3?1:0, status_word & SW_C2?1:0, /* cc */
-	 status_word & SW_C1?1:0, status_word & SW_C0?1:0, /* cc */
-	 status_word & SW_Precision?1:0, status_word & SW_Underflow?1:0,
-	 status_word & SW_Overflow?1:0, status_word & SW_Zero_Div?1:0,
-	 status_word & SW_Denorm_Op?1:0, status_word & SW_Invalid?1:0);
+	 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=%d%d pc=%d%d iem=%d     ef=%d%d%d%d%d%d\n",
 	 control_word & 0x1000 ? 1 : 0,
@@ -124,12 +122,14 @@
 	  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_Denormal: */
 	case TW_Infinity:
 	  printk("st(%d)  %c .%04x %04x %04x %04x e%+-6d ", i,
 		 r->sign ? '-' : '+',
@@ -179,21 +179,22 @@
  error was detected.
 
  Internal error types:
-       0x14   in e14.c
+       0      in load_store.c
+       0x14   in fpu_etc.c
        0x1nn  in a *.c file:
               0x101  in reg_add_sub.c
               0x102  in reg_mul.c
               0x103  in poly_sin.c
-              0x104  in poly_tan.c
+              0x104  in poly_atan.c
               0x105  in reg_mul.c
-	      0x106  in reg_mov.c
+	      0x106  in reg_ld_str.c
               0x107  in fpu_trig.c
 	      0x108  in reg_compare.c
 	      0x109  in reg_compare.c
 	      0x110  in reg_add_sub.c
-	      0x111  in interface.c
+	      0x111  in fpe_entry.c
 	      0x112  in fpu_trig.c
-	      0x113  in reg_add_sub.c
+	      0x113  in errors.c
 	      0x114  in reg_ld_str.c
 	      0x115  in fpu_trig.c
 	      0x116  in fpu_trig.c
@@ -204,8 +205,11 @@
 	      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
        0x2nn  in an *.s file:
-              0x201  in reg_u_add.S
+              0x201  in reg_u_add.S, reg_round.S
               0x202  in reg_u_div.S
               0x203  in reg_u_div.S
               0x204  in reg_u_div.S
@@ -225,7 +229,7 @@
 	      0x218  in reg_round.S
  */
 
-extern "C" void exception(int n)
+void exception(int n)
 {
   int i, int_type;
 
@@ -235,22 +239,23 @@
       int_type = n - EX_INTERNAL;
       n = EX_INTERNAL;
       /* Set lots of exception bits! */
-      status_word |= (SW_Exc_Mask | SW_Summary | FPU_BUSY);
+      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 */
-      status_word |= n;
-      if ( status_word & ~control_word & CW_Exceptions )
-	status_word |= SW_Summary;
+      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. */
-	    status_word &= ~SW_C1;
+	    partial_status &= ~SW_C1;
 	}
     }
 
@@ -305,12 +310,15 @@
 }
 
 
-/* Real operation attempted on two operands, one a NaN */
-extern "C" void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest)
+/* Real operation attempted on two operands, one a NaN. */
+/* Returns nz if the exception is unmasked */
+extern "C" int real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest)
 {
   FPU_REG *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)
     {
@@ -349,7 +357,7 @@
       if ( !(x->sigh & 0x80000000) )  /* pseudo-NaN ? */
 	x = &CONST_QNaN;
       reg_move(x, dest);
-      return;
+      return 0;
     }
 
   if ( control_word & CW_Invalid )
@@ -364,28 +372,30 @@
 
   EXCEPTION(EX_Invalid);
   
-  return;
+  return !(control_word & CW_Invalid);
 }
 
+
 /* Invalid arith operation on Valid registers */
-extern "C" void arith_invalid(FPU_REG *dest)
+/* Returns nz if the exception is unmasked */
+extern "C" int arith_invalid(FPU_REG *dest)
 {
+
+  EXCEPTION(EX_Invalid);
   
   if ( control_word & CW_Invalid )
     {
       /* The masked response */
       reg_move(&CONST_QNaN, dest);
     }
-
-  EXCEPTION(EX_Invalid);
   
-  return;
+  return !(control_word & CW_Invalid);
 
 }
 
 
 /* Divide a finite number by zero */
-extern "C" void divide_by_zero(int sign, FPU_REG *dest)
+extern "C" int divide_by_zero(int sign, FPU_REG *dest)
 {
 
   if ( control_word & CW_ZeroDiv )
@@ -397,16 +407,33 @@
  
   EXCEPTION(EX_ZeroDiv);
 
-  return;
+  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 */
 extern "C" void set_precision_flag_up(void)
 {
   if ( control_word & CW_Precision )
-    status_word |= (SW_Precision | SW_C1);   /* The masked response */
+    partial_status |= (SW_Precision | SW_C1);   /* The masked response */
   else
     exception(EX_Precision | SW_C1);
 
@@ -418,8 +445,8 @@
 {
   if ( control_word & CW_Precision )
     {   /* The masked response */
-      status_word &= ~SW_C1;
-      status_word |= SW_Precision;
+      partial_status &= ~SW_C1;
+      partial_status |= SW_Precision;
     }
   else
     exception(EX_Precision);
@@ -430,7 +457,7 @@
 {
   if ( control_word & CW_Denormal )
     {   /* The masked response */
-      status_word |= SW_Denorm_Op;
+      partial_status |= SW_Denorm_Op;
       return 0;
     }
   else
@@ -441,14 +468,14 @@
 }
 
 
-extern "C" void arith_overflow(FPU_REG *dest)
+extern "C" int arith_overflow(FPU_REG *dest)
 {
 
   if ( control_word & CW_Overflow )
     {
       char sign;
       /* The masked response */
-/* **** The response here depends upon the rounding mode */
+/* ###### The response here depends upon the rounding mode */
       sign = dest->sign;
       reg_move(&CONST_INF, dest);
       dest->sign = sign;
@@ -459,33 +486,50 @@
       dest->exp -= (3 * (1 << 13));
     }
 
-  /* By definition, precision is lost.
-     It appears that the roundup bit (C1) is also set by convention. */
-  EXCEPTION(EX_Overflow | EX_Precision | SW_C1);
+  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;
+  return !(control_word & CW_Overflow);
 
 }
 
 
-extern "C" void arith_underflow(FPU_REG *dest)
+extern "C" 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);
+	{
+	  reg_move(&CONST_Z, dest);
+	  partial_status &= ~SW_C1;       /* Round down. */
+	}
     }
   else
     {
-      /* Add the magic number to the exponent */
+      /* 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;
+  return !(control_word & CW_Underflow);
+
 }
 
 
diff --git a/kernel/FPU-emu/exception.h b/kernel/FPU-emu/exception.h
index 3692065..7f90ede 100644
--- a/kernel/FPU-emu/exception.h
+++ b/kernel/FPU-emu/exception.h
@@ -35,6 +35,10 @@
 #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
diff --git a/kernel/FPU-emu/fpu_arith.c b/kernel/FPU-emu/fpu_arith.c
index 96ba79a..b81e658 100644
--- a/kernel/FPU-emu/fpu_arith.c
+++ b/kernel/FPU-emu/fpu_arith.c
@@ -13,11 +13,16 @@
 #include "fpu_system.h"
 #include "fpu_emu.h"
 #include "control_w.h"
+#include "status_w.h"
 
 
 void fadd__()
 {
   /* fadd st,st(i) */
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
 }
 
@@ -25,6 +30,10 @@
 void fmul__()
 {
   /* fmul st,st(i) */
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
 }
 
@@ -33,6 +42,10 @@
 void fsub__()
 {
   /* fsub st,st(i) */
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
 }
 
@@ -40,6 +53,10 @@
 void fsubr_()
 {
   /* fsubr st,st(i) */
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
 }
 
@@ -47,6 +64,10 @@
 void fdiv__()
 {
   /* fdiv st,st(i) */
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
 }
 
@@ -54,6 +75,10 @@
 void fdivr_()
 {
   /* fdivr st,st(i) */
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
 }
 
@@ -62,6 +87,10 @@
 void fadd_i()
 {
   /* fadd st(i),st */
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
 }
 
@@ -69,7 +98,11 @@
 void fmul_i()
 {
   /* fmul st(i),st */
-  reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
+  reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
 }
 
 
@@ -78,6 +111,10 @@
   /* fsubr st(i),st */
   /* This is the sense of the 80486 manual
      reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
 }
 
@@ -87,6 +124,10 @@
   /* fsub st(i),st */
   /* This is the sense of the 80486 manual
      reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
 }
 
@@ -94,6 +135,10 @@
 void fdivri()
 {
   /* fdivr st(i),st */
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
 }
 
@@ -101,6 +146,10 @@
 void fdiv_i()
 {
   /* fdiv st(i),st */
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
 }
 
@@ -109,16 +158,24 @@
 void faddp_()
 {
   /* faddp st(i),st */
-  reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
-  pop();
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
+  if ( !reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
+    pop();
 }
 
 
 void fmulp_()
 {
   /* fmulp st(i),st */
-  reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
-  pop();
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
+  if ( !reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
+    pop();
 }
 
 
@@ -128,8 +185,12 @@
   /* fsubrp st(i),st */
   /* This is the sense of the 80486 manual
      reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */
-  reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
-  pop();
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
+  if ( !reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
+    pop();
 }
 
 
@@ -138,23 +199,35 @@
   /* fsubp st(i),st */
   /* This is the sense of the 80486 manual
      reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */
-  reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
-  pop();
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
+  if ( !reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) )
+    pop();
 }
 
 
 void fdivrp()
 {
   /* fdivrp st(i),st */
-  reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
-  pop();
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
+  if ( !reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
+    pop();
 }
 
 
 void fdivp_()
 {
   /* fdivp st(i),st */
-  reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
-  pop();
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
+  if ( !reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) )
+    pop();
 }
 
diff --git a/kernel/FPU-emu/fpu_aux.c b/kernel/FPU-emu/fpu_aux.c
index 0b9e2d1..e9f2bd2 100644
--- a/kernel/FPU-emu/fpu_aux.c
+++ b/kernel/FPU-emu/fpu_aux.c
@@ -14,14 +14,16 @@
 #include "exception.h"
 #include "fpu_emu.h"
 #include "status_w.h"
+#include "control_w.h"
 
 
 
 void fclex(void)
 {
-  status_word &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision|
+  partial_status &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision|
 		   SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op|
 		   SW_Invalid);
+  NO_NET_DATA_EFFECT;
   FPU_entry_eip = ip_offset;               /* We want no net effect */
 }
 
@@ -30,12 +32,18 @@
 {
   int r;
   control_word = 0x037f;
-  status_word = 0;
+  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 */
+  data_operand_offset = 0;
+  operand_selector = 0;
+  NO_NET_DATA_EFFECT;
+  FPU_entry_op_cs = 0;
   FPU_entry_eip = ip_offset = 0;
 }
 
@@ -51,12 +59,8 @@
 
 static void fstsw_ax(void)
 {
-
-  status_word &= ~SW_Top;
-  status_word |= (top&7) << SW_Top_Shift;
-
-  *(short *) &FPU_EAX = status_word;
-
+  *(short *) &FPU_EAX = status_word();
+  NO_NET_INSTR_EFFECT;
 }
 
 static FUNC fstsw_table[] = {
@@ -123,16 +127,22 @@
 	  stack_underflow_i(FPU_rm);
 	  return;
 	}
-      reg_move(sti_ptr, FPU_st0_ptr);
+      if ( control_word & CW_Invalid )
+	reg_move(sti_ptr, FPU_st0_ptr);   /* Masked response */
       stack_underflow_i(FPU_rm);
       return;
     }
   if ( sti_ptr->tag == TW_Empty )
     {
-      reg_move(FPU_st0_ptr, sti_ptr);
+      if ( control_word & CW_Invalid )
+	reg_move(FPU_st0_ptr, sti_ptr);   /* Masked response */
       stack_underflow();
       return;
     }
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   reg_move(FPU_st0_ptr, &t);
   reg_move(sti_ptr, FPU_st0_ptr);
   reg_move(&t, sti_ptr);
diff --git a/kernel/FPU-emu/fpu_emu.h b/kernel/FPU-emu/fpu_emu.h
index ed9cc06..a0c906a 100644
--- a/kernel/FPU-emu/fpu_emu.h
+++ b/kernel/FPU-emu/fpu_emu.h
@@ -38,6 +38,8 @@
 #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)
@@ -46,14 +48,12 @@
 #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_Denormal     Const(4) */       /* De-normal */
 #define TW_Infinity	Const(5)	/* + or - infinity */
 #define	TW_NaN		Const(6)	/* Not a Number */
 
 #define TW_Empty	Const(7)	/* empty */
 
-/* #define TW_FPU_Interrupt Const(0x80) */    /* Signals an interrupt */
-
 
 #ifndef __ASSEMBLER__
 
@@ -68,6 +68,16 @@
 #  define RE_ENTRANT_CHECK_ON
 #endif PARANOID
 
+/* These are to defeat the default action, giving the instruction
+   no net effect: */
+#define NO_NET_DATA_EFFECT \
+      { FPU_data_address = (void *)data_operand_offset; \
+	FPU_data_selector = operand_selector; }
+#define NO_NET_INSTR_EFFECT \
+      { FPU_entry_eip = ip_offset; \
+	FPU_entry_op_cs = cs_selector; }
+
+
 typedef void (*FUNC)(void);
 typedef struct fpu_reg FPU_REG;
 
@@ -82,7 +92,10 @@
 extern	char	FPU_st0_tag;
 extern	FPU_REG	*FPU_st0_ptr;
 
-extern void  *FPU_data_address;
+/* ###### These need to be shifted to somewhere safe. */
+/* extern void  *FPU_data_address; has been shifted */
+extern unsigned short FPU_data_selector;
+extern unsigned long FPU_entry_op_cs;
 
 extern  FPU_REG  FPU_loaded_data;
 
@@ -99,7 +112,7 @@
 
 
 /*----- Prototypes for functions written in assembler -----*/
-/* extern "C" void reg_move(FPU_REG *a, FPU_REG *b); */
+/* extern void reg_move(FPU_REG *a, FPU_REG *b); */
 
 extern "C" void mul64(long long *a, long long *b, long long *result);
 extern "C" void poly_div2(long long *x);
@@ -109,17 +122,17 @@
 		       unsigned short terms[][4], int n);
 extern "C" void normalize(FPU_REG *x);
 extern "C" void normalize_nuo(FPU_REG *x);
-extern "C" void reg_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+extern "C" int reg_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
 		    unsigned int control_w);
-extern "C" void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+extern "C" int reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
 		      unsigned int control_w);
-extern "C" void reg_u_mul(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+extern "C" int reg_u_mul(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
 		      unsigned int control_w);
-extern "C" void reg_u_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+extern "C" int reg_u_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
 		      unsigned int control_w);
-extern "C" void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+extern "C" int reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
 		      unsigned int control_w);
-extern "C" void wm_sqrt(FPU_REG *n, unsigned int control_w);
+extern "C" int wm_sqrt(FPU_REG *n, unsigned int control_w);
 extern "C" unsigned	shrx(void *l, unsigned x);
 extern "C" unsigned	shrxs(void *v, unsigned x);
 extern "C" unsigned long div_small(unsigned long long *x, unsigned long y);
diff --git a/kernel/FPU-emu/fpu_entry.c b/kernel/FPU-emu/fpu_entry.c
index f67c440..b3e188e 100644
--- a/kernel/FPU-emu/fpu_entry.c
+++ b/kernel/FPU-emu/fpu_entry.c
@@ -38,6 +38,15 @@
 
 #include <asm/segment.h>
 
+#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 __BAD__ Un_impl   /* Not implemented */
 
@@ -133,28 +142,43 @@
 char	       FPU_st0_tag;
 FPU_REG       *FPU_st0_ptr;
 
+/* ######## To be shifted */
+unsigned long FPU_entry_op_cs;
+unsigned short FPU_data_selector;
+
+
 #ifdef PARANOID
 char emulating=0;
 #endif PARANOID
 
 #define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
+static int valid_prefix(unsigned char byte);
 
 
 extern "C" void math_emulate(long arg)
 {
   unsigned char  FPU_modrm;
   unsigned short code;
+  int unmasked;
 
 #ifdef PARANOID
   if ( emulating )
     {
       printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
     }
-  RE_ENTRANT_CHECK_ON
+  RE_ENTRANT_CHECK_ON;
 #endif PARANOID
 
   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;
     }
@@ -181,22 +205,44 @@
 
 do_another_FPU_instruction:
 
-  RE_ENTRANT_CHECK_OFF
+  RE_ENTRANT_CHECK_OFF;
   code = get_fs_word((unsigned short *) FPU_EIP);
-  RE_ENTRANT_CHECK_ON
+  RE_ENTRANT_CHECK_ON;
 
-  if ( (code & 0xff) == 0x9b )  /* fwait */
+#ifdef PECULIAR_486
+  /* It would be more logical to do this only in get_address(),
+     but although it is supposed to be undefined for many fpu
+     instructions, an 80486 behaves as if this were done here: */
+  FPU_data_selector = FPU_DS;
+#endif PECULIAR_486
+
+  if ( (code & 0xf8) != 0xd8 )
     {
-      if (status_word & SW_Summary)
-	goto do_the_FPU_interrupt;
-      else
+      if ( (code & 0xff) == FWAIT_OPCODE )
 	{
-	  FPU_EIP++;
-	  goto FPU_instruction_done;
+	  if (partial_status & SW_Summary)
+	    goto do_the_FPU_interrupt;
+	  else
+	    {
+	      FPU_EIP++;
+	      goto FPU_fwait_done;
+	    }
 	}
+      else if ( valid_prefix(code & 0xff) )
+	{
+	  goto do_another_FPU_instruction;
+	}
+#ifdef PARANOID
+      RE_ENTRANT_CHECK_OFF;
+      printk("FPU emulator: Unknown prefix byte 0x%02x\n", code & 0xff);
+      RE_ENTRANT_CHECK_ON;
+      EXCEPTION(EX_INTERNAL|0x126);
+      FPU_EIP++;
+      goto do_the_FPU_interrupt;
+#endif PARANOID
     }
 
-  if (status_word & SW_Summary)
+  if (partial_status & SW_Summary)
     {
       /* Ignore the error for now if the current instruction is a no-wait
 	 control instruction */
@@ -210,9 +256,6 @@
 						     fnstsw */
 		 ((code & 0xc000) != 0xc000))) ) )
 	{
-	  /* This is a guess about what a real FPU might do to this bit: */
-/*	  status_word &= ~SW_Summary; ****/
-
 	  /*
 	   *  We need to simulate the action of the kernel to FPU
 	   *  interrupts here.
@@ -223,9 +266,9 @@
 	   */
 	do_the_FPU_interrupt:
 	  cs_selector &= 0xffff0000;
-	  cs_selector |= (status_word & ~SW_Top) | ((top&7) << SW_Top_Shift);
+	  cs_selector |= status_word();
       	  operand_selector = tag_word();
-	  status_word = 0;
+	  partial_status = 0;
 	  top = 0;
 	  {
 	    int r;
@@ -235,7 +278,7 @@
 	      }
 	  }
 
-	  RE_ENTRANT_CHECK_OFF
+	  RE_ENTRANT_CHECK_OFF;
 	  send_sig(SIGFPE, current, 1);
 	  return;
 	}
@@ -243,12 +286,18 @@
 
   FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
 
-  if ( (code & 0xff) == 0x66 )  /* size prefix */
+  {
+    unsigned short swapped_code = code;
+    bswapw(swapped_code);
+    FPU_entry_op_cs = (swapped_code << 16) | (FPU_CS & 0xffff) ;
+  }
+
+  if ( (code & 0xff) == OP_SIZE_PREFIX )
     {
       FPU_EIP++;
-      RE_ENTRANT_CHECK_OFF
+      RE_ENTRANT_CHECK_OFF;
       code = get_fs_word((unsigned short *) FPU_EIP);
-      RE_ENTRANT_CHECK_ON
+      RE_ENTRANT_CHECK_ON;
     }
   FPU_EIP += 2;
 
@@ -261,23 +310,24 @@
       get_address(FPU_modrm);
       if ( !(code & 1) )
 	{
-	  unsigned short status1 = status_word;
+	  unsigned short status1 = partial_status;
 	  FPU_st0_ptr = &st(0);
 	  FPU_st0_tag = FPU_st0_ptr->tag;
 
 	  /* Stack underflow has priority */
 	  if ( NOT_EMPTY_0 )
 	    {
+	      unmasked = 0;  /* Do this here to stop compiler warnings. */
 	      switch ( (code >> 1) & 3 )
 		{
 		case 0:
-		  reg_load_single();
+		  unmasked = reg_load_single();
 		  break;
 		case 1:
 		  reg_load_int32();
 		  break;
 		case 2:
-		  reg_load_double();
+		  unmasked = reg_load_double();
 		  break;
 		case 3:
 		  reg_load_int16();
@@ -297,27 +347,74 @@
 		{
 		  /* Restore the status word; we might have loaded a
 		     denormal. */
-		  status_word = status1;
+		  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 )
-			pop();             /* fcomp, so we pop. */
+		      if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
+			pop();             /* fcomp, masked, so we pop. */
 		    }
 		  else
-		    real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
+		    {
+#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(&FPU_loaded_data, FPU_st0_ptr,
+				     FPU_st0_ptr);
+		      else
+#endif PECULIAR_486
+			/* fadd, fdivr, fmul, or fsubr */
+			real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data,
+				     FPU_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 ( (FPU_st0_tag == TW_Zero) &&
+			  (FPU_loaded_data.tag == TW_Valid) )
+			{
+			  if ( divide_by_zero(FPU_loaded_data.sign,
+					      FPU_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 */
+#ifdef PECULIAR_486
+		  /* Default, this conveys no information,
+		     but an 80486 does it. */
+		  clear_C1();
+#endif PECULIAR_486
 		  reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
 			  control_word);
 		  break;
 		case 1:         /* fmul */
+#ifdef PECULIAR_486
+		  /* Default, this conveys no information,
+		     but an 80486 does it. */
+		  clear_C1();
+#endif PECULIAR_486
 		  reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
 			  control_word);
 		  break;
@@ -325,24 +422,44 @@
 		  compare_st_data();
 		  break;
 		case 3:         /* fcomp */
-		  compare_st_data();
-		  pop();
+		  if ( !compare_st_data() && !unmasked )
+		    pop();
 		  break;
 		case 4:         /* fsub */
+#ifdef PECULIAR_486
+		  /* Default, this conveys no information,
+		     but an 80486 does it. */
+		  clear_C1();
+#endif PECULIAR_486
 		  reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
 			  control_word);
 		  break;
 		case 5:         /* fsubr */
+#ifdef PECULIAR_486
+		  /* Default, this conveys no information,
+		     but an 80486 does it. */
+		  clear_C1();
+#endif PECULIAR_486
 		  reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr,
 			  control_word);
 		  break;
 		case 6:         /* fdiv */
+#ifdef PECULIAR_486
+		  /* Default, this conveys no information,
+		     but an 80486 does it. */
+		  clear_C1();
+#endif PECULIAR_486
 		  reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
 			  control_word);
 		  break;
 		case 7:         /* fdivr */
+#ifdef PECULIAR_486
+		  /* Default, this conveys no information,
+		     but an 80486 does it. */
+		  clear_C1();
+#endif PECULIAR_486
 		  if ( FPU_st0_tag == TW_Zero )
-		    status_word = status1;  /* Undo any denorm tag,
+		    partial_status = status1;  /* Undo any denorm tag,
 					       zero-divide has priority. */
 		  reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr,
 			  control_word);
@@ -356,8 +473,8 @@
 		  /* The instruction is fcom or fcomp */
 		  EXCEPTION(EX_StackUnder);
 		  setcc(SW_C3 | SW_C2 | SW_C0);
-		  if ( FPU_modrm & 0x08 )
-		    pop();             /* fcomp, Empty or not, we pop. */
+		  if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
+		    pop();             /* fcomp */
 		}
 	      else
 		stack_underflow();
@@ -370,13 +487,22 @@
 
     reg_mem_instr_done:
 
-      data_operand_offset = (unsigned long)FPU_data_address;
+#ifndef PECULIAR_486
+      *(unsigned short *)&operand_selector = FPU_data_selector;
+#endif PECULIAR_486
+      ;
     }
   else
     {
       /* None of these instructions access user memory */
       unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7);
 
+#ifdef PECULIAR_486
+      /* This is supposed to be undefined, but a real 80486 seems
+	 to do this: */
+      FPU_data_address = 0;
+#endif PECULIAR_486
+
       FPU_st0_ptr = &st(0);
       FPU_st0_tag = FPU_st0_ptr->tag;
       switch ( type_table[(int) instr_index] )
@@ -400,8 +526,7 @@
 	case _REGIp:
 	  if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
 	    {
-	      stack_underflow_i(FPU_rm);
-	      pop();
+	      stack_underflow_pop(FPU_rm);
 	      goto FPU_instruction_done;
 	    }
 	  break;
@@ -427,45 +552,72 @@
 FPU_instruction_done:
 
   ip_offset = FPU_entry_eip;
-  bswapw(code);
-  *(1 + (unsigned short *)&cs_selector) = code & 0x7ff;
+  cs_selector = FPU_entry_op_cs;
+  data_operand_offset = (unsigned long)FPU_data_address;
+#ifdef PECULIAR_486
+  *(unsigned short *)&operand_selector = FPU_data_selector;
+#endif PECULIAR_486
+  
+FPU_fwait_done:
 
 #ifdef DEBUG
-  RE_ENTRANT_CHECK_OFF
+  RE_ENTRANT_CHECK_OFF;
   emu_printall();
-  RE_ENTRANT_CHECK_ON
+  RE_ENTRANT_CHECK_ON;
 #endif DEBUG
 
   if (FPU_lookahead && !need_resched)
     {
       unsigned char next;
 
-      /* (This test should generate no machine code) */
-      while ( 1 )
-	{
-	  RE_ENTRANT_CHECK_OFF
-	  next = get_fs_byte((unsigned char *) FPU_EIP);
-	  RE_ENTRANT_CHECK_ON
-	  if ( ((next & 0xf8) == 0xd8) || (next == 0x9b) )  /* fwait */
-	    {
-	      goto do_another_FPU_instruction;
-	    }
-	  else if ( next == 0x66 )  /* size prefix */
-	    {
-	      RE_ENTRANT_CHECK_OFF
-	      next = get_fs_byte((unsigned char *) (FPU_EIP+1));
-	      RE_ENTRANT_CHECK_ON
-	      if ( (next & 0xf8) == 0xd8 )
-		{
-		  FPU_EIP++;
-		  goto do_another_FPU_instruction;
-		}
-	    }
-	  break;
-	}
+      RE_ENTRANT_CHECK_OFF;
+      next = get_fs_byte((unsigned char *) FPU_EIP);
+      RE_ENTRANT_CHECK_ON;
+      if ( valid_prefix(next) )
+	goto do_another_FPU_instruction;
     }
 
-  RE_ENTRANT_CHECK_OFF
+  RE_ENTRANT_CHECK_OFF;
+}
+
+
+/* This function is not yet complete. To properly handle all prefix
+   bytes, it will be necessary to change all emulator code which
+   accesses user address space. Access to separate segments is
+   important for msdos emulation. */
+static int valid_prefix(unsigned char byte)
+{
+  unsigned long ip = FPU_EIP;
+
+  while ( 1 )
+    {
+      switch ( byte )
+	{
+	case ADDR_SIZE_PREFIX:
+	case PREFIX_DS:   /* Redundant */
+	case PREFIX_CS:
+	case PREFIX_ES:
+	case PREFIX_SS:
+	case PREFIX_FS:
+	case PREFIX_GS:
+
+	case OP_SIZE_PREFIX:  /* Used often by gcc, but has no effect. */
+	  RE_ENTRANT_CHECK_OFF;
+	  byte = get_fs_byte((unsigned char *) (++FPU_EIP));
+	  RE_ENTRANT_CHECK_ON;
+	  break;
+	case FWAIT_OPCODE:
+	  return 1;
+	default:
+	  if ( (byte & 0xf8) == 0xd8 )
+	    return 1;
+	  else
+	    {
+	      FPU_EIP = ip;
+	      return 0;
+	    }
+	}
+    }
 }
 
 
@@ -473,7 +625,7 @@
 {
 	FPU_EIP = FPU_ORIG_EIP;
 	send_sig(signal,current,1);
-	RE_ENTRANT_CHECK_OFF
+	RE_ENTRANT_CHECK_OFF;
 	__asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4));
 #ifdef PARANOID
       printk("ERROR: wm-FPU-emu math_abort failed!\n");
@@ -487,7 +639,7 @@
 
 extern "C" void math_emulate(long arg)
 {
-  printk("math-meulation not enabled and no coprocessor found.\n");
+  printk("math-emulation not enabled and no coprocessor found.\n");
   printk("killing %s.\n",current->comm);
   send_sig(SIGFPE,current,1);
   schedule();
diff --git a/kernel/FPU-emu/fpu_etc.c b/kernel/FPU-emu/fpu_etc.c
index 4afe891..f8aee63 100644
--- a/kernel/FPU-emu/fpu_etc.c
+++ b/kernel/FPU-emu/fpu_etc.c
@@ -22,7 +22,10 @@
   if ( NOT_EMPTY_0 )
     {
       FPU_st0_ptr->sign ^= SIGN_POS^SIGN_NEG;
-      status_word &= ~SW_C1;
+#ifdef PECULIAR_486
+      /* Default, this conveys no information, but an 80486 does it. */
+      clear_C1();
+#endif PECULIAR_486
     }
   else
     stack_underflow();
@@ -33,7 +36,10 @@
   if ( FPU_st0_tag ^ TW_Empty )
     {
       FPU_st0_ptr->sign = SIGN_POS;
-      status_word &= ~SW_C1;
+#ifdef PECULIAR_486
+      /* Default, this conveys no information, but an 80486 does it. */
+      clear_C1();
+#endif PECULIAR_486
     }
   else
     stack_underflow();
@@ -48,16 +54,23 @@
       setcc(SW_C3);
       break;
     case TW_Valid:
-
-#ifdef DENORM_OPERAND
-      if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	return;
-#endif DENORM_OPERAND
-
       if (FPU_st0_ptr->sign == SIGN_POS)
         setcc(0);
       else
         setcc(SW_C0);
+
+#ifdef DENORM_OPERAND
+      if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	{
+#ifdef PECULIAR_486
+	  /* This is wierd! */
+	  if (FPU_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 */ 
@@ -68,7 +81,6 @@
         setcc(0);
       else
         setcc(SW_C0);
-      EXCEPTION(EX_Invalid);
       break;
     case TW_Empty:
       setcc(SW_C0|SW_C2|SW_C3);
@@ -97,7 +109,7 @@
       if ( FPU_st0_ptr->exp <= EXP_UNDER )
         c = SW_C2|SW_C3;  /* Denormal */
       else
-        c = SW_C3;
+        c = SW_C2;
       break;
     case TW_NaN:
       c = SW_C0;
diff --git a/kernel/FPU-emu/fpu_proto.h b/kernel/FPU-emu/fpu_proto.h
index 3bc692c..ac14a1e 100644
--- a/kernel/FPU-emu/fpu_proto.h
+++ b/kernel/FPU-emu/fpu_proto.h
@@ -5,15 +5,16 @@
 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);
 extern "C" void exception(int n);
-extern "C" void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest);
-extern "C" void arith_invalid(FPU_REG *dest);
-extern "C" void divide_by_zero(int sign, FPU_REG *dest);
+extern "C" int real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest);
+extern "C" int arith_invalid(FPU_REG *dest);
+extern "C" int divide_by_zero(int sign, FPU_REG *dest);
 extern "C" void set_precision_flag_up(void);
 extern "C" void set_precision_flag_down(void);
 extern "C" int denormal_operand(void);
-extern "C" void arith_overflow(FPU_REG *dest);
-extern "C" void arith_underflow(FPU_REG *dest);
+extern "C" int arith_overflow(FPU_REG *dest);
+extern "C" int arith_underflow(FPU_REG *dest);
 
 /* fpu_arith.c */
 extern void fadd__(void);
@@ -81,11 +82,11 @@
 extern void poly_sine(FPU_REG *arg, FPU_REG *result);
 
 /* poly_tan.c */
-extern void poly_tan(FPU_REG *arg, FPU_REG *y_reg);
+extern void poly_tan(FPU_REG *arg, FPU_REG *y_reg, int invert);
 
 /* reg_add_sub.c */
-extern void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w);
-extern void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w);
+extern int reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w);
+extern int reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w);
 
 /* reg_compare.c */
 extern int compare(FPU_REG *b);
@@ -101,9 +102,9 @@
 extern void fconst(void);
 
 /* reg_ld_str.c */
-extern void reg_load_extended(void);
-extern void reg_load_double(void);
-extern void reg_load_single(void);
+extern int reg_load_extended(void);
+extern int reg_load_double(void);
+extern int reg_load_single(void);
 extern void reg_load_int64(void);
 extern void reg_load_int32(void);
 extern void reg_load_int16(void);
@@ -123,4 +124,4 @@
 extern void fsave(void);
 
 /* reg_mul.c */
-extern void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w);
+extern int reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w);
diff --git a/kernel/FPU-emu/fpu_system.h b/kernel/FPU-emu/fpu_system.h
index 3f0def7..080ed06 100644
--- a/kernel/FPU-emu/fpu_system.h
+++ b/kernel/FPU-emu/fpu_system.h
@@ -27,7 +27,7 @@
 #define FPU_lookahead           (I387.soft.lookahead)
 #define FPU_entry_eip           (I387.soft.entry_eip)
 
-#define status_word		(I387.soft.swd)
+#define partial_status       	(I387.soft.swd)
 #define control_word		(I387.soft.cwd)
 #define regs			(I387.soft.regs)
 #define top			(I387.soft.top)
@@ -37,4 +37,7 @@
 #define data_operand_offset	(I387.soft.foo)
 #define operand_selector	(I387.soft.fos)
 
+/* ######## temporary and ugly ;-) */
+#define FPU_data_address        ((void *)(I387.soft.twd))
+
 #endif
diff --git a/kernel/FPU-emu/fpu_trig.c b/kernel/FPU-emu/fpu_trig.c
index ddac32d..cd49903 100644
--- a/kernel/FPU-emu/fpu_trig.c
+++ b/kernel/FPU-emu/fpu_trig.c
@@ -18,33 +18,105 @@
 #include "reg_constant.h"	
 
 
+static void rem_kernel(unsigned long long st0, unsigned long long *y,
+		       unsigned long long st1,
+		       long long q, int n);
 
-static int trig_arg(FPU_REG *X)
+#define BETTER_THAN_486
+
+#define FCOS  4
+#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, quot;
-  int rv;
+  FPU_REG tmp;
   long long q;
-  int old_cw = control_word;
+  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_move(X, &quot);
-  reg_div(&quot, &CONST_PI2, &quot, FULL_PRECISION);
 
-  reg_move(&quot, &tmp);
-  round_to_int(&tmp);
-  if ( tmp.sigh & 0x80000000 )
-    return -1;              /* |Arg| is >= 2^63 */
-  tmp.exp = EXP_BIAS + 63;
-  q = *(long long *)&(tmp.sigl);
-  normalize(&tmp);
+  reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
+  round_to_int(&tmp);  /* Fortunately, this can't overflow
+			  to 2^64 */
+  q = ((long long *)&(tmp.sigl))[0];
+  if ( q )
+    {
+      rem_kernel(((unsigned long long *)&(X->sigl))[0],
+		 (unsigned long long *)&(tmp.sigl),
+		 ((unsigned long long *)&(CONST_PI2.sigl))[0],
+		 q, X->exp - CONST_PI2.exp);
+      tmp.exp = CONST_PI2.exp;
+      normalize(&tmp);
+      reg_move(&tmp, X);
+    }
 
-  reg_sub(&quot, &tmp, X, FULL_PRECISION);
-  rv = q & 7;
-  
+  if ( even == FPTAN )
+    {
+      if ( ((X->exp >= EXP_BIAS) ||
+	    ((X->exp == EXP_BIAS-1)
+	     && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) )
+	even = FCOS;
+      else
+	even = 0;
+    }
+
+  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. */
+	  ((long long *)&(tmp.sigl))[0] = q + 1;
+	  tmp.exp = EXP_BIAS + 63;
+	  normalize(&tmp);
+	  reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
+	  reg_add(X, &tmp,  X, FULL_PRECISION);
+	}
+#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. */
+	  ((long long *)&(tmp.sigl))[0] = q;
+	  tmp.exp = EXP_BIAS + 63;
+	  normalize(&tmp);
+	  reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
+	  reg_sub(X, &tmp, X, FULL_PRECISION);
+	}
+    }
+#endif BETTER_THAN_486
+
   control_word = old_cw;
-  return rv;;
+  partial_status = saved_status & ~SW_C2;     /* Reduction complete. */
+
+  return (q & 3) | even;
 }
 
 
@@ -52,7 +124,7 @@
 void convert_l2reg(long *arg, FPU_REG *dest)
 {
   long num = *arg;
-  
+
   if (num == 0)
     { reg_move(&CONST_Z, dest); return; }
 
@@ -60,7 +132,7 @@
     dest->sign = SIGN_POS;
   else
     { num = -num; dest->sign = SIGN_NEG; }
-  
+
   dest->sigh = num;
   dest->sigl = 0;
   dest->exp = EXP_BIAS + 31;
@@ -77,8 +149,8 @@
       if ( !(FPU_st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
 	{
 	  EXCEPTION(EX_Invalid);
-	  /* Convert to a QNaN */
-	  FPU_st0_ptr->sigh |= 0x40000000;
+	  if ( control_word & CW_Invalid )
+	    FPU_st0_ptr->sigh |= 0x40000000;	  /* Convert to a QNaN */
 	}
       break;              /* return with a NaN in st(0) */
     case TW_Empty:
@@ -92,41 +164,91 @@
 }
 
 
+static void single_arg_2_error(void)
+{
+  FPU_REG *st_new_ptr;
+
+  switch ( FPU_st0_tag )
+    {
+    case TW_NaN:
+      if ( !(FPU_st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
+	{
+	  EXCEPTION(EX_Invalid);
+	  if ( control_word & CW_Invalid )
+	    {
+	      /* The masked response */
+	      /* Convert to a QNaN */
+	      FPU_st0_ptr->sigh |= 0x40000000;
+	      st_new_ptr = &st(-1);
+	      push();
+	      reg_move(&st(1), FPU_st0_ptr);
+	    }
+	}
+      else
+	{
+	  /* A QNaN */
+	  st_new_ptr = &st(-1);
+	  push();
+	  reg_move(&st(1), FPU_st0_ptr);
+	}
+      break;              /* return with a NaN in st(0) */
+#ifdef PARANOID
+    default:
+      EXCEPTION(EX_INTERNAL|0x0112);
+#endif PARANOID
+    }
+}
+
+
 /*---------------------------------------------------------------------------*/
 
 static void f2xm1(void)
 {
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   switch ( FPU_st0_tag )
     {
     case TW_Valid:
       {
 	FPU_REG rv, tmp;
 
-#ifdef DENORM_OPERAND
-      if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	return;
-#endif DENORM_OPERAND
-	
-	if ( FPU_st0_ptr->sign == SIGN_POS )
+	if ( FPU_st0_ptr->exp >= 0 )
 	  {
-	    /* poly_2xm1(x) requires 0 < x < 1. */
-	    if ( poly_2xm1(FPU_st0_ptr, &rv) )
-	      return;                  /* error */
-	    reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
+	    /* For an 80486 FPU, the result is undefined. */
+	  }
+	else if ( FPU_st0_ptr->exp >= -64 )
+	  {
+	    if ( FPU_st0_ptr->sign == SIGN_POS )
+	      {
+		/* poly_2xm1(x) requires 0 < x < 1. */
+		poly_2xm1(FPU_st0_ptr, &rv);
+		reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
+	      }
+	    else
+	      {
+		/* poly_2xm1(x) doesn't handle negative numbers yet. */
+		/* So we compute z=poly_2xm1(-x), and the answer is
+		   then -z/(1+z) */
+		FPU_st0_ptr->sign = SIGN_POS;
+		poly_2xm1(FPU_st0_ptr, &rv);
+		reg_mul(&rv, FPU_st0_ptr, &rv, FULL_PRECISION);
+		reg_add(&rv, &CONST_1, &tmp, FULL_PRECISION);
+		reg_div(&rv, &tmp, FPU_st0_ptr, FULL_PRECISION);
+		FPU_st0_ptr->sign = SIGN_NEG;
+	      }
 	  }
 	else
 	  {
-/* **** Should change poly_2xm1() to at least handle numbers near 0 */
-	    /* poly_2xm1(x) doesn't handle negative numbers. */
-	    /* So we compute (poly_2xm1(x+1)-1)/2, for -1 < x < 0 */
-	    reg_add(FPU_st0_ptr, &CONST_1, &tmp, FULL_PRECISION);
-	    poly_2xm1(&tmp, &rv);
-	    reg_mul(&rv, &tmp, &tmp, FULL_PRECISION);
-	    reg_sub(&tmp, &CONST_1, FPU_st0_ptr, FULL_PRECISION);
-	    FPU_st0_ptr->exp--;
-	    if ( FPU_st0_ptr->exp <= EXP_UNDER )
-	      arith_underflow(FPU_st0_ptr);
+#ifdef DENORM_OPERAND
+	    if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	      return;
+#endif DENORM_OPERAND
+	    /* For very small arguments, this is accurate enough. */
+	    reg_mul(&CONST_LN2, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
 	  }
+	set_precision_flag_up();
 	return;
       }
     case TW_Zero:
@@ -144,12 +266,26 @@
     }
 }
 
+
 static void fptan(void)
 {
   FPU_REG *st_new_ptr;
   int q;
   char arg_sign = FPU_st0_ptr->sign;
 
+  /* Stack underflow has higher priority */
+  if ( FPU_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; }
 
@@ -157,40 +293,56 @@
     {
     case TW_Valid:
 
-#ifdef DENORM_OPERAND
-      if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	return;
-#endif DENORM_OPERAND
-
-      FPU_st0_ptr->sign = SIGN_POS;
-      if ( (q = trig_arg(FPU_st0_ptr)) != -1 )
+      if ( FPU_st0_ptr->exp > EXP_BIAS - 40 )
 	{
-	  if (q & 1)
-	    reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
-	  
-	  poly_tan(FPU_st0_ptr, FPU_st0_ptr);
-
-	  FPU_st0_ptr->sign = (q & 1) ^ arg_sign;
-	  
-	  if ( FPU_st0_ptr->exp <= EXP_UNDER )
-	    arith_underflow(FPU_st0_ptr);
-	  
-	  push();
-	  reg_move(&CONST_1, FPU_st0_ptr);
-	  setcc(0);
+	  FPU_st0_ptr->sign = SIGN_POS;
+	  if ( (q = trig_arg(FPU_st0_ptr, FPTAN)) != -1 )
+	    {
+	      reg_div(FPU_st0_ptr, &CONST_PI2, FPU_st0_ptr,
+		      FULL_PRECISION);
+	      poly_tan(FPU_st0_ptr, FPU_st0_ptr, q & FCOS);
+	      FPU_st0_ptr->sign = (q & 1) ^ arg_sign;
+	    }
+	  else
+	    {
+	      /* Operand is out of range */
+	      FPU_st0_ptr->sign = arg_sign;         /* restore st(0) */
+	      return;
+	    }
 	}
       else
 	{
-	  /* Operand is out of range */
-	  setcc(SW_C2);
-	  FPU_st0_ptr->sign = arg_sign;         /* restore st(0) */
-	  return;
+	  /* For a small arg, the result == the argument */
+	  /* Underflow may happen */
+
+	  if ( FPU_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(FPU_st0_ptr) )
+		return;
+	    }
+	  else
+	    set_precision_flag_up();  /* Must be up. */
 	}
+      push();
+      reg_move(&CONST_1, FPU_st0_ptr);
+      return;
       break;
     case TW_Infinity:
-      /* Operand is out of range */
-      setcc(SW_C2);
-      FPU_st0_ptr->sign = arg_sign;         /* restore st(0) */
+      /* The 80486 treats infinity as an invalid operand */
+      arith_invalid(FPU_st0_ptr);
+      if ( control_word & CW_Invalid )
+	{
+	  st_new_ptr = &st(-1);
+	  push();
+	  arith_invalid(FPU_st0_ptr);
+	}
       return;
     case TW_Zero:
       push();
@@ -198,7 +350,7 @@
       setcc(0);
       break;
     default:
-      single_arg_error();
+      single_arg_2_error();
       break;
     }
 }
@@ -212,6 +364,10 @@
   if ( STACK_OVERFLOW )
     {  stack_overflow(); return; }
 
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   if ( !(FPU_st0_tag ^ TW_Valid) )
     {
       long e;
@@ -231,7 +387,8 @@
   else if ( FPU_st0_tag == TW_Zero )
     {
       char sign = FPU_st0_ptr->sign;
-      divide_by_zero(SIGN_NEG, FPU_st0_ptr);
+      if ( divide_by_zero(SIGN_NEG, FPU_st0_ptr) )
+	return;
       push();
       reg_move(&CONST_Z, FPU_st0_ptr);
       FPU_st0_ptr->sign = sign;
@@ -248,12 +405,8 @@
     }
   else if ( FPU_st0_tag == TW_NaN )
     {
-      if ( !(FPU_st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
-	{
-	  EXCEPTION(EX_Invalid);
-	  /* Convert to a QNaN */
-	  FPU_st0_ptr->sigh |= 0x40000000;
-	}
+      if ( real_2op_NaN(FPU_st0_ptr, FPU_st0_ptr, FPU_st0_ptr) )
+	return;
       push();
       reg_move(st1_ptr, FPU_st0_ptr);
       return;
@@ -279,17 +432,29 @@
 
 static void fdecstp(void)
 {
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   top--;  /* FPU_st0_ptr will be fixed in math_emulate() before the next instr */
 }
 
 static void fincstp(void)
 {
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   top++;  /* FPU_st0_ptr will be fixed in math_emulate() before the next instr */
 }
 
 
 static void fsqrt_(void)
 {
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   if ( !(FPU_st0_tag ^ TW_Valid) )
     {
       int expon;
@@ -329,6 +494,8 @@
 
 static void frndint_(void)
 {
+  int flags;
+
   if ( !(FPU_st0_tag ^ TW_Valid) )
     {
       if (FPU_st0_ptr->exp > EXP_BIAS+63)
@@ -339,7 +506,10 @@
 	return;
 #endif DENORM_OPERAND
 
-      round_to_int(FPU_st0_ptr);  /* Fortunately, this can't overflow to 2^64 */
+      /* Fortunately, this can't overflow to 2^64 */
+      if ( (flags = round_to_int(FPU_st0_ptr)) )
+	set_precision_flag(flags);
+
       FPU_st0_ptr->exp = EXP_BIAS + 63;
       normalize(FPU_st0_ptr);
       return;
@@ -357,42 +527,53 @@
 
   if ( FPU_st0_tag == TW_Valid )
     {
+      FPU_REG rv;
       int q;
-      FPU_st0_ptr->sign = SIGN_POS;
 
-#ifdef DENORM_OPERAND
-      if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	return;
-#endif DENORM_OPERAND
-
-      if ( (q = trig_arg(FPU_st0_ptr)) != -1 )
+      if ( FPU_st0_ptr->exp > EXP_BIAS - 40 )
 	{
-	  FPU_REG rv;
-	  
-	  if (q & 1)
-	    reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
-	  
-	  poly_sine(FPU_st0_ptr, &rv);
-	  
-	  setcc(0);
-	  if (q & 2)
-	    rv.sign ^= SIGN_POS ^ SIGN_NEG;
-	  rv.sign ^= arg_sign;
-	  reg_move(&rv, FPU_st0_ptr);
+	  FPU_st0_ptr->sign = SIGN_POS;
+	  if ( (q = trig_arg(FPU_st0_ptr, 0)) != -1 )
+	    {
+	      reg_div(FPU_st0_ptr, &CONST_PI2, FPU_st0_ptr, FULL_PRECISION);
 
-	  if ( FPU_st0_ptr->exp <= EXP_UNDER )
-	    arith_underflow(FPU_st0_ptr);
+	      poly_sine(FPU_st0_ptr, &rv);
 
-	  set_precision_flag_up();  /* We do not really know if up or down */
+	      if (q & 2)
+		rv.sign ^= SIGN_POS ^ SIGN_NEG;
+	      rv.sign ^= arg_sign;
+	      reg_move(&rv, FPU_st0_ptr);
 
-	  return;
+	      /* We do not really know if up or down */
+	      set_precision_flag_up();
+	      return;
+	    }
+	  else
+	    {
+	      /* Operand is out of range */
+	      FPU_st0_ptr->sign = arg_sign;         /* restore st(0) */
+	      return;
+	    }
 	}
       else
 	{
-	  /* Operand is out of range */
-	  setcc(SW_C2);
-	  FPU_st0_ptr->sign = arg_sign;         /* restore st(0) */
-	  return;
+	  /* For a small arg, the result == the argument */
+	  /* Underflow may happen */
+
+	  if ( FPU_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(FPU_st0_ptr);
+	      return;
+	    }
+
+	  set_precision_flag_up();  /* Must be up. */
 	}
     }
   else if ( FPU_st0_tag == TW_Zero )
@@ -402,9 +583,8 @@
     }
   else if ( FPU_st0_tag == TW_Infinity )
     {
-      /* Operand is out of range */
-      setcc(SW_C2);
-      FPU_st0_ptr->sign = arg_sign;         /* restore st(0) */
+      /* The 80486 treats infinity as an invalid operand */
+      arith_invalid(FPU_st0_ptr);
       return;
     }
   else
@@ -418,38 +598,49 @@
 
   if ( arg->tag == TW_Valid )
     {
+      FPU_REG rv;
       int q;
 
-#ifdef DENORM_OPERAND
-      if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	return 1;
-#endif DENORM_OPERAND
-
-      arg->sign = SIGN_POS;
-      if ( (q = trig_arg(arg)) != -1 )
+      if ( arg->exp > EXP_BIAS - 40 )
 	{
-	  FPU_REG rv;
-	  
-	  if ( !(q & 1) )
-	    reg_sub(&CONST_1, arg, arg, FULL_PRECISION);
-	  
-	  poly_sine(arg, &rv);
-	  
-	  setcc(0);
-	  if ((q+1) & 2)
-	    rv.sign ^= SIGN_POS ^ SIGN_NEG;
-	  reg_move(&rv, arg);
+	  arg->sign = SIGN_POS;
+	  if ( (q = trig_arg(arg, FCOS)) != -1 )
+	    {
+	      reg_div(arg, &CONST_PI2, arg, FULL_PRECISION);
+	      
+	      poly_sine(arg, &rv);
 
-	  set_precision_flag_up();  /* We do not really know if up or down */
+	      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;
+	      return 0;
+	    }
+	  else
+	    {
+	      /* Operand is out of range */
+	      arg->sign = arg_sign;         /* restore st(0) */
+	      return 1;
+	    }
 	}
       else
 	{
-	  /* Operand is out of range */
-	  setcc(SW_C2);
-	  arg->sign = arg_sign;         /* restore st(0) */
-	  return 1;
+#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 )
@@ -460,9 +651,8 @@
     }
   else if ( FPU_st0_tag == TW_Infinity )
     {
-      /* Operand is out of range */
-      setcc(SW_C2);
-      arg->sign = arg_sign;         /* restore st(0) */
+      /* The 80486 treats infinity as an invalid operand */
+      arith_invalid(FPU_st0_ptr);
       return 1;
     }
   else
@@ -483,10 +673,40 @@
 {
   FPU_REG *st_new_ptr;
   FPU_REG arg;
-	  
+
+  /* Stack underflow has higher priority */
+  if ( FPU_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 ( FPU_st0_tag == TW_NaN )
+    {
+      single_arg_2_error();
+      return;
+    }
+  else if ( FPU_st0_tag == TW_Infinity )
+    {
+      /* The 80486 treats infinity as an invalid operand */
+      if ( !arith_invalid(FPU_st0_ptr) )
+	{
+	  /* unmasked response */
+	  push();
+	  arith_invalid(FPU_st0_ptr);
+	}
+      return;
+    }
+
   reg_move(FPU_st0_ptr,&arg);
   if ( !f_cos(&arg) )
     {
@@ -501,84 +721,186 @@
 /*---------------------------------------------------------------------------*/
 /* The following all require two arguments: st(0) and st(1) */
 
-/* remainder of st(0) / st(1) */
-/* Assumes that st(0) and st(1) are both TW_Valid */
-static void fprem_kernel(int round)
+/* 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,
+		       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(int round)
 {
   FPU_REG *st1_ptr = &st(1);
   char st1_tag = st1_ptr->tag;
+  char sign = FPU_st0_ptr->sign;
 
   if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
     {
       FPU_REG tmp;
       int old_cw = control_word;
-      int expdif = FPU_st0_ptr->exp - (st1_ptr)->exp;
+      int expdif = FPU_st0_ptr->exp - st1_ptr->exp;
+      long long q;
+      unsigned short saved_status;
+      int cc = 0;
 
 #ifdef DENORM_OPERAND
-	  if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
-		(st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
-	    return;
+      if ( ((FPU_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 |= round;
-      
+      control_word |= RC_CHOP;
+
       if (expdif < 64)
 	{
 	  /* This should be the most common case */
-	  long long q;
-	  int c = 0;
 
-	  reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION);
-	  
-	  round_to_int(&tmp);  /* Fortunately, this can't overflow to 2^64 */
-	  tmp.exp = EXP_BIAS + 63;
-	  q = *(long long *)&(tmp.sigl);
-	  normalize(&tmp);
-	  
-	  reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION);
-	  reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION);
-	  
-	  if (q&4) c |= SW_C3;
-	  if (q&2) c |= SW_C1;
-	  if (q&1) c |= SW_C0;
-	  
-	  setcc(c);
+	  if ( expdif > -2 )
+	    {
+	      reg_div(FPU_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 = ((long long *)&(tmp.sigl))[0];
+
+		  rem_kernel(((unsigned long long *)&(FPU_st0_ptr->sigl))[0],
+			     (unsigned long long *)&(tmp.sigl),
+			     ((unsigned long long *)&(st1_ptr->sigl))[0],
+			     q, expdif);
+
+		  tmp.exp = st1_ptr->exp;
+		}
+	      else
+		{
+		  reg_move(FPU_st0_ptr, &tmp);
+		  q = 0;
+		}
+	      tmp.sign = sign;
+
+	      if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) )
+		{
+		  /* We may need to subtract st(1) once more. */
+		  unsigned long long x;
+		  x = ((unsigned long long *)&(st1_ptr->sigl))[0] -
+		    ((unsigned long long *)&(tmp.sigl))[0];
+		  if ( x < ((unsigned long long *)&(tmp.sigl))[0] )
+		    {
+		      tmp.sign ^= (SIGN_POS^SIGN_NEG);
+		      ((unsigned long long *)&(tmp.sigl))[0] = x;
+		    }
+		}
+
+	      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 ) */
-	  int N_exp;
-	  
-	  reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION);
-	  /* N is 'a number between 32 and 63' (p26-113) */
-	  N_exp = (tmp.exp & 31) + 32;
-	  tmp.exp = EXP_BIAS + N_exp;
-	  
-	  round_to_int(&tmp);  /* Fortunately, this can't overflow to 2^64 */
-	  tmp.exp = EXP_BIAS + 63;
-	  normalize(&tmp);
-	  
-	  tmp.exp = EXP_BIAS + expdif - N_exp;
-	  
-	  reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION);
-	  reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION);
-	  
-	  setcc(SW_C2);
-	}
-      control_word = old_cw;
+	  /* To make much sense, the code in this section should
+	     be done at high precision. */
+	  int exp_1;
 
-      if ( FPU_st0_ptr->exp <= EXP_UNDER )
-	arith_underflow(FPU_st0_ptr);
+	  /* prevent overflow here */
+	  /* N is 'a number between 32 and 63' (p26-113) */
+	  reg_move(FPU_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(((unsigned long long *)&(FPU_st0_ptr->sigl))[0],
+		     ((unsigned long long *)&(tmp.sigl)),
+		     ((unsigned long long *)&(st1_ptr->sigl))[0],
+		     ((long long *)&(tmp.sigl))[0],
+		     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, FPU_st0_ptr);
+	      FPU_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(&tmp);
+      reg_move(&tmp, FPU_st0_ptr);
+      setcc(cc);
+
+      if ( FPU_st0_ptr->tag != TW_Zero )
+	/* Use round_reg() to properly detect under/overflow etc */
+	round_reg(FPU_st0_ptr, 0, control_word);
+
       return;
     }
   else if ( (FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
-    { stack_underflow(); return; }
+    {
+      stack_underflow();
+      return;
+    }
   else if ( FPU_st0_tag == TW_Zero )
     {
       if ( st1_tag == TW_Valid )
 	{
-
 #ifdef DENORM_OPERAND
 	  if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
 	    return;
@@ -628,7 +950,7 @@
       EXCEPTION(EX_INTERNAL | 0x118);
 #endif PARANOID
 
-  real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+  real_2op_NaN(st1_ptr, FPU_st0_ptr, FPU_st0_ptr);
 
 }
 
@@ -639,6 +961,10 @@
   FPU_REG *st1_ptr = &st(1);
   char st1_tag = st1_ptr->tag;
 
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
     {
       if ( FPU_st0_ptr->sign == SIGN_POS )
@@ -653,7 +979,7 @@
 
 	  /* We use the general purpose arithmetic,
 	     so we need to save these. */
-	  saved_status = status_word;
+	  saved_status = partial_status;
 	  saved_control = control_word;
 	  control_word = FULL_PRECISION;
 
@@ -661,7 +987,7 @@
 
 	  /* Enough of the basic arithmetic is done now */
 	  control_word = saved_control;
-	  status_word = saved_status;
+	  partial_status = saved_status;
 
 	  /* Let the multiply set the flags */
 	  reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
@@ -670,9 +996,9 @@
 	}
       else
 	{
-	  /* negative	*/
-	  pop(); FPU_st0_ptr = &st(0);
-	  arith_invalid(FPU_st0_ptr);  /* st(0) cannot be negative */
+	  /* negative */
+	  if ( !arith_invalid(st1_ptr) )
+	    pop();
 	  return;
 	}
     }
@@ -683,8 +1009,8 @@
     }
   else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
     {
-      real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
-      pop();
+      if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+	pop();
       return;
     }
   else if ( (FPU_st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) )
@@ -692,21 +1018,27 @@
       /* one of the args is zero, the other valid, or both zero */
       if ( FPU_st0_tag == TW_Zero )
 	{
-	  pop(); FPU_st0_ptr = &st(0);
-	  if ( FPU_st0_ptr->tag == TW_Zero )
-	    arith_invalid(FPU_st0_ptr);   /* Both args zero is invalid */
+	  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 ( FPU_st0_ptr->tag == TW_Infinity )
 	    {
-	      reg_move(&CONST_INF, FPU_st0_ptr);
-	      return;
+	      reg_move(&CONST_INF, st1_ptr);
+	      pop();
 	    }
 #endif PECULIAR_486
 	  else
-	    divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, FPU_st0_ptr);
+	    {
+	      if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) )
+		pop();
+	    }
 	  return;
 	}
       else
@@ -715,16 +1047,18 @@
 	  /* Zero is the valid answer */
 	  char sign = st1_ptr->sign;
 
+	  if ( FPU_st0_ptr->sign == SIGN_NEG )
+	    {
+	      /* log(negative) */
+	      if ( !arith_invalid(st1_ptr) )
+		pop();
+	      return;
+	    }
+
 #ifdef DENORM_OPERAND
 	  if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
 	    return;
 #endif DENORM_OPERAND
-	  if ( FPU_st0_ptr->sign == SIGN_NEG )
-	    {
-	      pop(); FPU_st0_ptr = &st(0);
-	      arith_invalid(FPU_st0_ptr);   /* log(negative) */
-	      return;
-	    }
 
 	  if ( FPU_st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS;
 	  pop(); FPU_st0_ptr = &st(0);
@@ -737,9 +1071,12 @@
   else if ( FPU_st0_tag == TW_Infinity )
     {
       if ( (FPU_st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) )
-	{ pop(); FPU_st0_ptr = &st(0);
-	  arith_invalid(FPU_st0_ptr);  /* log(-infinity) or 0*log(infinity) */
-	  return; }
+	{
+	  /* log(-infinity) or 0*log(infinity) */
+	  if ( !arith_invalid(st1_ptr) )
+	    pop();
+	  return;
+	}
       else
 	{
 	  char sign = st1_ptr->sign;
@@ -765,8 +1102,9 @@
 	      (FPU_st0_ptr->sigl == 0) )
 	    {
 	      /* st(0) holds 1.0 */
-	      pop(); FPU_st0_ptr = &st(0);
-	      arith_invalid(FPU_st0_ptr); /* infinity*log(1) */
+	      /* infinity*log(1) */
+	      if ( !arith_invalid(st1_ptr) )
+		pop();
 	      return;
 	    }
 	  /* st(0) is positive and > 1.0 */
@@ -791,17 +1129,20 @@
       /* st(0) must be zero or negative */
       if ( FPU_st0_ptr->tag == TW_Zero )
 	{
-	  pop(); FPU_st0_ptr = st1_ptr;
-	  st1_ptr->sign ^= SIGN_NEG^SIGN_POS;
 	  /* This should be invalid, but a real 80486 is happy with it. */
 #ifndef PECULIAR_486
-	  divide_by_zero(st1_ptr->sign, FPU_st0_ptr);
+	  if ( !divide_by_zero(st1_ptr->sign, st1_ptr) )
 #endif PECULIAR_486
+	    {
+	      st1_ptr->sign ^= SIGN_NEG^SIGN_POS;
+	      pop();
+	    }
 	}
       else
 	{
-	  pop(); FPU_st0_ptr = st1_ptr;
-	  arith_invalid(FPU_st0_ptr);    /* log(negative) */
+	  /* log(negative) */
+	  if ( !arith_invalid(st1_ptr) )
+	    pop();
 	}
       return;
     }
@@ -812,12 +1153,17 @@
 {
   FPU_REG *st1_ptr = &st(1);
   char st1_tag = st1_ptr->tag;
+  char st1_sign = st1_ptr->sign, st0_sign = FPU_st0_ptr->sign;
 
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
     {
       int saved_control, saved_status;
       FPU_REG sum;
-      int quadrant = st1_ptr->sign | ((FPU_st0_ptr->sign)<<1);
+      char inverted;
 
 #ifdef DENORM_OPERAND
       if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
@@ -826,35 +1172,49 @@
 #endif DENORM_OPERAND
 
       /* We use the general purpose arithmetic so we need to save these. */
-      saved_status = status_word;
+      saved_status = partial_status;
       saved_control = control_word;
       control_word = FULL_PRECISION;
 
       st1_ptr->sign = FPU_st0_ptr->sign = SIGN_POS;
-      if ( compare(st1_ptr) == COMP_A_lt_B )
+      if ( (compare(st1_ptr) & ~COMP_Denormal) == COMP_A_lt_B )
 	{
-	  quadrant |= 4;
+	  inverted = 1;
 	  reg_div(FPU_st0_ptr, st1_ptr, &sum, FULL_PRECISION);
 	}
       else
-	reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION);
+	{
+	  inverted = 0;
+	  if ( (st0_sign == 0) &&
+	      (st1_ptr->exp - FPU_st0_ptr->exp < -64) )
+	    {
+	      control_word = saved_control;
+	      partial_status = saved_status;
+	      reg_div(st1_ptr, FPU_st0_ptr, st1_ptr,
+		      control_word | PR_64_BITS);
+	      st1_ptr->sign = st1_sign;
+	      pop();
+	      set_precision_flag_down();
+	      return;
+	    }
+	  reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION);
+	}
 
       poly_atan(&sum);
-      
-      if (quadrant & 4)
+
+      if ( inverted )
 	{
 	  reg_sub(&CONST_PI2, &sum, &sum, FULL_PRECISION);
 	}
-      if (quadrant & 2)
+      if ( st0_sign )
 	{
 	  reg_sub(&CONST_PI, &sum, &sum, FULL_PRECISION);
 	}
-      if (quadrant & 1)
-	sum.sign ^= SIGN_POS^SIGN_NEG;
+      sum.sign = st1_sign;
 
       /* All of the basic arithmetic is done now */
       control_word = saved_control;
-      status_word = saved_status;
+      partial_status = saved_status;
 
       reg_move(&sum, st1_ptr);
     }
@@ -865,8 +1225,8 @@
     }
   else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
     {
-      real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
-      pop();
+      if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+	  pop();
       return;
     }
   else if ( (FPU_st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) )
@@ -883,14 +1243,21 @@
 	    }
 	  else
 	    {
-
 #ifdef DENORM_OPERAND
-	      if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-		return;
+	      if ( st1_tag != TW_Zero )
+		{
+		  if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+		    return;
+		}
 #endif DENORM_OPERAND
 
 	      if ( FPU_st0_ptr->sign == SIGN_POS )
-		{ reg_move(&CONST_Z, st1_ptr); pop(); return; }
+		{
+		  reg_move(&CONST_Z, st1_ptr);
+		  st1_ptr->sign = sign;   /* An 80486 preserves the sign */
+		  pop();
+		  return;
+		}
 	      else
 		reg_move(&CONST_PI, st1_ptr);
 	    }
@@ -899,8 +1266,11 @@
 	{
 	  /* st(1) is infinity, st(0) not infinity */
 #ifdef DENORM_OPERAND
-	  if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	    return;
+	  if ( FPU_st0_tag != TW_Zero )
+	    {
+	      if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+		return;
+	    }
 #endif DENORM_OPERAND
 
 	  reg_move(&CONST_PI2, st1_ptr);
@@ -913,12 +1283,15 @@
       char sign = st1_ptr->sign;
 
 #ifdef DENORM_OPERAND
-      if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	return;
+      if ( FPU_st0_tag != TW_Zero )
+	{
+	  if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	    return;
+	}
 #endif DENORM_OPERAND
 
       if ( FPU_st0_ptr->sign == SIGN_POS )
-	{ reg_move(&CONST_Z, st1_ptr); pop(); return; }
+	{ /* An 80486 preserves the sign */ pop(); return; }
       else
 	reg_move(&CONST_PI, st1_ptr);
       st1_ptr->sign = sign;
@@ -938,7 +1311,7 @@
     }
 #ifdef PARANOID
   else
-    EXCEPTION(EX_INTERNAL | 0x220);
+    EXCEPTION(EX_INTERNAL | 0x125);
 #endif PARANOID
 
   pop();
@@ -948,13 +1321,13 @@
 
 static void fprem(void)
 {
-  fprem_kernel(RC_CHOP);
+  do_fprem(RC_CHOP);
 }
 
 
 static void fprem1(void)
 {
-  fprem_kernel(RC_RND);
+  do_fprem(RC_RND);
 }
 
 
@@ -963,30 +1336,42 @@
   FPU_REG *st1_ptr = &st(1);
   char st1_tag = st1_ptr->tag;
 
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
     {
       int saved_control, saved_status;
 
 #ifdef DENORM_OPERAND
       if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
-	    (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
+	    (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() )
 	return;
 #endif DENORM_OPERAND
 
       /* We use the general purpose arithmetic so we need to save these. */
-      saved_status = status_word;
+      saved_status = partial_status;
       saved_control = control_word;
       control_word = FULL_PRECISION;
 
       if ( poly_l2p1(FPU_st0_ptr, FPU_st0_ptr) )
 	{
-	  arith_invalid(st1_ptr);  /* poly_l2p1() returned invalid */
+#ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
+	  st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+	  control_word = saved_control;
+	  partial_status = saved_status;
+	  set_precision_flag_down();
+#else
+	  if ( arith_invalid(st1_ptr) )  /* poly_l2p1() returned invalid */
+	    return;
+#endif PECULIAR_486
 	  pop(); return;
 	}
       
       /* Enough of the basic arithmetic is done now */
       control_word = saved_control;
-      status_word = saved_status;
+      partial_status = saved_status;
 
       /* Let the multiply set the flags */
       reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
@@ -1002,26 +1387,26 @@
     {
       if ( st1_tag <= TW_Zero )
 	{
-
 #ifdef DENORM_OPERAND
-	      if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
-		  (denormal_operand()) )
-		return;
+	  if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
+	      (denormal_operand()) )
+	    return;
 #endif DENORM_OPERAND
-
-	  st1_ptr->sign ^= FPU_st0_ptr->sign;
+	  
+	  FPU_st0_ptr->sign ^= st1_ptr->sign;
 	  reg_move(FPU_st0_ptr, st1_ptr);
 	}
       else if ( st1_tag == TW_Infinity )
 	{
-	  arith_invalid(st1_ptr);  /* Infinity*log(1) */
-	  pop();
+	  /* Infinity*log(1) */
+	  if ( !arith_invalid(st1_ptr) )
+	    pop();
 	  return;
 	}
       else if ( st1_tag == TW_NaN )
 	{
-	  real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
-	  pop();
+	  if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+	    pop();
 	  return;
 	}
 #ifdef PARANOID
@@ -1042,7 +1427,11 @@
 	      if ( FPU_st0_ptr->exp >= EXP_BIAS )
 		{
 		  /* st(0) holds <= -1.0 */
-		  arith_invalid(st1_ptr); /* infinity*log(1) */
+#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
@@ -1067,7 +1456,11 @@
 		    (FPU_st0_ptr->sigl == 0)) )
 		{
 		  /* st(0) holds < -1.0 */
-		  arith_invalid(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) ) return;
+#endif PECULIAR_486
 		  pop(); return;
 		}
 #ifdef DENORM_OPERAND
@@ -1085,38 +1478,61 @@
 	}
       if ( st1_tag == TW_NaN )
 	{
-	  real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
-	  pop();
+	  if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+	    pop();
 	  return;
 	}
     }
   else if ( FPU_st0_tag == TW_NaN )
     {
-      real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
-      pop();
+      if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+	pop();
       return;
     }
   else if ( FPU_st0_tag == TW_Infinity )
     {
       if ( st1_tag == TW_NaN )
 	{
-	  real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+	  if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+	    pop();
+	  return;
+	}
+      else if ( FPU_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 ( (FPU_st0_ptr->sign == SIGN_NEG) ||
-	       (st1_tag == TW_Zero) )
+      else if ( st1_tag == TW_Zero )
 	{
-	  arith_invalid(st1_ptr);  /* log(infinity) */
-	  pop();
+	  /* 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;
+      if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	return;
 #endif DENORM_OPERAND
 
       /* The Manual says that log(Infinity) is invalid, but a real
@@ -1142,7 +1558,12 @@
   FPU_REG *st1_ptr = &st(1);
   char st1_tag = st1_ptr->tag;
   int old_cw = control_word;
+  char sign = FPU_st0_ptr->sign;
 
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
   if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
     {
       long scale;
@@ -1204,14 +1625,12 @@
 	}
       if ( st1_tag == TW_Infinity )
 	{
-	  char sign = st1_ptr->sign;
-
 #ifdef DENORM_OPERAND
 	  if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
 	    return;
 #endif DENORM_OPERAND
 
-	  if ( sign == SIGN_POS )
+	  if ( st1_ptr->sign == SIGN_POS )
 	    { reg_move(&CONST_INF, FPU_st0_ptr); }
 	  else
 	      reg_move(&CONST_Z, FPU_st0_ptr);
diff --git a/kernel/FPU-emu/get_address.c b/kernel/FPU-emu/get_address.c
index b3d77b6..86ae753 100644
--- a/kernel/FPU-emu/get_address.c
+++ b/kernel/FPU-emu/get_address.c
@@ -40,9 +40,6 @@
 #define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info))
 
 
-void  *FPU_data_address;
-
-
 /* Decode the SIB byte. This function assumes mod != 0 */
 static void *sib(int mod)
 {
@@ -117,7 +114,12 @@
   unsigned char mod;
   long *cpu_reg_ptr;
   int offset = 0;     /* Initialized just to stop compiler warnings. */
-  
+
+#ifndef PECULIAR_486
+  /* This is a reasonable place to do this */
+  FPU_data_selector = FPU_DS;
+#endif PECULIAR_486
+
   mod = (FPU_modrm >> 6) & 3;
 
   if (FPU_rm == 4 && mod != 3)
diff --git a/kernel/FPU-emu/load_store.c b/kernel/FPU-emu/load_store.c
index febe359..33affc0 100644
--- a/kernel/FPU-emu/load_store.c
+++ b/kernel/FPU-emu/load_store.c
@@ -24,6 +24,7 @@
 #include "exception.h"
 #include "fpu_emu.h"
 #include "status_w.h"
+#include "control_w.h"
 
 
 #define _NONE_ 0   /* FPU_st0_ptr etc not needed */
@@ -47,7 +48,8 @@
 
 void load_store_instr(char type)
 {
-  FPU_REG *pop_ptr;  /* We need a version of FPU_st0_ptr which won't change. */
+  FPU_REG *pop_ptr;  /* We need a version of FPU_st0_ptr which won't
+			change if the emulator is re-entered. */
 
   pop_ptr = NULL;    /* Initialized just to stop compiler warnings. */
   switch ( type_table[(int) (unsigned) type] )
@@ -82,53 +84,109 @@
 switch ( type )
   {
   case 000:       /* fld m32real */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     reg_load_single();
-    setcc(0);     /* Clear the SW_C1 bit, "other bits undefined" */
+    if ( (FPU_loaded_data.tag == TW_NaN) &&
+	real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) )
+      {
+	top++;
+	break;
+      }
     reg_move(&FPU_loaded_data, pop_ptr);
     break;
   case 001:      /* fild m32int */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     reg_load_int32();
-    setcc(0);     /* Clear the SW_C1 bit, "other bits undefined" */
     reg_move(&FPU_loaded_data, pop_ptr);
     break;
   case 002:      /* fld m64real */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     reg_load_double();
-    setcc(0);     /* Clear the SW_C1 bit, "other bits undefined" */
+    if ( (FPU_loaded_data.tag == TW_NaN) &&
+	real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) )
+      {
+	top++;
+	break;
+      }
     reg_move(&FPU_loaded_data, pop_ptr);
     break;
   case 003:      /* fild m16int */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     reg_load_int16();
-    setcc(0);     /* Clear the SW_C1 bit, "other bits undefined" */
     reg_move(&FPU_loaded_data, pop_ptr);
     break;
   case 010:      /* fst m32real */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     reg_store_single();
     break;
   case 011:      /* fist m32int */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     reg_store_int32();
     break;
   case 012:     /* fst m64real */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     reg_store_double();
     break;
   case 013:     /* fist m16int */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     reg_store_int16();
     break;
   case 014:     /* fstp m32real */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     if ( reg_store_single() )
       pop_0();  /* pop only if the number was actually stored
 		 (see the 80486 manual p16-28) */
     break;
   case 015:     /* fistp m32int */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     if ( reg_store_int32() )
       pop_0();  /* pop only if the number was actually stored
 		 (see the 80486 manual p16-28) */
     break;
   case 016:     /* fstp m64real */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     if ( reg_store_double() )
       pop_0();  /* pop only if the number was actually stored
 		 (see the 80486 manual p16-28) */
     break;
   case 017:     /* fistp m16int */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     if ( reg_store_int16() )
       pop_0();  /* pop only if the number was actually stored
 		 (see the 80486 manual p16-28) */
@@ -140,72 +198,90 @@
     frstor();
     break;
   case 023:     /* fbld m80dec */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     reg_load_bcd();
-    setcc(0);     /* Clear the SW_C1 bit, "other bits undefined" */
     reg_move(&FPU_loaded_data, pop_ptr);
     break;
   case 024:     /* fldcw */
-    RE_ENTRANT_CHECK_OFF
+    RE_ENTRANT_CHECK_OFF;
     control_word = get_fs_word((unsigned short *) FPU_data_address);
-    RE_ENTRANT_CHECK_ON
-#ifdef NO_UNDERFLOW_TRAP
-    if ( !(control_word & EX_Underflow) )
-      {
-	control_word |= EX_Underflow;
-      }
-#endif
-    FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
-    FPU_entry_eip = ip_offset;               /* We want no net effect */
+    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
+    NO_NET_DATA_EFFECT;
+    NO_NET_INSTR_EFFECT;
     break;
   case 025:      /* fld m80real */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     reg_load_extended();
-    setcc(0);     /* Clear the SW_C1 bit, "other bits undefined" */
     reg_move(&FPU_loaded_data, pop_ptr);
     break;
   case 027:      /* fild m64int */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     reg_load_int64();
-    setcc(0);     /* Clear the SW_C1 bit, "other bits undefined" */
     reg_move(&FPU_loaded_data, pop_ptr);
     break;
   case 030:     /* fstenv  m14/28byte */
     fstenv();
-    FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
-    FPU_entry_eip = ip_offset;               /* We want no net effect */
+    NO_NET_DATA_EFFECT;
     break;
   case 032:      /* fsave */
     fsave();
-    FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
-    FPU_entry_eip = ip_offset;               /* We want no net effect */
+    NO_NET_DATA_EFFECT;
     break;
   case 033:      /* fbstp m80dec */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     if ( reg_store_bcd() )
       pop_0();  /* pop only if the number was actually stored
 		 (see the 80486 manual p16-28) */
     break;
   case 034:      /* fstcw m16int */
-    RE_ENTRANT_CHECK_OFF
+    RE_ENTRANT_CHECK_OFF;
     verify_area(VERIFY_WRITE,FPU_data_address,2);
     put_fs_word(control_word, (short *) FPU_data_address);
-    RE_ENTRANT_CHECK_ON
-    FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
-    FPU_entry_eip = ip_offset;               /* We want no net effect */
+    RE_ENTRANT_CHECK_ON;
+    NO_NET_DATA_EFFECT;
+    NO_NET_INSTR_EFFECT;
     break;
   case 035:      /* fstp m80real */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     if ( reg_store_extended() )
       pop_0();  /* pop only if the number was actually stored
 		 (see the 80486 manual p16-28) */
     break;
   case 036:      /* fstsw m2byte */
-    status_word &= ~SW_Top;
-    status_word |= (top&7) << SW_Top_Shift;
-    RE_ENTRANT_CHECK_OFF
+    RE_ENTRANT_CHECK_OFF;
     verify_area(VERIFY_WRITE,FPU_data_address,2);
-    put_fs_word(status_word,(short *) FPU_data_address);
-    RE_ENTRANT_CHECK_ON
-    FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
-    FPU_entry_eip = ip_offset;               /* We want no net effect */
+    put_fs_word(status_word(),(short *) FPU_data_address);
+    RE_ENTRANT_CHECK_ON;
+    NO_NET_DATA_EFFECT;
+    NO_NET_INSTR_EFFECT;
     break;
   case 037:      /* fistp m64int */
+#ifdef PECULIAR_486
+    /* Default, this conveys no information, but an 80486 does it. */
+    clear_C1();
+#endif PECULIAR_486
     if ( reg_store_int64() )
       pop_0();  /* pop only if the number was actually stored
 		 (see the 80486 manual p16-28) */
diff --git a/kernel/FPU-emu/poly_2xm1.c b/kernel/FPU-emu/poly_2xm1.c
index 5dcccf2..7e8aec7 100644
--- a/kernel/FPU-emu/poly_2xm1.c
+++ b/kernel/FPU-emu/poly_2xm1.c
@@ -3,7 +3,8 @@
  |                                                                           |
  | Function to compute 2^x-1 by a polynomial approximation.                  |
  |                                                                           |
- | Copyright (C) 1992    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
+ | Copyright (C) 1992,1993                                                   |
+ |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
  |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
  |                                                                           |
  |                                                                           |
@@ -35,52 +36,38 @@
 
 
 /*--- poly_2xm1() -----------------------------------------------------------+
- |                                                                           |
+ | Requires a positive argument which is TW_Valid and < 1.                   |
  +---------------------------------------------------------------------------*/
 int	poly_2xm1(FPU_REG *arg, FPU_REG *result)
 {
   short		exponent;
   long long     Xll;
-  FPU_REG           accum;
+  FPU_REG       accum;
 
 
   exponent = arg->exp - EXP_BIAS;
 
-  if ( arg->tag == TW_Zero )
+#ifdef PARANOID
+  if ( (arg->sign != SIGN_POS)	/* Can't hack a number < 0.0 */
+      || (exponent >= 0)    	/* or a |number| >= 1.0 */
+      || (arg->tag != TW_Valid) )
     {
-      /* Return 0.0 */
-      reg_move(&CONST_Z, result);
-      return 0;
-    }
-
-  if ( exponent >= 0 )	/* Can't hack a number >= 1.0 */
-    {
-      arith_invalid(result);  /* Number too large */
+      /* Number negative, too large, or not Valid. */
+      EXCEPTION(EX_INTERNAL|0x127);
       return 1;
     }
-
-  if ( arg->sign != SIGN_POS )	/* Can't hack a number < 0.0 */
-    {
-      arith_invalid(result);  /* Number negative */
-      return 1;
-    }
-  
-  if ( exponent < -64 )
-    {
-      reg_move(&CONST_LN2, result);
-      return 0;
-    }
+#endif PARANOID
 
   *(unsigned *)&Xll = arg->sigl;
   *(((unsigned *)&Xll)+1) = arg->sigh;
   if ( exponent < -1 )
     {
-      /* shift the argument right by the required places */
+      /* Shift the argument right by the required places. */
       if ( shrx(&Xll, -1-exponent) >= 0x80000000U )
 	Xll++;	/* round up */
     }
 
-  *(short *)&(accum.sign) = 0; /* will be a valid positive nr with expon = 0 */
+  *(short *)&(accum.sign) = 0; /* Will be a valid positive nr with expon = 0 */
   accum.exp = 0;
 
   /* Do the basic fixed point polynomial evaluation */
diff --git a/kernel/FPU-emu/poly_atan.c b/kernel/FPU-emu/poly_atan.c
index a549d2b..e9bceee 100644
--- a/kernel/FPU-emu/poly_atan.c
+++ b/kernel/FPU-emu/poly_atan.c
@@ -51,7 +51,7 @@
 {
   char		recursions = 0;
   short		exponent;
-  FPU_REG       odd_poly, even_poly, pos_poly, neg_poly;
+  FPU_REG       odd_poly, even_poly, pos_poly, neg_poly, ratio;
   FPU_REG       argSq;
   long long     arg_signif, argSqSq;
   
@@ -85,6 +85,7 @@
 	    }
 #ifdef PARANOID
 	  EXCEPTION(EX_INTERNAL|0x104);	/* There must be a logic error */
+	  return;
 #endif PARANOID
 	}
 
@@ -162,9 +163,6 @@
   reg_move(&pos_poly, &odd_poly);
   poly_add_1(&odd_poly);
 
-  /* The complete odd polynomial */
-  reg_u_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
-
   /* will be a valid positive nr with expon = 0 */
   *(short *)&(even_poly.sign) = 0;
 
@@ -173,10 +171,13 @@
 
   poly_add_1(&even_poly);
 
-  reg_div(&odd_poly, &even_poly, arg, FULL_PRECISION);
+  reg_div(&odd_poly, &even_poly, &ratio, FULL_PRECISION);
+
+  reg_u_mul(&ratio, arg, arg, FULL_PRECISION);
 
   if ( recursions )
     reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION);
+
 }
 
 
diff --git a/kernel/FPU-emu/poly_sin.c b/kernel/FPU-emu/poly_sin.c
index 98bed99..24c58fe 100644
--- a/kernel/FPU-emu/poly_sin.c
+++ b/kernel/FPU-emu/poly_sin.c
@@ -43,7 +43,7 @@
 void	poly_sine(FPU_REG *arg, FPU_REG *result)
 {
   short	exponent;
-  FPU_REG	Xx, Xx2, Xx4, accum, negaccum;
+  FPU_REG	fixed_arg, arg_sqrd, arg_to_4, accum, negaccum;
   
   
   exponent = arg->exp - EXP_BIAS;
@@ -76,34 +76,34 @@
     }
 #endif PARANOID
   
-  Xx.sigl = arg->sigl;
-  Xx.sigh = arg->sigh;
+  fixed_arg.sigl = arg->sigl;
+  fixed_arg.sigh = arg->sigh;
   if ( exponent < -1 )
     {
       /* shift the argument right by the required places */
-      if ( shrx(&(Xx.sigl), -1-exponent) >= 0x80000000U )
-	(*((long long *)(&(Xx.sigl))))++;	/* round up */
+      if ( shrx(&(fixed_arg.sigl), -1-exponent) >= 0x80000000U )
+	(*((long long *)(&(fixed_arg.sigl))))++;	/* round up */
     }
   
-  mul64((long long *)&(Xx.sigl), (long long *)&(Xx.sigl),
-	(long long *)&(Xx2.sigl));
-  mul64((long long *)&(Xx2.sigl), (long long *)&(Xx2.sigl),
-	(long long *)&(Xx4.sigl));
+  mul64((long long *)&(fixed_arg.sigl), (long long *)&(fixed_arg.sigl),
+	(long long *)&(arg_sqrd.sigl));
+  mul64((long long *)&(arg_sqrd.sigl), (long long *)&(arg_sqrd.sigl),
+	(long long *)&(arg_to_4.sigl));
   
   /* will be a valid positive nr with expon = 0 */
   *(short *)&(accum.sign) = 0;
   accum.exp = 0;
 
   /* Do the basic fixed point polynomial evaluation */
-  polynomial(&(accum.sigl), &(Xx4.sigl), lterms, HIPOWER-1);
+  polynomial(&(accum.sigl), &(arg_to_4.sigl), lterms, HIPOWER-1);
   
   /* will be a valid positive nr with expon = 0 */
   *(short *)&(negaccum.sign) = 0;
   negaccum.exp = 0;
   
   /* Do the basic fixed point polynomial evaluation */
-  polynomial(&(negaccum.sigl), &(Xx4.sigl), negterms, HIPOWER-1);
-  mul64((long long *)&(Xx2.sigl), (long long *)&(negaccum.sigl),
+  polynomial(&(negaccum.sigl), &(arg_to_4.sigl), negterms, HIPOWER-1);
+  mul64((long long *)&(arg_sqrd.sigl), (long long *)&(negaccum.sigl),
 	(long long *)&(negaccum.sigl));
 
   /* Subtract the mantissas */
@@ -111,20 +111,17 @@
   
   /* Convert to 64 bit signed-compatible */
   accum.exp = EXP_BIAS - 1 + accum.exp;
-  
-  *(short *)&(result->sign) = *(short *)&(accum.sign);
-  result->exp = accum.exp;
-  result->sigl = accum.sigl;
-  result->sigh = accum.sigh;
-  
+
+  reg_move(&accum, result);
+
   normalize(result);
 
   reg_mul(result, arg, result, FULL_PRECISION);
   reg_u_add(result, arg, result, FULL_PRECISION);
   
-  /* A small overflow may be possible... but an illegal result. */
   if ( result->exp >= EXP_BIAS )
     {
+      /* A small overflow may be possible... but an illegal result. */
       if (    (result->exp > EXP_BIAS) /* Larger or equal 2.0 */
 	  || (result->sigl > 1)	  /* Larger than 1.0+msb */
 	  ||	(result->sigh != 0x80000000) /* Much > 1.0 */
@@ -149,4 +146,5 @@
 
       result->sigl = 0;	/* Truncate the result to 1.00 */
     }
+
 }
diff --git a/kernel/FPU-emu/poly_tan.c b/kernel/FPU-emu/poly_tan.c
index b56d0e9..8cb202a 100644
--- a/kernel/FPU-emu/poly_tan.c
+++ b/kernel/FPU-emu/poly_tan.c
@@ -49,9 +49,8 @@
 /*--- poly_tan() ------------------------------------------------------------+
  |                                                                           |
  +---------------------------------------------------------------------------*/
-void	poly_tan(FPU_REG *arg, FPU_REG *y_reg)
+void	poly_tan(FPU_REG *arg, FPU_REG *y_reg, int invert)
 {
-  char		invert = 0;
   short		exponent;
   FPU_REG       odd_poly, even_poly, pos_poly, neg_poly;
   FPU_REG       argSq;
@@ -59,39 +58,6 @@
   
 
   exponent = arg->exp - EXP_BIAS;
-  
-  if ( arg->tag == TW_Zero )
-    {
-      /* Return 0.0 */
-      reg_move(&CONST_Z, y_reg);
-      return;
-    }
-
-  if ( exponent >= -1 )
-    {
-      /* argument is in the range  [0.5 .. 1.0] */
-      if ( exponent >= 0 )
-	{
-#ifdef PARANOID
-	  if ( (exponent == 0) && 
-	      (arg->sigl == 0) && (arg->sigh == 0x80000000) )
-#endif PARANOID
-	    {
-	      arith_overflow(y_reg);
-	      return;
-	    }
-#ifdef PARANOID
-	  EXCEPTION(EX_INTERNAL|0x104);	/* There must be a logic error */
-	  return;
-#endif PARANOID
-	}
-      /* The argument is in the range  [0.5 .. 1.0) */
-      /* Convert the argument to a number in the range  (0.0 .. 0.5] */
-      *((long long *)(&arg->sigl)) = - *((long long *)(&arg->sigl));
-      normalize(arg);	/* Needed later */
-      exponent = arg->exp - EXP_BIAS;
-      invert = 1;
-    }
 
 #ifdef PARANOID
   if ( arg->sign != 0 )	/* Can't hack a number < 0.0 */
@@ -135,8 +101,8 @@
   normalize(&odd_poly);
   
   reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
-  reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION);	/* This is just the odd polynomial */
-
+  /* Complete the odd polynomial. */
+  reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION);
 
   /* will be a valid positive nr with expon = 0 */
   *(short *)&(pos_poly.sign) = 0;
@@ -171,7 +137,8 @@
 
   reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION);
   reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION);
-  reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION);  /* This is just the even polynomial */
+  /* Complete the even polynomial */
+  reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION);
 
   /* Now ready to copy the results */
   if ( invert )
diff --git a/kernel/FPU-emu/polynomial.S b/kernel/FPU-emu/polynomial.S
index 3f12fbb..894eded 100644
--- a/kernel/FPU-emu/polynomial.S
+++ b/kernel/FPU-emu/polynomial.S
@@ -22,10 +22,17 @@
 #include "fpu_asm.h"
 
 
-//	#define	EXTRA_PRECISE
+//	#define	EXTRA_PRECISE	// Do not use: not complete
 
 #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	SUM_LS_HI	-25(%ebp)	/* high byte of sum ls */
+#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 ACCUM_LS_HI	-9(%ebp)	/* high byte of accum ls */
 
 .text
 	.align 2,144
@@ -38,101 +45,92 @@
 	pushl	%edi
 	pushl	%ebx
 
-	movl	PARAM1,%esi		// accum
-	movl	PARAM2,%edi		// x
-	movl	PARAM3,%ebx		// terms
-	movl	PARAM4,%ecx		// n
+	movl	PARAM2,%esi		// x
+	movl	PARAM3,%edi		// terms
 
 	movl	TERM_SIZE,%eax
-	mull	%ecx
-	movl	%eax,%ecx
+	mull	PARAM4			// n
+	addl	%eax,%edi
 
-	movl	4(%ebx,%ecx,1),%edx	// terms[n]
-	movl	%edx,-20(%ebp)
-	movl	(%ebx,%ecx,1),%edx	// terms[n]
-	movl	%edx,-24(%ebp)		
+	movl	4(%edi),%edx		// terms[n]
+	movl	%edx,SUM_MS
+	movl	(%edi),%edx		// terms[n]
+	movl	%edx,SUM_MIDDLE
 	xor	%eax,%eax
-	movl	%eax,-28(%ebp)
+	movl	%eax,SUM_LS
 
-	subl	TERM_SIZE,%ecx
+	subl	TERM_SIZE,%edi
+	decl	PARAM4
 	js	L_accum_done
 
 L_accum_loop:
 	xor	%eax,%eax
-	movl	%eax,-4(%ebp)
-	movl	%eax,-8(%ebp)
+	movl	%eax,ACCUM_MS
+	movl	%eax,ACCUM_MIDDLE
 
-#ifdef EXTRA_PRECISE
-	movl	-28(%ebp),%eax
-	mull	4(%edi)			// x ms long
-	movl	%edx,-12(%ebp)
-#endif EXTRA_PRECISE
-
-	movl	-24(%ebp),%eax
-	mull	(%edi)			// x ls long
+	movl	SUM_MIDDLE,%eax
+	mull	(%esi)			// x ls long
 //	movl	%eax,-16(%ebp)		// Not needed
-	addl	%edx,-12(%ebp)
-	adcl	$0,-8(%ebp)
+	movl	%edx,ACCUM_LS
 
-	movl	-24(%ebp),%eax
-	mull	4(%edi)			// x ms long
-	addl	%eax,-12(%ebp)
-	adcl	%edx,-8(%ebp)
-	adcl	$0,-4(%ebp)
+	movl	SUM_MIDDLE,%eax
+	mull	4(%esi)			// x ms long
+	addl	%eax,ACCUM_LS
+	adcl	%edx,ACCUM_MIDDLE
+	adcl	$0,ACCUM_MS
 
-	movl	-20(%ebp),%eax
-	mull	(%edi)
-	addl	%eax,-12(%ebp)
-	adcl	%edx,-8(%ebp)
-	adcl	$0,-4(%ebp)
+	movl	SUM_MS,%eax
+	mull	(%esi)			// x ls long
+	addl	%eax,ACCUM_LS
+	adcl	%edx,ACCUM_MIDDLE
+	adcl	$0,ACCUM_MS
 
-	movl	-20(%ebp),%eax
-	mull	4(%edi)
-	addl	%eax,-8(%ebp)
-	adcl	%edx,-4(%ebp)
+	movl	SUM_MS,%eax
+	mull	4(%esi)			// x ms long
+	addl	%eax,ACCUM_MIDDLE
+	adcl	%edx,ACCUM_MS
 
-/* Now add the next term */
-	movl	(%ebx,%ecx,1),%eax
-	addl	%eax,-8(%ebp)
-	movl	4(%ebx,%ecx,1),%eax
-	adcl	%eax,-4(%ebp)
-
-/* And put into the second register */
-	movl	-4(%ebp),%eax
-	movl	%eax,-20(%ebp)
-	movl	-8(%ebp),%eax
-	movl	%eax,-24(%ebp)
+// Now put the sum of next term and the accumulator
+// into the sum register
+	movl	ACCUM_MIDDLE,%eax
+	addl	(%edi),%eax		// term ls long
+	movl	%eax,SUM_MIDDLE
+	movl	ACCUM_MS,%eax
+	adcl	4(%edi),%eax		// term ms long
+	movl	%eax,SUM_MS
 
 #ifdef EXTRA_PRECISE
-	movl	-12(%ebp),%eax
-	movl	%eax,-28(%ebp)
+	movl	ACCUM_LS,%eax
+	movl	%eax,SUM_LS
 #else
-	testb	$128,-25(%ebp)
+	testb	$0x80,ACCUM_LS_HI	// ms bit of ACCUM_LS
 	je	L_no_poly_round
 
-	addl	$1,-24(%ebp)
-	adcl	$0,-20(%ebp)
+	addl	$1,SUM_MIDDLE
+	adcl	$0,SUM_MS
 L_no_poly_round:
 #endif EXTRA_PRECISE
 
-	subl	TERM_SIZE,%ecx
+	subl	TERM_SIZE,%edi
+	decl	PARAM4
 	jns	L_accum_loop
 
 L_accum_done:
 #ifdef EXTRA_PRECISE
 /* And round the result */
-	testb	$128,-25(%ebp)
+	testb	$128,SUM_LS_HI
 	je	L_poly_done
 
-	addl	$1,-24(%ebp)
-	adcl	$0,-20(%ebp)
+	addl	$1,SUM_MIDDLE
+	adcl	$0,SUM_MS
 #endif EXTRA_PRECISE
 
 L_poly_done:
-	movl	-24(%ebp),%eax
-	movl	%eax,(%esi)
-	movl	-20(%ebp),%eax
-	movl	%eax,4(%esi)
+	movl	PARAM1,%edi		// accum
+	movl	SUM_MIDDLE,%eax
+	movl	%eax,(%edi)
+	movl	SUM_MS,%eax
+	movl	%eax,4(%edi)
 
 	popl	%ebx
 	popl	%edi
diff --git a/kernel/FPU-emu/reg_add_sub.c b/kernel/FPU-emu/reg_add_sub.c
index 380b481..890484e 100644
--- a/kernel/FPU-emu/reg_add_sub.c
+++ b/kernel/FPU-emu/reg_add_sub.c
@@ -22,8 +22,9 @@
 #include "fpu_system.h"
 
 
-void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
+int reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
 {
+  char saved_sign = dest->sign;
   int diff;
   
   if ( !(a->tag | b->tag) )
@@ -32,9 +33,13 @@
       if (!(a->sign ^ b->sign))
 	{
 	  /* signs are the same */
-	  reg_u_add(a, b, dest, control_w);
 	  dest->sign = a->sign;
-	  return;
+	  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 */
@@ -52,11 +57,20 @@
       
       if (diff > 0)
 	{
-	  reg_u_sub(a, b, dest, control_w);
 	  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)
@@ -64,15 +78,19 @@
 	}
       else
 	{
-	  reg_u_sub(b, a, dest, control_w);
 	  dest->sign = b->sign;
+	  if ( reg_u_sub(b, a, dest, control_w) )
+	    {
+	      dest->sign = saved_sign;
+	      return 1;
+	    }
 	}
-      return;
+      return 0;
     }
   else
     {
       if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
-	{ real_2op_NaN(a, b, dest); return; }
+	{ return real_2op_NaN(a, b, dest); }
       else if (a->tag == TW_Zero)
 	{
 	  if (b->tag == TW_Zero)
@@ -93,20 +111,20 @@
 #ifdef DENORM_OPERAND
 	      if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 		  denormal_operand() )
-		return;
+		return 1;
 #endif DENORM_OPERAND
 	      reg_move(b, dest);
 	    }
-	  return;
+	  return 0;
 	}
       else if (b->tag == TW_Zero)
 	{
 #ifdef DENORM_OPERAND
 	  if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 	      denormal_operand() )
-	    return;
+	    return 1;
 #endif DENORM_OPERAND
-	  reg_move(a, dest); return;
+	  reg_move(a, dest); return 0;
 	}
       else if (a->tag == TW_Infinity)
 	{
@@ -115,37 +133,38 @@
 #ifdef DENORM_OPERAND
 	      if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 		  denormal_operand() )
-		return;
+		return 1;
 #endif DENORM_OPERAND
-	      reg_move(a, dest); return;
+	      reg_move(a, dest); return 0;
 	    }
 	  if (a->sign == b->sign)
 	    {
 	      /* They are both + or - infinity */
-	      reg_move(a, dest); return;
+	      reg_move(a, dest); return 0;
 	    }
-	  arith_invalid(dest);	/* Infinity-Infinity is undefined. */
-	  return;
+	  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;
+	    return 1;
 #endif DENORM_OPERAND
-	  reg_move(b, dest); return;
+	  reg_move(b, dest); return 0;
 	}
     }
 #ifdef PARANOID
   EXCEPTION(EX_INTERNAL|0x101);
 #endif
+  return 1;
 }
 
 
 /* Subtract b from a.  (a-b) -> dest */
-void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
+int reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
 {
+  char saved_sign = dest->sign;
   int diff;
 
   if ( !(a->tag | b->tag) )
@@ -162,22 +181,28 @@
 		diff = -(a->sigl < b->sigl);
 	    }
 	}
-      
+
       switch (a->sign*2 + b->sign)
 	{
 	case 0: /* P - P */
 	case 3: /* N - N */
 	  if (diff > 0)
 	    {
-	      reg_u_sub(a, b, dest, control_w);
+	      /* |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;
+		return 1;
 #endif DENORM_OPERAND
 	      reg_move(&CONST_Z, dest);
 	      /* sign depends upon rounding mode */
@@ -186,24 +211,37 @@
 	    }
 	  else
 	    {
-	      reg_u_sub(b, a, dest, control_w);
 	      dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
+	      if ( reg_u_sub(b, a, dest, control_w) )
+		{
+		  dest->sign = saved_sign;
+		  return 1;
+		}
 	    }
-	  return;
+	  break;
 	case 1: /* P - N */
-	  reg_u_add(a, b, dest, control_w);
 	  dest->sign = SIGN_POS;
-	  return;
+	  if ( reg_u_add(a, b, dest, control_w) )
+	    {
+	      dest->sign = saved_sign;
+	      return 1;
+	    }
+	  break;
 	case 2: /* N - P */
-	  reg_u_add(a, b, dest, control_w);
 	  dest->sign = SIGN_NEG;
-	  return;
+	  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) )
-	{ real_2op_NaN(a, b, dest); return; }
+	{ return real_2op_NaN(b, a, dest); }
       else if (b->tag == TW_Zero)
 	{ 
 	  if (a->tag == TW_Zero)
@@ -223,22 +261,22 @@
 #ifdef DENORM_OPERAND
 	      if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 		  denormal_operand() )
-		return;
+		return 1;
 #endif DENORM_OPERAND
 	      reg_move(a, dest);
 	    }
-	  return;
+	  return 0;
 	}
       else if (a->tag == TW_Zero)
 	{
 #ifdef DENORM_OPERAND
 	  if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 	      denormal_operand() )
-	    return;
+	    return 1;
 #endif DENORM_OPERAND
 	  reg_move(b, dest);
 	  dest->sign ^= SIGN_POS^SIGN_NEG;
-	  return;
+	  return 0;
 	}
       else if (a->tag == TW_Infinity)
 	{
@@ -247,33 +285,34 @@
 #ifdef DENORM_OPERAND
 	      if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 		  denormal_operand() )
-		return;
+		return 1;
 #endif DENORM_OPERAND
-	      reg_move(a, dest); return;
+	      reg_move(a, dest); return 0;
 	    }
 	  /* Both args are Infinity */
 	  if (a->sign == b->sign)
 	    {
-	      arith_invalid(dest);	/* Infinity-Infinity is undefined. */
-	      return;
+	      /* Infinity-Infinity is undefined. */
+	      return arith_invalid(dest);
 	    }
 	  reg_move(a, dest);
-	  return;
+	  return 0;
 	}
       else if (b->tag == TW_Infinity)
 	{
 #ifdef DENORM_OPERAND
 	  if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 	      denormal_operand() )
-	    return;
+	    return 1;
 #endif DENORM_OPERAND
 	  reg_move(b, dest);
 	  dest->sign ^= SIGN_POS^SIGN_NEG;
-	  return;
+	  return 0;
 	}
     }
 #ifdef PARANOID
   EXCEPTION(EX_INTERNAL|0x110);
 #endif
+  return 1;
 }
 
diff --git a/kernel/FPU-emu/reg_compare.c b/kernel/FPU-emu/reg_compare.c
index defbcd4..d1ec646 100644
--- a/kernel/FPU-emu/reg_compare.c
+++ b/kernel/FPU-emu/reg_compare.c
@@ -32,22 +32,25 @@
 	  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
-	      if ( (b->exp <= EXP_UNDER) && (denormal_operand()) )
-		return COMP_Denormal;
+		| ((b->exp <= EXP_UNDER) ?
+		   COMP_Denormal : 0)
 #endif DENORM_OPERAND
-	      return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B ;
+		  ;
 	    }
 	}
       else if ( b->tag == TW_Zero )
 	{
 	  if ( FPU_st0_ptr->tag == TW_Valid )
 	    {
+	      return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
+		      : COMP_A_lt_B)
 #ifdef DENORM_OPERAND
-	      if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-		return COMP_Denormal;
+		| ((FPU_st0_ptr->exp <= EXP_UNDER )
+		   ? COMP_Denormal : 0 )
 #endif DENORM_OPERAND
-	      return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B ;
+		  ;
 	    }
 	}
 
@@ -55,12 +58,13 @@
 	{
 	  if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) )
 	    {
+	      return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
+		      : COMP_A_lt_B)
 #ifdef DENORM_OPERAND
-	      if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER)
-		  && (denormal_operand()) )
-		return COMP_Denormal;
+	      | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ?
+		COMP_Denormal : 0 )
 #endif DENORM_OPERAND
-	      return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
+;
 	    }
 	  else if ( b->tag == TW_Infinity )
 	    {
@@ -74,13 +78,13 @@
 	{
 	  if ( (FPU_st0_ptr->tag == TW_Valid) || (FPU_st0_ptr->tag == TW_Zero) )
 	    {
+	      return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
 #ifdef DENORM_OPERAND
-	      if ( (FPU_st0_ptr->tag == TW_Valid)
-		  && (FPU_st0_ptr->exp <= EXP_UNDER)
-		  && (denormal_operand()) )
-		return COMP_Denormal;
+		| (((FPU_st0_ptr->tag == TW_Valid)
+		    && (FPU_st0_ptr->exp <= EXP_UNDER)) ?
+		   COMP_Denormal : 0)
 #endif DENORM_OPERAND
-	      return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
+		  ;
 	    }
 	  /* Fall through to the NaN code */
 	}
@@ -106,15 +110,18 @@
   if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
 #endif PARANOID
 
-#ifdef DENORM_OPERAND
-  if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
-	(b->exp <= EXP_UNDER)) && (denormal_operand()) )
-    return COMP_Denormal;
-#endif DENORM_OPERAND
   
   if (FPU_st0_ptr->sign != b->sign)
-    return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
-  
+    {
+      return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
+#ifdef DENORM_OPERAND
+	|
+	  ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+	   COMP_Denormal : 0)
+#endif DENORM_OPERAND
+	    ;
+    }
+
   diff = FPU_st0_ptr->exp - b->exp;
   if ( diff == 0 )
     {
@@ -129,10 +136,33 @@
     }
 
   if ( diff > 0 )
-    return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B ;
+    {
+      return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
+#ifdef DENORM_OPERAND
+	|
+	  ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+	   COMP_Denormal : 0)
+#endif DENORM_OPERAND
+	    ;
+    }
   if ( diff < 0 )
-    return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B ;
-  return COMP_A_eq_B;
+    {
+      return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
+#ifdef DENORM_OPERAND
+	|
+	  ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+	   COMP_Denormal : 0)
+#endif DENORM_OPERAND
+	    ;
+    }
+
+  return COMP_A_eq_B
+#ifdef DENORM_OPERAND
+    |
+      ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+       COMP_Denormal : 0)
+#endif DENORM_OPERAND
+	;
 
 }
 
@@ -144,21 +174,13 @@
 
   c = compare(&FPU_loaded_data);
 
-  if (c & (COMP_NaN|COMP_Denormal))
+  if (c & COMP_NaN)
     {
-      if (c & COMP_NaN)
-	{
-	  EXCEPTION(EX_Invalid);
-	  f = SW_C3 | SW_C2 | SW_C0;
-	}
-      else
-	{
-	  /* One of the operands is a de-normal */
-	  return 0;
-	}
+      EXCEPTION(EX_Invalid);
+      f = SW_C3 | SW_C2 | SW_C0;
     }
   else
-    switch (c)
+    switch (c & 7)
       {
       case COMP_A_lt_B:
 	f = SW_C0;
@@ -180,7 +202,11 @@
 #endif PARANOID
       }
   setcc(f);
-  return 1;
+  if (c & COMP_Denormal)
+    {
+      return denormal_operand();
+    }
+  return 0;
 }
 
 
@@ -193,26 +219,18 @@
       setcc(SW_C3 | SW_C2 | SW_C0);
       /* Stack fault */
       EXCEPTION(EX_StackUnder);
-      return control_word & CW_Invalid;
+      return !(control_word & CW_Invalid);
     }
 
   c = compare(&st(nr));
-  if (c & (COMP_NaN|COMP_Denormal))
+  if (c & COMP_NaN)
     {
-      if (c & COMP_NaN)
-	{
-	  setcc(SW_C3 | SW_C2 | SW_C0);
-	  EXCEPTION(EX_Invalid);
-	  return control_word & CW_Invalid;
-	}
-      else
-	{
-	  /* One of the operands is a de-normal */
-	  return control_word & CW_Denormal;
-	}
+      setcc(SW_C3 | SW_C2 | SW_C0);
+      EXCEPTION(EX_Invalid);
+      return !(control_word & CW_Invalid);
     }
   else
-    switch (c)
+    switch (c & 7)
       {
       case COMP_A_lt_B:
 	f = SW_C0;
@@ -234,7 +252,11 @@
 #endif PARANOID
       }
   setcc(f);
-  return 1;
+  if (c & COMP_Denormal)
+    {
+      return denormal_operand();
+    }
+  return 0;
 }
 
 
@@ -247,31 +269,23 @@
       setcc(SW_C3 | SW_C2 | SW_C0);
       /* Stack fault */
       EXCEPTION(EX_StackUnder);
-      return control_word & CW_Invalid;
+      return !(control_word & CW_Invalid);
     }
 
   c = compare(&st(nr));
-  if (c & (COMP_NaN|COMP_Denormal))
+  if (c & COMP_NaN)
     {
-      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 */
 	{
-	  setcc(SW_C3 | SW_C2 | SW_C0);
-	  if (c & COMP_SNaN)       /* This is the only difference between
-				      un-ordered and ordinary comparisons */
-	    {
-	      EXCEPTION(EX_Invalid);
-	      return control_word & CW_Invalid;
-	    }
-	  return 1;
+	  EXCEPTION(EX_Invalid);
+	  return !(control_word & CW_Invalid);
 	}
-      else
-	{
-	  /* One of the operands is a de-normal */
-	  return control_word & CW_Denormal;
-	}
+      return 0;
     }
   else
-    switch (c)
+    switch (c & 7)
       {
       case COMP_A_lt_B:
 	f = SW_C0;
@@ -293,34 +307,39 @@
 #endif PARANOID
       }
   setcc(f);
-  return 1;
+  if (c & COMP_Denormal)
+    {
+      return denormal_operand();
+    }
+  return 0;
 }
 
 /*---------------------------------------------------------------------------*/
 
-void fcom_st(void)
+void fcom_st()
 {
   /* fcom st(i) */
   compare_st_st(FPU_rm);
 }
 
 
-void fcompst(void)
+void fcompst()
 {
   /* fcomp st(i) */
-  if ( compare_st_st(FPU_rm) )
+  if ( !compare_st_st(FPU_rm) )
     pop();
 }
 
 
-void fcompp(void)
+void fcompp()
 {
   /* fcompp */
-  if (FPU_rm != 1) {
-    Un_impl();
-    return;
-  }
-  if ( compare_st_st(1) )
+  if (FPU_rm != 1)
+    {
+      Un_impl();
+      return;
+    }
+  if ( !compare_st_st(1) )
     {
       pop(); FPU_st0_ptr = &st(0);
       pop();
@@ -328,7 +347,7 @@
 }
 
 
-void fucom_(void)
+void fucom_()
 {
   /* fucom st(i) */
   compare_u_st_st(FPU_rm);
@@ -336,20 +355,20 @@
 }
 
 
-void fucomp(void)
+void fucomp()
 {
   /* fucomp st(i) */
-  if ( compare_u_st_st(FPU_rm) )
+  if ( !compare_u_st_st(FPU_rm) )
     pop();
 }
 
 
-void fucompp(void)
+void fucompp()
 {
   /* fucompp */
   if (FPU_rm == 1)
     {
-      if ( compare_u_st_st(1) )
+      if ( !compare_u_st_st(1) )
 	{
 	  pop(); FPU_st0_ptr = &st(0);
 	  pop();
diff --git a/kernel/FPU-emu/reg_constant.c b/kernel/FPU-emu/reg_constant.c
index 88c03b6..6cda7f0 100644
--- a/kernel/FPU-emu/reg_constant.c
+++ b/kernel/FPU-emu/reg_constant.c
@@ -37,8 +37,12 @@
 FPU_REG 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_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66,
+			    0xfc8f8cbb, 0xece675d1 };
+
 /* Only the sign (and tag) is used in internal zeroes */
-FPU_REG CONST_Z    = { SIGN_POS, TW_Zero, 0,          0x0,        0x0 };
+FPU_REG 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 
@@ -63,7 +67,10 @@
     }
   push();
   reg_move(c, FPU_st0_ptr);
-  status_word &= ~SW_C1;
+#ifdef PECULIAR_486
+  /* Default, this conveys no information, but an 80486 does it. */
+  clear_C1();
+#endif PECULIAR_486
 }
 
 
diff --git a/kernel/FPU-emu/reg_constant.h b/kernel/FPU-emu/reg_constant.h
index a039bc6..23ba06d 100644
--- a/kernel/FPU-emu/reg_constant.h
+++ b/kernel/FPU-emu/reg_constant.h
@@ -18,6 +18,7 @@
 extern FPU_REG CONST_L2E;
 extern FPU_REG CONST_PI;
 extern FPU_REG CONST_PI2;
+extern FPU_REG CONST_PI2extra;
 extern FPU_REG CONST_PI4;
 extern FPU_REG CONST_LG2;
 extern FPU_REG CONST_LN2;
diff --git a/kernel/FPU-emu/reg_div.S b/kernel/FPU-emu/reg_div.S
index 854e617..9d65f60 100644
--- a/kernel/FPU-emu/reg_div.S
+++ b/kernel/FPU-emu/reg_div.S
@@ -86,8 +86,9 @@
 L_arg1_NaN:
 L_arg2_NaN:
 	pushl	%edi			// Destination
-	pushl	%ebx
+//	pushl	%ebx
 	pushl	%esi
+	pushl	%ebx			// Ordering is important here
 	call	_real_2op_NaN
 	jmp	LDiv_exit
 
@@ -196,23 +197,18 @@
 	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)
-	jmp	LDiv_exit
-
-LDiv_set_result_sign:
-	movb	SIGN(%esi),%cl
-	cmpb	%cl,SIGN(%edi)
-	jne	LDiv_negative_result
-
-	movb	SIGN_POS,SIGN(%ebx)
+	xorl	%eax,%eax		// Valid result
 	jmp	LDiv_exit
 
 LDiv_negative_result:
 	movb	SIGN_NEG,SIGN(%edi)
+	xorl	%eax,%eax		// Valid result
 
 LDiv_exit:
 	leal	-12(%ebp),%esp
@@ -225,6 +221,10 @@
 
 
 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
 
@@ -240,5 +240,5 @@
 	movl	%eax,SIGL(%edi)
 	movl	_CONST_QNaN+8,%eax
 	movl	%eax,SIGH(%edi)
-	jmp	LDiv_exit
+	jmp	LDiv_exit		// %eax is nz
 #endif PARANOID
diff --git a/kernel/FPU-emu/reg_ld_str.c b/kernel/FPU-emu/reg_ld_str.c
index 8cbef5a..1ddc553 100644
--- a/kernel/FPU-emu/reg_ld_str.c
+++ b/kernel/FPU-emu/reg_ld_str.c
@@ -27,7 +27,6 @@
 #include "status_w.h"
 
 
-#define EXTENDED_Emax 0x3fff     /* largest valid exponent */
 #define EXTENDED_Ebias 0x3fff
 #define EXTENDED_Emin (-0x3ffe)  /* smallest valid exponent */
 
@@ -39,14 +38,13 @@
 #define SINGLE_Ebias 127
 #define SINGLE_Emin (-126)       /* smallest valid exponent */
 
-#define LOST_UP    (EX_Precision | SW_C1)
-#define LOST_DOWN  EX_Precision
+static void write_to_extended(FPU_REG *rp, char *d);
 
 FPU_REG FPU_loaded_data;
 
 
 /* Get a long double from user memory */
-void reg_load_extended(void)
+int reg_load_extended(void)
 {
   long double *s = (long double *)FPU_data_address;
   unsigned long sigl, sigh, exp;
@@ -59,63 +57,94 @@
   exp = get_fs_word(4 + (unsigned short *) s);
   RE_ENTRANT_CHECK_ON
 
+  FPU_loaded_data.tag = TW_Valid;   /* Default */
   FPU_loaded_data.sigl = sigl;
   FPU_loaded_data.sigh = sigh;
-  FPU_loaded_data.exp = exp;
-
-  if (FPU_loaded_data.exp & 0x8000)
+  if (exp & 0x8000)
     FPU_loaded_data.sign = SIGN_NEG;
   else
     FPU_loaded_data.sign = SIGN_POS;
-  if ( (FPU_loaded_data.exp &= 0x7fff) == 0 )
+  exp &= 0x7fff;
+  FPU_loaded_data.exp = exp - EXTENDED_Ebias + EXP_BIAS;
+
+  /* Assume that optimisation can keep sigl, sigh, and exp in
+     registers, otherwise it would be more efficient to work
+     with FPU_loaded_data (which is static) here. */
+  if ( exp == 0 )
     {
-      if ( !(FPU_loaded_data.sigl | FPU_loaded_data.sigh) )
+      if ( !(sigh | sigl) )
 	{
 	  FPU_loaded_data.tag = TW_Zero;
-	  return;
+	  return 0;
 	}
       /* The number is a de-normal or pseudodenormal. */
-      /* The 80486 doesn't regard pseudodenormals as denormals here. */
-      if ( !(FPU_loaded_data.sigh & 0x80000000) )
-	EXCEPTION(EX_Denormal);
-      FPU_loaded_data.exp++;
-
-      /* The default behaviour will now take care of it. */
+      if (sigh & 0x80000000)
+	{
+	  /* Is a pseudodenormal. */
+	  /* Convert it for internal use. */
+	  /* This is non-80486 behaviour because the number
+	     loses its 'denormal' identity. */
+	  FPU_loaded_data.exp++;
+	  return 1;
+	}
+      else
+	{
+	  /* Is a denormal. */
+	  /* Convert it for internal use. */
+	  FPU_loaded_data.exp++;
+	  normalize_nuo(&FPU_loaded_data);
+	  return 0;
+	}
     }
-  else if ( FPU_loaded_data.exp == 0x7fff )
+  else if ( exp == 0x7fff )
     {
-      FPU_loaded_data.exp = EXTENDED_Emax;
-      if ( (FPU_loaded_data.sigh == 0x80000000)
-	  && (FPU_loaded_data.sigl == 0) )
+      if ( !((sigh ^ 0x80000000) | sigl) )
 	{
+	  /* Matches the bit pattern for Infinity. */
+	  FPU_loaded_data.exp = EXP_Infinity;
 	  FPU_loaded_data.tag = TW_Infinity;
-	  return;
+	  return 0;
 	}
-      else if ( !(FPU_loaded_data.sigh & 0x80000000) )
-	{
-	  /* Unsupported NaN data type */
-	  EXCEPTION(EX_Invalid);
-	  FPU_loaded_data.tag = TW_NaN;
-	  return;
-	}
+
+      FPU_loaded_data.exp = EXP_NaN;
       FPU_loaded_data.tag = TW_NaN;
-      return;
+      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 */
+	  FPU_loaded_data.sigh = 0x80000000;
+	  FPU_loaded_data.sigl = 0x00000001;
+	  FPU_loaded_data.sign = SIGN_NEG;
+	  return 1;
+	}
+      return 0;
     }
-  FPU_loaded_data.exp = (FPU_loaded_data.exp & 0x7fff) - EXTENDED_Ebias
-    + EXP_BIAS;
-  FPU_loaded_data.tag = TW_Valid;
 
   if ( !(sigh & 0x80000000) )
     {
-      /* Unsupported data type */
-      EXCEPTION(EX_Invalid);
-      normalize_nuo(&FPU_loaded_data);
+      /* 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 */
+      FPU_loaded_data.sigh = 0x80000000;
+      FPU_loaded_data.sigl = 0x00000001;
+      FPU_loaded_data.sign = SIGN_NEG;
+      FPU_loaded_data.exp = EXP_NaN;
+      FPU_loaded_data.tag = TW_NaN;
+      return 1;
     }
+  return 0;
 }
 
 
 /* Get a double from user memory */
-void reg_load_double(void)
+int reg_load_double(void)
 {
   double *dfloat = (double *)FPU_data_address;
   int exp;
@@ -138,19 +167,21 @@
       if ((m64 == 0) && (l64 == 0))
 	{
 	  /* +- infinity */
-	  FPU_loaded_data.exp = EXTENDED_Emax;
+	  FPU_loaded_data.sigh = 0x80000000;
+	  FPU_loaded_data.sigl = 0x00000000;
+	  FPU_loaded_data.exp = EXP_Infinity;
 	  FPU_loaded_data.tag = TW_Infinity;
-	  return;
+	  return 0;
 	}
       else
 	{
 	  /* Must be a signaling or quiet NaN */
-	  FPU_loaded_data.exp = EXTENDED_Emax;
+	  FPU_loaded_data.exp = EXP_NaN;
 	  FPU_loaded_data.tag = TW_NaN;
 	  FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
 	  FPU_loaded_data.sigh |= l64 >> 21;
 	  FPU_loaded_data.sigl = l64 << 11;
-	  return;
+	  return 0; /* The calling function must look for NaNs */
 	}
     }
   else if ( exp < DOUBLE_Emin )
@@ -162,19 +193,18 @@
 	  int c = FPU_loaded_data.sign;
 	  reg_move(&CONST_Z, &FPU_loaded_data);
 	  FPU_loaded_data.sign = c;
-	  return;
+	  return 0;
 	}
       else
 	{
 	  /* De-normal */
-	  EXCEPTION(EX_Denormal);
 	  FPU_loaded_data.exp = DOUBLE_Emin + EXP_BIAS;
 	  FPU_loaded_data.tag = TW_Valid;
 	  FPU_loaded_data.sigh = m64 << 11;
 	  FPU_loaded_data.sigh |= l64 >> 21;
 	  FPU_loaded_data.sigl = l64 << 11;
 	  normalize_nuo(&FPU_loaded_data);
-	  return;
+	  return denormal_operand();
 	}
     }
   else
@@ -185,13 +215,13 @@
       FPU_loaded_data.sigh |= l64 >> 21;
       FPU_loaded_data.sigl = l64 << 11;
 
-      return;
+      return 0;
     }
 }
 
 
 /* Get a float from user memory */
-void reg_load_single(void)
+int reg_load_single(void)
 {
   float *single = (float *)FPU_data_address;
   unsigned m32;
@@ -211,20 +241,19 @@
       int c = FPU_loaded_data.sign;
       reg_move(&CONST_Z, &FPU_loaded_data);
       FPU_loaded_data.sign = c;
-      return;
+      return 0;
     }
   exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
   m32 = (m32 & 0x7fffff) << 8;
   if ( exp < SINGLE_Emin )
     {
       /* De-normals */
-      EXCEPTION(EX_Denormal);
       FPU_loaded_data.exp = SINGLE_Emin + EXP_BIAS;
       FPU_loaded_data.tag = TW_Valid;
       FPU_loaded_data.sigh = m32;
       FPU_loaded_data.sigl = 0;
       normalize_nuo(&FPU_loaded_data);
-      return;
+      return denormal_operand();
     }
   else if ( exp > SINGLE_Emax )
     {
@@ -232,18 +261,20 @@
       if ( m32 == 0 )
 	{
 	  /* +- infinity */
-	  FPU_loaded_data.exp = EXTENDED_Emax;
+	  FPU_loaded_data.sigh = 0x80000000;
+	  FPU_loaded_data.sigl = 0x00000000;
+	  FPU_loaded_data.exp = EXP_Infinity;
 	  FPU_loaded_data.tag = TW_Infinity;
-	  return;
+	  return 0;
 	}
       else
 	{
 	  /* Must be a signaling or quiet NaN */
-	  FPU_loaded_data.exp = EXTENDED_Emax;
+	  FPU_loaded_data.exp = EXP_NaN;
 	  FPU_loaded_data.tag = TW_NaN;
 	  FPU_loaded_data.sigh = m32 | 0x80000000;
 	  FPU_loaded_data.sigl = 0;
-	  return;
+	  return 0; /* The calling function must look for NaNs */
 	}
     }
   else
@@ -252,6 +283,7 @@
       FPU_loaded_data.sigh = m32 | 0x80000000;
       FPU_loaded_data.sigl = 0;
       FPU_loaded_data.tag = TW_Valid;
+      return 0;
     }
 }
 
@@ -397,122 +429,36 @@
 /* Put a long double into user memory */
 int reg_store_extended(void)
 {
+  /*
+    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.
+   */
   long double *d = (long double *)FPU_data_address;
-  long e = FPU_st0_ptr->exp - EXP_BIAS + EXTENDED_Ebias;
-  unsigned short sign = FPU_st0_ptr->sign*0x8000;
-  unsigned long ls, ms;
 
+  if ( FPU_st0_tag != TW_Empty )
+    {
+      verify_area(VERIFY_WRITE, d, 10);
+      write_to_extended(FPU_st0_ptr, (char *) FPU_data_address);
+      return 1;
+    }
 
-  if ( FPU_st0_tag == TW_Valid )
+  /* Empty register (stack underflow) */
+  EXCEPTION(EX_StackUnder);
+  if ( control_word & CW_Invalid )
     {
-      if ( e >= 0x7fff )
-	{
-	  EXCEPTION(EX_Overflow);  /* Overflow */
-	  /* This is a special case: see sec 16.2.5.1 of the 80486 book */
-	  if ( control_word & EX_Overflow )
-	    {
-	      /* Overflow to infinity */
-	      ls = 0;
-	      ms = 0x80000000;
-	      e = 0x7fff;
-	    }
-	  else
-	    return 0;
-	}
-      else if ( e <= 0 )
-	{
-	  if ( e > -63 )
-	    {
-	      /* Correctly format the de-normal */
-	      int precision_loss;
-	      FPU_REG tmp;
-
-	      EXCEPTION(EX_Denormal);
-	      reg_move(FPU_st0_ptr, &tmp);
-	      tmp.exp += -EXTENDED_Emin + 63;  /* largest exp to be 62 */
-	      if ( (precision_loss = round_to_int(&tmp)) )
-		{
-		  EXCEPTION(EX_Underflow | precision_loss);
-		  /* This is a special case: see sec 16.2.5.1 of
-		     the 80486 book */
-		  if ( !(control_word & EX_Underflow) )
-		    return 0;
-		}
-	      e = 0;
-	      ls = tmp.sigl;
-	      ms = tmp.sigh;
-	    }
-	  else
-	    {
-	      /* ****** ??? This should not be possible */
-	      EXCEPTION(EX_Underflow);  /* Underflow */
-	      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
-	      if ( control_word & EX_Underflow )
-		{
-		  /* Underflow to zero */
-		  ls = 0;
-		  ms = 0;
-		  e = FPU_st0_ptr->sign == SIGN_POS ? 0x7fff : 0xffff;
-		}
-	      else
-		return 0;
-	    }
-	}
-      else
-	{
-	  ls = FPU_st0_ptr->sigl;
-	  ms = FPU_st0_ptr->sigh;
-	}
-    }
-  else if ( FPU_st0_tag == TW_Zero )
-    {
-      ls = ms = 0;
-      e = 0;
-    }
-  else if ( FPU_st0_tag == TW_Infinity )
-    {
-      ls = 0;
-      ms = 0x80000000;
-      e = 0x7fff;
-    }
-  else if ( FPU_st0_tag == TW_NaN )
-    {
-      ls = FPU_st0_ptr->sigl;
-      ms = FPU_st0_ptr->sigh;
-      e = 0x7fff;
-    }
-  else if ( FPU_st0_tag == TW_Empty )
-    {
-      /* Empty register (stack underflow) */
-      EXCEPTION(EX_StackUnder);
-      if ( control_word & EX_Invalid )
-	{
-	  /* The masked response */
-	  /* Put out the QNaN indefinite */
-	  ls = 0;
-	  ms = 0xc0000000;
-	  e = 0xffff;
-	}
-      else
-	return 0;
+      /* The masked response */
+      /* Put out the QNaN indefinite */
+      RE_ENTRANT_CHECK_OFF;
+      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
-    {
-      /* We don't use TW_Denormal yet ... perhaps never! */
-      EXCEPTION(EX_Invalid);
-      /* Store a NaN */
-      e = 0x7fff;
-      ls = 1;
-      ms = 0x80000000;
-    }
-  RE_ENTRANT_CHECK_OFF
-  verify_area(VERIFY_WRITE,d,10);
-  put_fs_long(ls, (unsigned long *) d);
-  put_fs_long(ms, 1 + (unsigned long *) d);
-  put_fs_word((unsigned short)e | sign, 4 + (short *) d);
-  RE_ENTRANT_CHECK_ON
-
-  return 1;
+    return 0;
 
 }
 
@@ -522,6 +468,7 @@
 {
   double *dfloat = (double *)FPU_data_address;
   unsigned long l[2];
+  unsigned long increment = 0;	/* avoid gcc warnings */
 
   if (FPU_st0_tag == TW_Valid)
     {
@@ -533,11 +480,19 @@
 
       if ( exp < DOUBLE_Emin )     /* It may be a denormal */
 	{
-	  /* Make a de-normal */
 	  int precision_loss;
 
-	  if ( exp <= -EXTENDED_Ebias )
-	    EXCEPTION(EX_Denormal);
+	  /* A denormal will always underflow. */
+#ifndef PECULIAR_486
+	  /* An 80486 is supposed to be able to generate
+	     a denormal exception here, but... */
+	  if ( FPU_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 */
 
@@ -548,18 +503,19 @@
 	      /* This behaviour might be regarded as peculiar, it appears
 		 that the 80486 rounds to the dest precision, then
 		 converts to decide underflow. */
-	      if ( (tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
-		  (FPU_st0_ptr->sigl & 0x000007ff) )
-		EXCEPTION(precision_loss);
-	      else
+	      if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
+		  (FPU_st0_ptr->sigl & 0x000007ff)) )
 #endif PECULIAR_486
 		{
-		  EXCEPTION(EX_Underflow | precision_loss);
+		  EXCEPTION(EX_Underflow);
 		  /* This is a special case: see sec 16.2.5.1 of
 		     the 80486 book */
-		  if ( !(control_word & EX_Underflow) )
+		  if ( !(control_word & CW_Underflow) )
 		    return 0;
 		}
+	      EXCEPTION(precision_loss);
+	      if ( !(control_word & CW_Precision) )
+		return 0;
 	    }
 	  l[0] = tmp.sigl;
 	  l[1] = tmp.sigh;
@@ -568,8 +524,6 @@
 	{
 	  if ( tmp.sigl & 0x000007ff )
 	    {
-	      unsigned long increment = 0;	/* avoid gcc warnings */
-	      
 	      switch (control_word & CW_RC)
 		{
 		case RC_RND:
@@ -629,15 +583,16 @@
 	    {
 	    overflow:
 	      EXCEPTION(EX_Overflow);
-	      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
-	      if ( control_word & EX_Overflow )
-		{
-		  /* Overflow to infinity */
-		  l[0] = 0x00000000;	/* Set to */
-		  l[1] = 0x7ff00000;	/* + INF */
-		}
-	      else
+	      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
 	    {
@@ -662,12 +617,13 @@
       /* See if we can get a valid NaN from the FPU_REG */
       l[0] = (FPU_st0_ptr->sigl >> 11) | (FPU_st0_ptr->sigh << 21);
       l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff);
-      if ( !(l[0] | l[1]) )
+      if ( !(FPU_st0_ptr->sigh & 0x40000000) )
 	{
-	  /* This case does not seem to be handled by the 80486 specs */
+	  /* It is a signalling NaN */
 	  EXCEPTION(EX_Invalid);
-	  /* Make the quiet NaN "real indefinite" */
-	  goto put_indefinite;
+	  if ( !(control_word & CW_Invalid) )
+	    return 0;
+	  l[1] |= (0x40000000 >> 11);
 	}
       l[1] |= 0x7ff00000;
     }
@@ -675,11 +631,10 @@
     {
       /* Empty register (stack underflow) */
       EXCEPTION(EX_StackUnder);
-      if ( control_word & EX_Invalid )
+      if ( control_word & CW_Invalid )
 	{
 	  /* The masked response */
 	  /* Put out the QNaN indefinite */
-put_indefinite:
 	  RE_ENTRANT_CHECK_OFF
 	  verify_area(VERIFY_WRITE,(void *)dfloat,8);
 	  put_fs_long(0, (unsigned long *) dfloat);
@@ -690,14 +645,6 @@
       else
 	return 0;
     }
-#if 0 /* TW_Denormal is not used yet, and probably won't be */
-  else if (FPU_st0_tag == TW_Denormal)
-    {
-      /* Extended real -> double real will always underflow */
-      l[0] = l[1] = 0;
-      EXCEPTION(EX_Underflow);
-    }
-#endif
   if (FPU_st0_ptr->sign)
     l[1] |= 0x80000000;
 
@@ -716,6 +663,7 @@
 {
   float *single = (float *)FPU_data_address;
   long templ;
+  unsigned long increment = 0;     	/* avoid gcc warnings */
 
   if (FPU_st0_tag == TW_Valid)
     {
@@ -727,11 +675,19 @@
 
       if ( exp < SINGLE_Emin )
 	{
-	  /* Make a de-normal */
 	  int precision_loss;
 
-	  if ( exp <= -EXTENDED_Ebias )
-	    EXCEPTION(EX_Denormal);
+	  /* A denormal will always underflow. */
+#ifndef PECULIAR_486
+	  /* An 80486 is supposed to be able to generate
+	     a denormal exception here, but... */
+	  if ( FPU_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 */
 
@@ -742,18 +698,19 @@
 	      /* This behaviour might be regarded as peculiar, it appears
 		 that the 80486 rounds to the dest precision, then
 		 converts to decide underflow. */
-	      if ( (tmp.sigl == 0x00800000) &&
-		  ((FPU_st0_ptr->sigh & 0x000000ff) || FPU_st0_ptr->sigl) )
-		EXCEPTION(precision_loss);
-	      else
+	      if ( !((tmp.sigl == 0x00800000) &&
+		  ((FPU_st0_ptr->sigh & 0x000000ff) || FPU_st0_ptr->sigl)) )
 #endif PECULIAR_486
 		{
-		  EXCEPTION(EX_Underflow | precision_loss);
+		  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;
 	}
@@ -761,7 +718,6 @@
 	{
 	  if ( tmp.sigl | (tmp.sigh & 0x000000ff) )
 	    {
-	      unsigned long increment = 0;     	/* avoid gcc warnings */
 	      unsigned long sigh = tmp.sigh;
 	      unsigned long sigl = tmp.sigl;
 	      
@@ -819,14 +775,15 @@
 	    {
 	    overflow:
 	      EXCEPTION(EX_Overflow);
-	      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
-	      if ( control_word & EX_Overflow )
-		{
-		  /* Overflow to infinity */
-		  templ = 0x7f800000;
-		}
-	      else
+	      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 respose is overflow to infinity. */
+	      templ = 0x7f800000;
 	    }
 	  else
 	    templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
@@ -844,12 +801,13 @@
     {
       /* See if we can get a valid NaN from the FPU_REG */
       templ = FPU_st0_ptr->sigh >> 8;
-      if ( !(templ & 0x3fffff) )
+      if ( !(FPU_st0_ptr->sigh & 0x40000000) )
 	{
-	  /* This case does not seem to be handled by the 80486 specs */
+	  /* It is a signalling NaN */
 	  EXCEPTION(EX_Invalid);
-	  /* Make the quiet NaN "real indefinite" */
-	  goto put_indefinite;
+	  if ( !(control_word & CW_Invalid) )
+	    return 0;
+	  templ |= (0x40000000 >> 8);
 	}
       templ |= 0x7f800000;
     }
@@ -861,7 +819,6 @@
 	{
 	  /* The masked response */
 	  /* Put out the QNaN indefinite */
-put_indefinite:
 	  RE_ENTRANT_CHECK_OFF
 	  verify_area(VERIFY_WRITE,(void *)single,4);
 	  put_fs_long(0xffc00000, (unsigned long *) single);
@@ -871,14 +828,6 @@
       else
 	return 0;
     }
-#if 0 /* TW_Denormal is not used yet, and probably won't be */
-  else if (FPU_st0_tag == TW_Denormal)
-    {
-      /* Extended real -> real will always underflow */
-      templ = 0;
-      EXCEPTION(EX_Underflow);
-    }
-#endif
 #ifdef PARANOID
   else
     {
@@ -904,42 +853,48 @@
   long long *d = (long long *)FPU_data_address;
   FPU_REG t;
   long long tll;
+  int precision_loss;
 
   if ( FPU_st0_tag == TW_Empty )
     {
       /* Empty register (stack underflow) */
       EXCEPTION(EX_StackUnder);
-      if ( control_word & EX_Invalid )
-	{
-	  /* The masked response */
-	  /* Put out the QNaN indefinite */
-	  goto put_indefinite;
-	}
-      else
-	return 0;
+      goto invalid_operand;
+    }
+  else if ( (FPU_st0_tag == TW_Infinity) ||
+	   (FPU_st0_tag == TW_NaN) )
+    {
+      EXCEPTION(EX_Invalid);
+      goto invalid_operand;
     }
 
   reg_move(FPU_st0_ptr, &t);
-  round_to_int(&t);
+  precision_loss = round_to_int(&t);
   ((long *)&tll)[0] = t.sigl;
   ((long *)&tll)[1] = t.sigh;
-  if ( (t.sigh & 0x80000000) &&
-      !((t.sigh == 0x80000000) && (t.sigl == 0) && (t.sign == SIGN_NEG)) )
+  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 "indefinite" */
-put_indefinite:
-	  ((long *)&tll)[1] = 0x80000000;
-	  ((long *)&tll)[0] = 0;
+	  /* Produce something like QNaN "indefinite" */
+	  tll = 0x8000000000000000LL;
 	}
       else
 	return 0;
     }
-  else if ( t.sign )
-    tll = - tll;
+  else
+    {
+      if ( precision_loss )
+	set_precision_flag(precision_loss);
+      if ( t.sign )
+	tll = - tll;
+    }
 
   RE_ENTRANT_CHECK_OFF
   verify_area(VERIFY_WRITE,(void *)d,8);
@@ -956,43 +911,45 @@
 {
   long *d = (long *)FPU_data_address;
   FPU_REG t;
+  int precision_loss;
 
   if ( FPU_st0_tag == TW_Empty )
     {
       /* Empty register (stack underflow) */
       EXCEPTION(EX_StackUnder);
-      if ( control_word & EX_Invalid )
-	{
-	  /* The masked response */
-	  /* Put out the QNaN indefinite */
-	  RE_ENTRANT_CHECK_OFF
-	  verify_area(VERIFY_WRITE,d,4);
-	  put_fs_long(0x80000000, (unsigned long *) d);
-	  RE_ENTRANT_CHECK_ON
-	  return 1;
-	}
-      else
-	return 0;
+      goto invalid_operand;
+    }
+  else if ( (FPU_st0_tag == TW_Infinity) ||
+	   (FPU_st0_tag == TW_NaN) )
+    {
+      EXCEPTION(EX_Invalid);
+      goto invalid_operand;
     }
 
   reg_move(FPU_st0_ptr, &t);
-  round_to_int(&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 "indefinite" */
+	  /* Produce something like QNaN "indefinite" */
 	  t.sigl = 0x80000000;
 	}
       else
 	return 0;
     }
-  else if ( t.sign )
-    t.sigl = -(long)t.sigl;
+  else
+    {
+      if ( precision_loss )
+	set_precision_flag(precision_loss);
+      if ( t.sign )
+	t.sigl = -(long)t.sigl;
+    }
 
   RE_ENTRANT_CHECK_OFF
   verify_area(VERIFY_WRITE,d,4);
@@ -1008,44 +965,45 @@
 {
   short *d = (short *)FPU_data_address;
   FPU_REG t;
-  short ts;
+  int precision_loss;
 
   if ( FPU_st0_tag == TW_Empty )
     {
       /* Empty register (stack underflow) */
       EXCEPTION(EX_StackUnder);
-      if ( control_word & EX_Invalid )
-	{
-	  /* The masked response */
-	  /* Put out the QNaN indefinite */
-	  RE_ENTRANT_CHECK_OFF
-	  verify_area(VERIFY_WRITE,d,2);
-	  put_fs_word(0x8000, (unsigned short *) d);
-	  RE_ENTRANT_CHECK_ON
-	  return 1;
-	}
-      else
-	return 0;
+      goto invalid_operand;
+    }
+  else if ( (FPU_st0_tag == TW_Infinity) ||
+	   (FPU_st0_tag == TW_NaN) )
+    {
+      EXCEPTION(EX_Invalid);
+      goto invalid_operand;
     }
 
   reg_move(FPU_st0_ptr, &t);
-  round_to_int(&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 "indefinite" */
-	  ts = 0x8000;
+	  /* Produce something like QNaN "indefinite" */
+	  t.sigl = 0x8000;
 	}
       else
 	return 0;
     }
-  else if ( t.sign )
-    t.sigl = -t.sigl;
+  else
+    {
+      if ( precision_loss )
+	set_precision_flag(precision_loss);
+      if ( t.sign )
+	t.sigl = -t.sigl;
+    }
 
   RE_ENTRANT_CHECK_OFF
   verify_area(VERIFY_WRITE,d,2);
@@ -1063,25 +1021,18 @@
   FPU_REG t;
   unsigned long long ll;
   unsigned char b;
-  int i;
+  int i, precision_loss;
   unsigned char sign = (FPU_st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
 
   if ( FPU_st0_tag == TW_Empty )
     {
       /* Empty register (stack underflow) */
       EXCEPTION(EX_StackUnder);
-      if ( control_word & EX_Invalid )
-	{
-	  /* The masked response */
-	  /* Put out the QNaN indefinite */
-	  goto put_indefinite;
-	}
-      else
-	return 0;
+      goto invalid_operand;
     }
 
   reg_move(FPU_st0_ptr, &t);
-  round_to_int(&t);
+  precision_loss = round_to_int(&t);
   ll = *(unsigned long long *)(&t.sigl);
 
   /* Check for overflow, by comparing with 999999999999999999 decimal. */
@@ -1090,13 +1041,13 @@
     {
       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 )
 	{
-put_indefinite:
-	  /* Produce "indefinite" */
+	  /* Produce the QNaN "indefinite" */
 	  RE_ENTRANT_CHECK_OFF
 	  verify_area(VERIFY_WRITE,d,10);
-	  put_fs_byte(0xff,(unsigned char *) d+7);
+	  put_fs_byte(0xff,(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
@@ -1105,6 +1056,11 @@
       else
 	return 0;
     }
+  else if ( precision_loss )
+    {
+      if ( set_precision_flag(precision_loss) )
+	return 0;
+    }
 
   verify_area(VERIFY_WRITE,d,10);
   for ( i = 0; i < 9; i++)
@@ -1132,7 +1088,6 @@
 /* Overflow is signalled by a non-zero return value (in eax).
    In the case of overflow, the returned significand always has the
    the largest possible value */
-/* The value returned in eax is never actually needed :-) */
 int round_to_int(FPU_REG *r)
 {
   char     very_big;
@@ -1164,7 +1119,7 @@
 	{
 	  if ( very_big ) return 1;        /* overflow */
 	  (*(long long *)(&r->sigl)) ++;
-	  return LOST_UP;
+	  return PRECISION_LOST_UP;
 	}
       break;
     case RC_DOWN:
@@ -1172,7 +1127,7 @@
 	{
 	  if ( very_big ) return 1;        /* overflow */
 	  (*(long long *)(&r->sigl)) ++;
-	  return LOST_UP;
+	  return PRECISION_LOST_UP;
 	}
       break;
     case RC_UP:
@@ -1180,14 +1135,14 @@
 	{
 	  if ( very_big ) return 1;        /* overflow */
 	  (*(long long *)(&r->sigl)) ++;
-	  return LOST_UP;
+	  return PRECISION_LOST_UP;
 	}
       break;
     case RC_CHOP:
       break;
     }
 
-  return eax ? LOST_DOWN : 0;
+  return eax ? PRECISION_LOST_DOWN : 0;
 
 }
 
@@ -1202,7 +1157,7 @@
 
   RE_ENTRANT_CHECK_OFF
   control_word = get_fs_word((unsigned short *) s);
-  status_word = get_fs_word((unsigned short *) (s+4));
+  partial_status = get_fs_word((unsigned short *) (s+4));
   tag_word = get_fs_word((unsigned short *) (s+8));
   ip_offset = get_fs_long((unsigned long *) (s+0x0c));
   cs_selector = get_fs_long((unsigned long *) (s+0x10));
@@ -1210,32 +1165,50 @@
   operand_selector = get_fs_long((unsigned long *) (s+0x18));
   RE_ENTRANT_CHECK_ON
 
-  top = (status_word >> SW_Top_Shift) & 7;
+  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;
 
-      switch ( tag )
+      if ( tag == 3 )
+	/* New tag is empty.  Accept it */
+	regs[i].tag = TW_Empty;
+      else if ( regs[i].tag == TW_Empty )
 	{
-	case 0:
-	  regs[i].tag = TW_Valid;
-	  break;
-	case 1:
-	  regs[i].tag = TW_Zero;
-	  break;
-	case 2:
-	  regs[i].tag = TW_NaN;
-	  break;
-	case 3:
-	  regs[i].tag = TW_Empty;
-	  break;
-	}
+	  /* 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 */
     }
 
-  FPU_data_address = (void *)data_operand_offset;  /* We want no net effect */
-  FPU_entry_eip = ip_offset;               /* We want no net effect */
+  /* Ensure that the values just loaded are not changed by
+     fix-up operations. */
+  NO_NET_DATA_EFFECT;
+  NO_NET_INSTR_EFFECT;
 
   return s + 0x1c;
 }
@@ -1245,35 +1218,24 @@
 {
   int i, stnr;
   unsigned char tag;
-  unsigned short saved_status, saved_control;
-  char *s = (char *)fldenv();
+  char *s = fldenv();
 
-  saved_status = status_word;
-  saved_control = control_word;
-  control_word = 0x037f;      /* Mask all interrupts while we load. */
   for ( i = 0; i < 8; i++ )
     {
-      /* load each register */
+      /* Load each register. */
       FPU_data_address = (void *)(s+i*10);
       reg_load_extended();
       stnr = (i+top) & 7;
-      tag = regs[stnr].tag;    /* derived from the loaded tag word */
+      tag = regs[stnr].tag;   /* Derived from the loaded tag word. */
       reg_move(&FPU_loaded_data, &regs[stnr]);
-      if ( tag == TW_NaN )
-	{
-	  /* The current data is a special, i.e. NaN, unsupported, infinity,
-	     or denormal */
-	  unsigned char t = regs[stnr].tag;  /* derived from the new data */
-	  if ( /* (t == TW_Valid) || ****/ (t == TW_Zero) )
-	    regs[stnr].tag = TW_NaN;
-	}
-      else
+      if ( tag == TW_Empty )  /* The loaded data over-rides all other cases. */
 	regs[stnr].tag = tag;
     }
-  control_word = saved_control;
-  status_word = saved_status;
 
-  FPU_data_address = (void *)data_operand_offset;  /* We want no net effect */
+  /* Reverse the effect which loading the registers had on the
+     data pointer */
+  NO_NET_DATA_EFFECT;
+
 }
 
 
@@ -1287,9 +1249,6 @@
     {
       switch ( tag = regs[i].tag )
 	{
-#if 0 /* TW_Denormal is not used yet, and probably won't be */
-	case TW_Denormal:
-#endif
 	case TW_Valid:
 	  if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) )
 	    tag = 2;
@@ -1301,7 +1260,7 @@
 	case TW_Empty:
 	  tag = 3;
 	  break;
-	  /* TW_Valid and TW_Zero already have the correct value */
+	  /* TW_Zero already has the correct value */
 	}
       word <<= 2;
       word |= tag;
@@ -1316,21 +1275,30 @@
 
   verify_area(VERIFY_WRITE,d,28);
 
-#if 0 /****/
-  *(unsigned short *)&cs_selector = fpu_cs;
-  *(unsigned short *)&operand_selector = fpu_os;
-#endif /****/
-
   RE_ENTRANT_CHECK_OFF
+#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 & ~SW_Top) | ((top&7) << SW_Top_Shift),
-	      (unsigned short *) (d+4));
+  put_fs_word(status_word(), (unsigned short *) (d+4));
   put_fs_word(tag_word(), (unsigned short *) (d+8));
+#endif PECULIAR_486
   put_fs_long(ip_offset, (unsigned long *) (d+0x0c));
-  put_fs_long(cs_selector, (unsigned long *) (d+0x10));
+  put_fs_long(cs_selector & ~0xf8000000, (unsigned long *) (d+0x10));
   put_fs_long(data_operand_offset, (unsigned long *) (d+0x14));
+#ifdef PECULIAR_486
+  /* An 80486 sets all the reserved bits to 1. */
+  put_fs_long(0xffff0000 | operand_selector, (unsigned long *) (d+0x18));
+#else
   put_fs_long(operand_selector, (unsigned long *) (d+0x18));
+#endif PECULIAR_486
   RE_ENTRANT_CHECK_ON
+  
+  control_word |= CW_Exceptions;
+  partial_status &= ~(SW_Summary | SW_Backward);
 
   return d + 0x1c;
 }
@@ -1339,101 +1307,79 @@
 void fsave(void)
 {
   char *d;
-  FPU_REG tmp, *rp;
   int i;
-  short e;
 
   d = fstenv();
   verify_area(VERIFY_WRITE,d,80);
   for ( i = 0; i < 8; i++ )
-    {
-      /* Store each register in the order: st(0), st(1), ... */
-      rp = &regs[(top+i) & 7];
-
-      e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
-
-      if ( rp->tag == TW_Valid )
-	{
-	  if ( e >= 0x7fff )
-	    {
-	      /* Overflow to infinity */
-	      RE_ENTRANT_CHECK_OFF
-	      put_fs_long(0, (unsigned long *) (d+i*10));
-	      put_fs_long(0x80000000, (unsigned long *) (d+i*10+4));
-	      RE_ENTRANT_CHECK_ON
-	      e = 0x7fff;
-	    }
-	  else if ( e <= 0 )
-	    {
-	      if ( e > -63 )
-		{
-		  /* Make a de-normal */
-		  reg_move(rp, &tmp);
-		  tmp.exp += -EXTENDED_Emin + 63;  /* largest exp to be 62 */
-		  round_to_int(&tmp);
-		  RE_ENTRANT_CHECK_OFF
-		  put_fs_long(tmp.sigl, (unsigned long *) (d+i*10));
-		  put_fs_long(tmp.sigh, (unsigned long *) (d+i*10+4));
-		  RE_ENTRANT_CHECK_ON
-		}
-	      else
-		{
-		  /* Underflow to zero */
-		  RE_ENTRANT_CHECK_OFF
-		  put_fs_long(0, (unsigned long *) (d+i*10));
-		  put_fs_long(0, (unsigned long *) (d+i*10+4));
-		  RE_ENTRANT_CHECK_ON
-		}
-	      e = 0;
-	    }
-	  else
-	    {
-	      RE_ENTRANT_CHECK_OFF
-	      put_fs_long(rp->sigl, (unsigned long *) (d+i*10));
-	      put_fs_long(rp->sigh, (unsigned long *) (d+i*10+4));
-	      RE_ENTRANT_CHECK_ON
-	    }
-	}
-      else if ( rp->tag == TW_Zero )
-	{
-	  RE_ENTRANT_CHECK_OFF
-	  put_fs_long(0, (unsigned long *) (d+i*10));
-	  put_fs_long(0, (unsigned long *) (d+i*10+4));
-	  RE_ENTRANT_CHECK_ON
-	  e = 0;
-	}
-      else if ( rp->tag == TW_Infinity )
-	{
-	  RE_ENTRANT_CHECK_OFF
-	  put_fs_long(0, (unsigned long *) (d+i*10));
-	  put_fs_long(0x80000000, (unsigned long *) (d+i*10+4));
-	  RE_ENTRANT_CHECK_ON
-	  e = 0x7fff;
-	}
-      else if ( rp->tag == TW_NaN )
-	{
-	  RE_ENTRANT_CHECK_OFF
-	  put_fs_long(rp->sigl, (unsigned long *) (d+i*10));
-	  put_fs_long(rp->sigh, (unsigned long *) (d+i*10+4));
-	  RE_ENTRANT_CHECK_ON
-	  e = 0x7fff;
-	}
-      else if ( rp->tag == TW_Empty )
-	{
-	  /* just copy the reg */
-	  RE_ENTRANT_CHECK_OFF
-	  put_fs_long(rp->sigl, (unsigned long *) (d+i*10));
-	  put_fs_long(rp->sigh, (unsigned long *) (d+i*10+4));
-	  RE_ENTRANT_CHECK_ON
-	}
-      e |= rp->sign == SIGN_POS ? 0 : 0x8000;
-      RE_ENTRANT_CHECK_OFF
-      put_fs_word(e, (unsigned short *) (d+i*10+8));
-      RE_ENTRANT_CHECK_ON
-    }
+    write_to_extended(&regs[(top + i) & 7], d + 10 * i);
 
   finit();
 
 }
 
 /*===========================================================================*/
+
+/*
+  A call to this function must be preceeded by a call to
+  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 | 0x114);
+      break;
+    case TW_Infinity:
+    case TW_NaN:
+      if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) )
+	EXCEPTION(EX_INTERNAL | 0x114);
+      break;
+    default:
+      if (e > 0x7fff || e < -63)
+	EXCEPTION(EX_INTERNAL | 0x114);
+    }
+#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 --git a/kernel/FPU-emu/reg_mul.c b/kernel/FPU-emu/reg_mul.c
index 67a08c1..a7d48ad 100644
--- a/kernel/FPU-emu/reg_mul.c
+++ b/kernel/FPU-emu/reg_mul.c
@@ -21,16 +21,21 @@
 
 
 /* This routine must be called with non-empty source registers */
-void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w)
+int reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w)
 {
+  char saved_sign = dest->sign;
   char sign = (a->sign ^ b->sign);
 
   if (!(a->tag | b->tag))
     {
-      /* This should be the most common case */
-      reg_u_mul(a, b, dest, control_w);
+      /* Both regs Valid, this should be the most common case. */
       dest->sign = sign;
-      return;
+      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))
     {
@@ -38,7 +43,7 @@
       if ( ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ||
 	  ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER)) )
 	{
-	  if ( denormal_operand() ) return;
+	  if ( denormal_operand() ) return 1;
 	}
 #endif DENORM_OPERAND
       /* Must have either both arguments == zero, or
@@ -50,58 +55,50 @@
 	 80486 appears to behave this way... */
       dest->sign = sign;
 #endif PECULIAR_486
-      return;
+      return 0;
     }
-#if 0  /* TW_Denormal is not used yet... perhaps never will be. */
-  else if ((a->tag <= TW_Denormal) && (b->tag <= TW_Denormal))
-    {
-      /* One or both arguments are de-normalized */
-      /* Internal de-normalized numbers are not supported yet */
-      EXCEPTION(EX_INTERNAL|0x105);
-      reg_move(&CONST_Z, dest);
-    }
-#endif
   else
     {
       /* Must have infinities, NaNs, etc */
       if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
-	{ real_2op_NaN(a, b, dest); return; }
+	{ return real_2op_NaN(a, b, dest); }
       else if (a->tag == TW_Infinity)
 	{
 	  if (b->tag == TW_Zero)
-	    { arith_invalid(dest); return; }  /* Zero*Infinity is invalid */
+	    { return arith_invalid(dest); }  /* Zero*Infinity is invalid */
 	  else
 	    {
 #ifdef DENORM_OPERAND
 	      if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 		  denormal_operand() )
-		return;
+		return 1;
 #endif DENORM_OPERAND
 	      reg_move(a, dest);
 	      dest->sign = sign;
 	    }
-	  return;
+	  return 0;
 	}
       else if (b->tag == TW_Infinity)
 	{
 	  if (a->tag == TW_Zero)
-	    { arith_invalid(dest); return; }  /* Zero*Infinity is invalid */
+	    { return arith_invalid(dest); }  /* Zero*Infinity is invalid */
 	  else
 	    {
 #ifdef DENORM_OPERAND
 	      if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 		  denormal_operand() )
-		return;
+		return 1;
 #endif DENORM_OPERAND
 	      reg_move(b, dest);
 	      dest->sign = sign;
 	    }
-	  return;
+	  return 0;
 	}
 #ifdef PARANOID
       else
 	{
 	  EXCEPTION(EX_INTERNAL|0x102);
+	  return 1;
 	}
 #endif PARANOID
     }
diff --git a/kernel/FPU-emu/reg_round.S b/kernel/FPU-emu/reg_round.S
index 4097606..7c338c2 100644
--- a/kernel/FPU-emu/reg_round.S
+++ b/kernel/FPU-emu/reg_round.S
@@ -16,6 +16,9 @@
  | 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.                                                                     |
+ |                                                                           |
  +---------------------------------------------------------------------------*/
 
 /*---------------------------------------------------------------------------+
@@ -70,8 +73,11 @@
 #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
 
@@ -345,9 +351,9 @@
 	adcl	$0,%eax
 
 LCheck_Round_Overflow:
-	jnc	LRe_normalise		/* Rounding done, no overflow */
+	jnc	LRe_normalise
 
-	/* Overflow, adjust the result (to 1.0) */
+	/* Overflow, adjust the result (significand to 1.0) */
 	rcrl	$1,%eax
 	rcrl	$1,%ebx
 	incl	EXP(%edi)
@@ -372,9 +378,6 @@
 	je	xL_precision_lost_down
 
 xL_no_precision_loss:
-	cmpl	EXP_OVER,EXP(%edi)
-	jge	L_overflow
-
 	/* store the result */
 	movb	TW_Valid,TAG(%edi)
 
@@ -382,6 +385,11 @@
 	movl	%eax,SIGH(%edi)
 	movl	%ebx,SIGL(%edi)
 
+	xorl	%eax,%eax	// No errors detected.
+
+	cmpl	EXP_OVER,EXP(%edi)
+	jge	L_overflow
+
 FPU_Arith_exit:
 	popl	%ebx
 	popl	%edi
@@ -419,9 +427,8 @@
 	movb	DENORMAL,FPU_denormal
 
 	pushl	%ecx		// Save
-	movl	EXP(%edi),%ecx
-	subl	EXP_UNDER+1,%ecx
-	negl	%ecx
+	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
@@ -434,7 +441,7 @@
 // Shift by [1..31] bits
 	addl	%ecx,EXP(%edi)
 	orl	%edx,%edx	// extension
-	setne	%ch
+	setne	%ch		// Save whether %edx is non-zero
 	xorl	%edx,%edx
 	shrd	%cl,%ebx,%edx
 	shrd	%cl,%eax,%ebx
@@ -495,8 +502,6 @@
 
 
 xUnmasked_underflow:
-	// Increase the exponent by the magic number
-	addl	$(3*(1<<13)),EXP(%edi)
 	movb	UNMASKED_UNDERFLOW,FPU_denormal
 	jmp	xDenorm_done
 
@@ -513,7 +518,20 @@
 	jne	L_norm_bugged
 #endif PARANOID
 
-	orl	%eax,%eax	// ms bits
+#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
@@ -558,6 +576,8 @@
 	popl	%eax
 	popl	%eax
 
+// Reduce the exponent to EXP_UNDER
+	movl	EXP_UNDER,EXP(%edi)
 	movb	TW_Zero,TAG(%edi)
 	jmp	xL_Store_significand
 
@@ -571,6 +591,16 @@
 
 
 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
@@ -585,17 +615,19 @@
 	pushl	EX_INTERNAL|0x201
 	call	EXCEPTION
 	popl	%ebx
-	jmp	FPU_Arith_exit
+	jmp	L_exception_exit
 
 L_norm_bugged:
 	pushl	EX_INTERNAL|0x216
 	call	EXCEPTION
 	popl	%ebx
-	jmp	FPU_Arith_exit
+	jmp	L_exception_exit
 
 L_entry_bugged:
 	pushl	EX_INTERNAL|0x217
 	call	EXCEPTION
 	popl	%ebx
+L_exception_exit:
+	mov	$1,%eax
 	jmp	FPU_Arith_exit
 #endif PARANOID
diff --git a/kernel/FPU-emu/reg_u_add.S b/kernel/FPU-emu/reg_u_add.S
index 3aca1bc..fbcffa3 100644
--- a/kernel/FPU-emu/reg_u_add.S
+++ b/kernel/FPU-emu/reg_u_add.S
@@ -82,8 +82,8 @@
 
 L_accum_loaded:
 	movl	PARAM3,%edi		/* destination */
-	movb	SIGN(%esi),%dl
-	movb	%dl,SIGN(%edi)		/* Copy the sign from the first arg */
+//	movb	SIGN(%esi),%dl
+//	movb	%dl,SIGN(%edi)		/* Copy the sign from the first arg */
 
 
 	movl	EXP(%esi),%edx
diff --git a/kernel/FPU-emu/reg_u_sub.S b/kernel/FPU-emu/reg_u_sub.S
index 88f7f5c..191f48a 100644
--- a/kernel/FPU-emu/reg_u_sub.S
+++ b/kernel/FPU-emu/reg_u_sub.S
@@ -61,13 +61,11 @@
 xOp2_not_denorm:
 #endif DENORM_OPERAND
 
-//	xorl	%ecx,%ecx
 	movl	EXP(%esi),%ecx
 	subl	EXP(%edi),%ecx	/* exp1 - exp2 */
 
 #ifdef PARANOID
 	/* source 2 is always smaller than source 1 */
-//	jc	L_bugged
 	js	L_bugged_1
 
 	testl	$0x80000000,SIGH(%edi)	/* The args are assumed to be be normalized */
@@ -87,8 +85,8 @@
 	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 */
+//	movb	SIGN(%esi),%dl
+//	movb	%dl,SIGN(%edi)	/* Copy the sign from the first arg */
 
 	xorl	%edx,%edx	// register extension
 
@@ -213,8 +211,8 @@
 
 	/* Shift left 64 bits */
 	subl	$64,EXP(%edi)
-	movl	%edx,%eax
-	jmp	L_store
+	xchg	%edx,%eax
+	jmp	FPU_round
 
 L_must_be_zero:
 #ifdef PARANOID
@@ -227,7 +225,7 @@
 	movl	$0,EXP(%edi)		/* exponent */
 	movl	$0,SIGL(%edi)
 	movl	$0,SIGH(%edi)
-	jmp	L_exit		// Does not underflow
+	jmp	L_exit		// %eax contains zero
 
 L_shift_32:
 	movl	%ebx,%eax
@@ -254,57 +252,39 @@
 	pushl	EX_INTERNAL|0x206
 	call	EXCEPTION
 	pop	%ebx
-	jmp	L_exit
+	jmp	L_error_exit
 
 L_bugged_2:
 	pushl	EX_INTERNAL|0x209
 	call	EXCEPTION
 	pop	%ebx
-	jmp	L_exit
+	jmp	L_error_exit
 
 L_bugged_3:
 	pushl	EX_INTERNAL|0x210
 	call	EXCEPTION
 	pop	%ebx
-	jmp	L_exit
+	jmp	L_error_exit
 
 L_bugged_4:
 	pushl	EX_INTERNAL|0x211
 	call	EXCEPTION
 	pop	%ebx
-	jmp	L_exit
+	jmp	L_error_exit
 
 L_bugged:
 	pushl	EX_INTERNAL|0x212
 	call	EXCEPTION
 	pop	%ebx
-	jmp	L_exit
+	jmp	L_error_exit
 #endif PARANOID
 
 
-L_store:
-/*------------------------------+
- |	Store the result	|
- +------------------------------*/
-	movl	%eax,SIGH(%edi)
-	movl	%ebx,SIGL(%edi)
-
-	movb	TW_Valid,TAG(%edi)		/* Set the tags to TW_Valid */
-
-	cmpl	EXP_UNDER,EXP(%edi)
-	jle	L_underflow
-
+L_error_exit:
+	movl	$1,%eax
 L_exit:
 	popl	%ebx
 	popl	%edi
 	popl	%esi
 	leave
 	ret
-
-
-L_underflow:
-	push	%edi
-	call	_arith_underflow
-	pop	%ebx
-	jmp	L_exit
-
diff --git a/kernel/FPU-emu/status_w.h b/kernel/FPU-emu/status_w.h
index 00e291f..0e23d6c 100644
--- a/kernel/FPU-emu/status_w.h
+++ b/kernel/FPU-emu/status_w.h
@@ -45,9 +45,14 @@
 #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) ({ \
-  status_word &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
-  status_word |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
+  partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
+  partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
+
+/* Clear the SW_C1 bit, "other bits undefined" */
+#define clear_C1()  { partial_status &= ~SW_C1; }
 
 #endif __ASSEMBLER__
 
diff --git a/kernel/FPU-emu/version.h b/kernel/FPU-emu/version.h
index b0b1fe6..fd562ce 100644
--- a/kernel/FPU-emu/version.h
+++ b/kernel/FPU-emu/version.h
@@ -9,5 +9,5 @@
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
-#define FPU_VERSION "wm-FPU-emu version BETA 1.4"
+#define FPU_VERSION "wm-FPU-emu version BETA 1.5"
 
diff --git a/kernel/Makefile b/kernel/Makefile
index 1009703..7c9637a 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -21,7 +21,7 @@
 OBJS  = sched.o sys_call.o traps.o irq.o dma.o fork.o \
 	panic.o printk.o vsprintf.o sys.o exit.o \
 	signal.o mktime.o ptrace.o ioport.o itimer.o \
-	info.o
+	info.o ldt.o
 
 all: kernel.o kernelsubdirs
 
diff --git a/kernel/blk_drv/Makefile b/kernel/blk_drv/Makefile
index 621e6f7..3cf68b3 100644
--- a/kernel/blk_drv/Makefile
+++ b/kernel/blk_drv/Makefile
@@ -18,7 +18,7 @@
 
 SUBDIRS	= scsi
 
-OBJS = xd.o hd.o ll_rw_blk.o floppy.o ramdisk.o genhd.o
+OBJS = xd.o hd.o ll_rw_blk.o floppy.o ramdisk.o genhd.o cdu31a.o mcd.o
 
 all: blk_drv.a scsisubdirs
 
diff --git a/kernel/blk_drv/blk.h b/kernel/blk_drv/blk.h
index 9167318..2e4f158 100644
--- a/kernel/blk_drv/blk.h
+++ b/kernel/blk_drv/blk.h
@@ -79,6 +79,8 @@
 extern int * blksize_size[MAX_BLKDEV];
 
 extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
+extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
+extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
 extern int is_read_only(int dev);
 extern void set_device_ro(int dev,int flag);
 
@@ -168,6 +170,23 @@
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
+#elif (MAJOR_NR == 15)
+/* CDU31A CD-ROM */
+#define DEVICE_NAME "CDU31A"
+#define DEVICE_REQUEST do_cdu31a_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == 23)
+/* MITSUMI CD-ROM */
+#define DEVICE_NAME "Mitsumi CD-ROM"
+/* #define DEVICE_INTR do_mcd */
+#define DEVICE_REQUEST do_mcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
 #else
 /* unknown blk device */
 #error "unknown blk device"
diff --git a/kernel/blk_drv/cdu31a.c b/kernel/blk_drv/cdu31a.c
new file mode 100644
index 0000000..0267039
--- /dev/null
+++ b/kernel/blk_drv/cdu31a.c
@@ -0,0 +1,1705 @@
+/*
+ * Sony CDU-31A CDROM interface device driver.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com)
+ *
+ * Colossians 3:17
+ *
+ * The Sony interface device driver handles Sony interface CDROM
+ * drives and provides a complete block-level interface as well as an
+ * ioctl() interface compatible with the Sun (as specified in
+ * include/linux/cdrom.h).  With this interface, CDROMs can be
+ * accessed and standard audio CDs can be played back normally.
+ *
+ * This interface is (unfortunatly) a polled interface.  This is
+ * because most Sony interfaces are set up with DMA and interrupts
+ * disables.  Some (like mine) do not even have the capability to
+ * handle interrupts or DMA.  For this reason you will see a lot of
+ * the following:
+ *
+ *   retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
+ *   while ((retry_count > jiffies) && (! <some condition to wait for))
+ *   {
+ *      while (handle_sony_cd_attention())
+ *         ;
+ *
+ *      sony_sleep();
+ *   }
+ *   if (the condition not met)
+ *   {
+ *      return an error;
+ *   }
+ *
+ * This ugly hack waits for something to happen, sleeping a little
+ * between every try.  it also handles attentions, which are
+ * asyncronous events from the drive informing the driver that a disk
+ * has been inserted, removed, etc.
+ *
+ * One thing about these drives: They talk in MSF (Minute Second Frame) format.
+ * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
+ * disk.  The funny thing is that these are sent to the drive in BCD, but the
+ * interface wants to see them in decimal.  A lot of conversion goes on.
+ *
+ *  Copyright (C) 1993  Corey Minyard
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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.
+ *
+ */
+
+
+#include <linux/config.h>
+#ifdef CONFIG_CDU31A
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#include <linux/cdrom.h>
+#include <linux/cdu31a.h>
+
+#define MAJOR_NR 15
+#include "blk.h"
+
+
+static unsigned short cdu31a_addresses[] =
+{
+   0x340,	/* Standard configuration Sony Interface */
+   0x1f88,	/* Fusion CD-16 */
+   0x360,	/* Secondary standard Sony Interface */
+   0x320,	/* Secondary standard Sony Interface */
+   0x330,	/* Secondary standard Sony Interface */
+   0
+};
+
+
+static int handle_sony_cd_attention(void);
+static int read_subcode(void);
+static void sony_get_toc(void);
+static int scd_open(struct inode *inode, struct file *filp);
+
+
+/* The base I/O address of the Sony Interface.  This is a variable (not a
+   #define) so it can be easily changed via some future ioctl() */
+static unsigned short sony_cd_base_io = 0;
+
+/*
+ * The following are I/O addresses of the various registers for the drive.  The
+ * comment for the base address also applies here.
+ */
+static volatile unsigned short sony_cd_cmd_reg;
+static volatile unsigned short sony_cd_param_reg;
+static volatile unsigned short sony_cd_write_reg;
+static volatile unsigned short sony_cd_control_reg;
+static volatile unsigned short sony_cd_status_reg;
+static volatile unsigned short sony_cd_result_reg;
+static volatile unsigned short sony_cd_read_reg;
+static volatile unsigned short sony_cd_fifost_reg;
+
+
+static int initialized = 0;                /* Has the drive been initialized? */
+static int sony_disc_changed = 1;          /* Has the disk been changed
+                                              since the last check? */
+static int sony_toc_read = 0;              /* Has the table of contents been
+                                              read? */
+static int sony_spun_up = 0;               /* Has the drive been spun up? */
+static unsigned int sony_buffer_size;      /* Size in bytes of the read-ahead
+                                              buffer. */
+static unsigned int sony_buffer_sectors;   /* Size (in 2048 byte records) of
+                                              the read-ahead buffer. */
+static unsigned int sony_usage = 0;        /* How many processes have the
+                                              drive open. */
+
+static volatile int sony_first_block = -1; /* First OS block (512 byte) in
+                                              the read-ahead buffer */
+static volatile int sony_last_block = -1;  /* Last OS block (512 byte) in
+                                              the read-ahead buffer */
+
+static struct s_sony_toc *sony_toc;              /* Points to the table of
+                                                    contents. */
+static struct s_sony_subcode * volatile last_sony_subcode; /* Points to the last
+                                                    subcode address read */
+static unsigned char * volatile sony_buffer;     /* Points to the read-ahead
+                                                    buffer */
+
+static volatile int sony_inuse = 0;  /* Is the drive in use?  Only one operation at a time
+                                        allowed */
+
+static struct wait_queue * sony_wait = NULL;
+
+static struct task_struct *has_cd_task = NULL;  /* The task that is currently using the
+                                                   CDROM drive, or NULL if none. */
+
+/*
+ * The audio status uses the values from read subchannel data as specified
+ * in include/linux/cdrom.h.
+ */
+static volatile int sony_audio_status = CDROM_AUDIO_NO_STATUS;
+
+/*
+ * The following are a hack for pausing and resuming audio play.  The drive
+ * does not work as I would expect it, if you stop it then start it again,
+ * the drive seeks back to the beginning and starts over.  This holds the
+ * position during a pause so a resume can restart it.  It uses the
+ * audio status variable above to tell if it is paused.
+ */
+unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 };
+unsigned volatile char final_pos_msf[3] = { 0, 0, 0 };
+
+/*
+ * This routine returns 1 if the disk has been changed since the last
+ * check or 0 if it hasn't.  Setting flag to 0 resets the changed flag.
+ */
+int
+check_cdu31a_media_change(int full_dev, int flag)
+{
+   int retval, target;
+
+
+   target = MINOR(full_dev);
+
+   if (target > 0) {
+      printk("Sony CD-ROM request error: invalid device.\n");
+      return 0;
+   }
+
+   retval = sony_disc_changed;
+   if (!flag)
+   {
+      sony_disc_changed = 0;
+   }
+
+   return retval;
+}
+
+
+/*
+ * Wait a little while (used for polling the drive).  If in initialization,
+ * setting a timeout doesn't work, so just loop for a while.
+ */
+static inline void
+sony_sleep(void)
+{
+   current->state = TASK_INTERRUPTIBLE;
+   current->timeout = jiffies;
+   schedule();
+}
+
+
+/*
+ * The following are convenience routine to read various status and set
+ * various conditions in the drive.
+ */
+static inline int
+is_attention(void)
+{
+   return((inb(sony_cd_status_reg) & SONY_ATTN_BIT) != 0);
+}
+
+static inline int
+is_busy(void)
+{
+   return((inb(sony_cd_status_reg) & SONY_BUSY_BIT) != 0);
+}
+
+static inline int
+is_data_ready(void)
+{
+   return((inb(sony_cd_status_reg) & SONY_DATA_RDY_BIT) != 0);
+}
+
+static inline int
+is_data_requested(void)
+{
+   return((inb(sony_cd_status_reg) & SONY_DATA_REQUEST_BIT) != 0);
+}
+
+static inline int
+is_result_ready(void)
+{
+   return((inb(sony_cd_status_reg) & SONY_RES_RDY_BIT) != 0);
+}
+
+static inline int
+is_param_write_rdy(void)
+{
+   return((inb(sony_cd_fifost_reg) & SONY_PARAM_WRITE_RDY_BIT) != 0);
+}
+
+static inline void
+reset_drive(void)
+{
+   outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_attention(void)
+{
+   outb(SONY_ATTN_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_result_ready(void)
+{
+   outb(SONY_RES_RDY_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_data_ready(void)
+{
+   outb(SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_param_reg(void)
+{
+   outb(SONY_PARAM_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline unsigned char
+read_status_register(void)
+{
+   return(inb(sony_cd_status_reg));
+}
+
+static inline unsigned char
+read_result_register(void)
+{
+   return(inb(sony_cd_result_reg));
+}
+
+static inline unsigned char
+read_data_register(void)
+{
+   return(inb(sony_cd_read_reg));
+}
+
+static inline void
+write_param(unsigned char param)
+{
+   outb(param, sony_cd_param_reg);
+}
+
+static inline void
+write_cmd(unsigned char cmd)
+{
+   outb(cmd, sony_cd_cmd_reg);
+   outb(SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg);
+}
+
+
+/*
+ * This routine writes data to the parameter register.  Since this should
+ * happen fairly fast, it is polled with no OS waits between.
+ */
+static int
+write_params(unsigned char *params,
+             int num_params)
+{
+   unsigned int retry_count;
+
+
+   retry_count = SONY_READY_RETRIES;
+   while ((retry_count > 0) && (!is_param_write_rdy()))
+   {
+      retry_count--;
+   }
+   if (!is_param_write_rdy())
+   {
+      return -EIO;
+   }
+
+   while (num_params > 0)
+   {
+      write_param(*params);
+      params++;
+      num_params--;
+   }
+
+   return 0;
+}
+
+
+/*
+ * The following reads data from the command result register.  It is a
+ * fairly complex routine, all status info flows back through this
+ * interface.  The algorithm is stolen directly from the flowcharts in
+ * the drive manual.
+ */
+static void
+get_result(unsigned char *result_buffer,
+           unsigned int *result_size)
+{
+   unsigned char a, b;
+   int i;
+   unsigned int retry_count;
+
+
+   while (handle_sony_cd_attention())
+      ;
+   /* Wait for the result data to be ready */
+   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+   while ((retry_count > jiffies) && (is_busy() || (!(is_result_ready()))))
+   {
+      sony_sleep();
+
+      while (handle_sony_cd_attention())
+         ;
+   }
+   if (is_busy() || (!(is_result_ready())))
+   {
+      result_buffer[0] = 0x20;
+      result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+      *result_size = 2;
+      return;
+   }
+
+   /*
+    * Get the first two bytes.  This determines what else needs
+    * to be done.
+    */
+   clear_result_ready();
+   a = read_result_register();
+   *result_buffer = a;
+   result_buffer++;
+   b = read_result_register();
+   *result_buffer = b;
+   result_buffer++;
+   *result_size = 2;
+
+   /*
+    * 0x20 means an error occured.  Byte 2 will have the error code.
+    * Otherwise, the command succeded, byte 2 will have the count of
+    * how many more status bytes are coming.
+    *
+    * The result register can be read 10 bytes at a time, a wait for
+    * result ready to be asserted must be done between every 10 bytes.
+    */
+   if ((a & 0xf0) != 0x20)
+   {
+      if (b > 8)
+      {
+         for (i=0; i<8; i++)
+         {
+            *result_buffer = read_result_register();
+            result_buffer++;
+            (*result_size)++;
+         }
+         b = b - 8;
+
+         while (b > 10)
+         {
+            retry_count = SONY_READY_RETRIES;
+            while ((retry_count > 0) && (!is_result_ready()))
+            {
+               retry_count--;
+            }
+            if (!is_result_ready())
+            {
+               result_buffer[0] = 0x20;
+               result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+               *result_size = 2;
+               return;
+            }
+
+            clear_result_ready();
+                                
+            for (i=0; i<10; i++)
+            {
+               *result_buffer = read_result_register();
+               result_buffer++;
+               (*result_size)++;
+            }
+            b = b - 10;
+         }
+
+         if (b > 0)
+         {
+            retry_count = SONY_READY_RETRIES;
+            while ((retry_count > 0) && (!is_result_ready()))
+            {
+               retry_count--;
+            }
+            if (!is_result_ready())
+            {
+               result_buffer[0] = 0x20;
+               result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+               *result_size = 2;
+               return;
+            }
+         }
+      }
+
+      while (b > 0)
+      {
+         *result_buffer = read_result_register();
+         result_buffer++;
+         (*result_size)++;
+         b--;
+      }
+   }
+}
+
+
+/*
+ * This routine issues a read data command and gets the data.  I don't
+ * really like the way this is done (I would prefer for do_sony_cmd() to
+ * handle it automatically) but I found that the drive returns status
+ * when it finishes reading (not when the host has read all the data)
+ * or after it gets an error.  This means that the status can be
+ * received at any time and should be handled immediately (at least
+ * between every 2048 byte block) to check for errors, we can't wait
+ * until all the data is read.
+ */
+static void
+get_data(unsigned char *data,
+         unsigned char *params,         /* 6 bytes with the MSF start address
+                                           and number of sectors to read. */
+         unsigned int data_size,
+         unsigned char *result_buffer,
+         unsigned int *result_size)
+{
+   int i;
+   unsigned int cur_offset;
+   unsigned int retry_count;
+   int result_read;
+   int num_retries;
+
+
+   cli();
+   if (current != has_cd_task) /* Allow recursive calls to this routine */
+   {
+      while (sony_inuse)
+      {
+         interruptible_sleep_on(&sony_wait);
+         if (current->signal & ~current->blocked)
+         {
+            result_buffer[0] = 0x20;
+            result_buffer[1] = SONY_SIGNAL_OP_ERR;
+            *result_size = 2;
+            return;
+         }
+      }
+   }
+   sony_inuse = 1;
+   has_cd_task = current;
+   sti();
+
+   num_retries = 0;
+retry_data_operation:
+
+   /*
+    * Clear any outstanding attentions and wait for the drive to
+    * complete any pending operations.
+    */
+   while (handle_sony_cd_attention())
+      ;
+
+   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+   while ((retry_count > jiffies) && (is_busy()))
+   {
+      sony_sleep();
+      
+      while (handle_sony_cd_attention())
+         ;
+   }
+   if (is_busy())
+   {
+      result_buffer[0] = 0x20;
+      result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+      *result_size = 2;
+      goto get_data_end;
+   }
+
+   /* Issue the command */
+   clear_result_ready();
+   clear_param_reg();
+
+   write_params(params, 6);
+   write_cmd(SONY_READ_CMD);
+
+   /*
+    * Read the data from the drive one 2048 byte sector at a time.  Handle
+    * any results received between sectors, if an error result is returned
+    * terminate the operation immediately.
+    */
+   cur_offset = 0;
+   result_read = 0;
+   while (data_size > 0)
+   {
+      /* Wait for the drive to tell us we have something */
+      retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+      while ((retry_count > jiffies) && (!(is_result_ready() || is_data_ready())))
+      {
+         while (handle_sony_cd_attention())
+            ;
+         
+         sony_sleep();
+      }
+      if (!(is_result_ready() || is_data_ready()))
+      {
+         result_buffer[0] = 0x20;
+         result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+         *result_size = 2;
+         goto get_data_end;
+      }
+      
+      /* Handle results first */
+      if (is_result_ready())
+      {
+         result_read = 1;
+         get_result(result_buffer, result_size);
+         if ((*result_size < 2) || (result_buffer[0] != 0))
+         {
+            goto get_data_end;
+         }
+      }
+      else /* Handle data next */
+      {
+         /*
+          * The drive has to be polled for status on a byte-by-byte basis
+          * to know if the data is ready.  Yuck.  I really wish I could use DMA.
+          */
+         clear_data_ready();
+         for (i=0; i<2048; i++)
+         {
+            retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+            while ((retry_count > jiffies) && (!is_data_requested()))
+            {
+               while (handle_sony_cd_attention())
+                  ;
+               
+               sony_sleep();
+            }
+            if (!is_data_requested())
+            {
+               result_buffer[0] = 0x20;
+               result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+               *result_size = 2;
+               goto get_data_end;
+            }
+            
+            *data = read_data_register();
+            data++;
+            data_size--;
+         }
+         cur_offset = cur_offset + 2048;
+      }
+   }
+
+   /* Make sure the result has been read */
+   if (!result_read)
+   {
+      get_result(result_buffer, result_size);
+   }
+
+get_data_end:
+   if (   ((result_buffer[0] & 0x20) == 0x20)
+       && (num_retries < MAX_CDU31A_RETRIES))
+   {
+      num_retries++;
+      goto retry_data_operation;
+   }
+
+   has_cd_task = NULL;
+   sony_inuse = 0;
+   wake_up_interruptible(&sony_wait);
+}
+
+
+/*
+ * Do a command that does not involve data transfer.
+ */
+static void
+do_sony_cd_cmd(unsigned char cmd,
+               unsigned char *params,
+               unsigned int num_params,
+               unsigned char *result_buffer,
+               unsigned int *result_size)
+{
+   unsigned int retry_count;
+   int num_retries;
+
+
+   cli();
+   if (current != has_cd_task) /* Allow recursive calls to this routine */
+   {
+      while (sony_inuse)
+      {
+         interruptible_sleep_on(&sony_wait);
+         if (current->signal & ~current->blocked)
+         {
+            result_buffer[0] = 0x20;
+            result_buffer[1] = SONY_SIGNAL_OP_ERR;
+            *result_size = 2;
+            return;
+         }
+      }
+   }
+   sony_inuse = 1;
+   has_cd_task = current;
+   sti();
+
+   num_retries = 0;
+retry_cd_operation:
+
+   while (handle_sony_cd_attention())
+      ;
+   
+   retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+   while ((retry_count > jiffies) && (is_busy()))
+   {
+      sony_sleep();
+      
+      while (handle_sony_cd_attention())
+         ;
+   }
+   if (is_busy())
+   {
+      result_buffer[0] = 0x20;
+      result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+      *result_size = 2;
+      goto do_cmd_end;
+   }
+
+   clear_result_ready();
+   clear_param_reg();
+
+   write_params(params, num_params);
+   write_cmd(cmd);
+
+   get_result(result_buffer, result_size);
+
+do_cmd_end:
+   if (   ((result_buffer[0] & 0x20) == 0x20)
+       && (num_retries < MAX_CDU31A_RETRIES))
+   {
+      num_retries++;
+      goto retry_cd_operation;
+   }
+
+   has_cd_task = NULL;
+   sony_inuse = 0;
+   wake_up_interruptible(&sony_wait);
+}
+
+
+/*
+ * Handle an attention from the drive.  This will return 1 if it found one
+ * or 0 if not (if one is found, the caller might want to call again).
+ */
+static int
+handle_sony_cd_attention(void)
+{
+   unsigned char atten_code;
+   unsigned char res_reg[2];
+   unsigned int res_size;
+
+
+   if (is_attention())
+   {
+      clear_attention();
+      atten_code = read_result_register();
+
+      switch (atten_code)
+      {
+       /* Someone changed the CD.  Mark it as changed */
+      case SONY_MECH_LOADED_ATTN:
+         sony_disc_changed = 1;
+         sony_toc_read = 0;
+         sony_audio_status = CDROM_AUDIO_NO_STATUS;
+         sony_first_block = -1;
+         sony_last_block = -1;
+         if (initialized)
+         {
+            do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+            sony_get_toc();
+         }
+         break;
+
+      case SONY_AUDIO_PLAY_DONE_ATTN:
+         sony_audio_status = CDROM_AUDIO_COMPLETED;
+         read_subcode();
+         break;
+
+      case SONY_EJECT_PUSHED_ATTN:
+         sony_audio_status = CDROM_AUDIO_INVALID;
+         break;
+
+      case SONY_LEAD_IN_ERR_ATTN:
+      case SONY_LEAD_OUT_ERR_ATTN:
+      case SONY_DATA_TRACK_ERR_ATTN:
+      case SONY_AUDIO_PLAYBACK_ERR_ATTN:
+         sony_audio_status = CDROM_AUDIO_ERROR;
+         break;
+      }
+      return(1);
+   }
+
+   return(0);
+}
+
+
+/* Convert from an integer 0-99 to BCD */
+static inline unsigned int
+int_to_bcd(unsigned int val)
+{
+   int retval;
+
+
+   retval = (val / 10) << 4;
+   retval = retval | val % 10;
+   return(retval);
+}
+
+
+/* Convert from BCD to an integer from 0-99 */
+static unsigned int
+bcd_to_int(unsigned int bcd)
+{
+   return((((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f));
+}
+
+
+/*
+ * Convert a logical sector value (like the OS would want to use for
+ * a block device) to an MSF format.
+ */
+static void
+log_to_msf(unsigned int log, unsigned char *msf)
+{
+   log = log + LOG_START_OFFSET;
+   msf[0] = int_to_bcd(log / 4500);
+   log = log % 4500;
+   msf[1] = int_to_bcd(log / 75);
+   msf[2] = int_to_bcd(log % 75);
+}
+
+
+/*
+ * Convert an MSF format to a logical sector.
+ */
+static unsigned int
+msf_to_log(unsigned char *msf)
+{
+   unsigned int log;
+
+
+   log = bcd_to_int(msf[2]);
+   log += bcd_to_int(msf[1]) * 75;
+   log += bcd_to_int(msf[0]) * 4500;
+   log = log - LOG_START_OFFSET;
+
+   return log;
+}
+
+
+/*
+ * Take in integer size value and put it into a buffer like
+ * the drive would want to see a number-of-sector value.
+ */
+static void
+size_to_buf(unsigned int size,
+            unsigned char *buf)
+{
+   buf[0] = size / 65536;
+   size = size % 65536;
+   buf[1] = size / 256;
+   buf[2] = size % 256;
+}
+
+
+/*
+ * The OS calls this to perform a read or write operation to the drive.
+ * Write obviously fail.  Reads to a read ahead of sony_buffer_size
+ * bytes to help speed operations.  This especially helps since the OS
+ * uses 1024 byte blocks and the drive uses 2048 byte blocks.  Since most
+ * data access on a CD is done sequentially, this saves a lot of operations.
+ */
+static void
+do_cdu31a_request(void)
+{
+   int block;
+   unsigned int dev;
+   int nsect;
+   unsigned char params[10];
+   unsigned char res_reg[2];
+   unsigned int res_size;
+   int copyoff;
+   int spin_up_retry;
+   unsigned int read_size;
+
+
+   if (!sony_spun_up)
+   {
+      scd_open (NULL,NULL);
+   }
+
+   while (1)
+   {
+      /*
+       * The beginning here is stolen from the hard disk driver.  I hope
+       * its right.
+       */
+      if (!(CURRENT) || CURRENT->dev < 0)
+      {
+         return;
+      }
+
+      INIT_REQUEST;
+      dev = MINOR(CURRENT->dev);
+      block = CURRENT->sector;
+      nsect = CURRENT->nr_sectors;
+      if (dev != 0)
+      {
+         end_request(0);
+         continue;
+      }
+
+      switch(CURRENT->cmd)
+      {
+      case READ:
+         /*
+          * If the block address is invalid or the request goes beyond the end of
+          * the media, return an error.
+          */
+         if ((block / 4) >= sony_toc->lead_out_start_lba)
+         {
+            end_request(0);
+            return;
+         }
+         if (((block + nsect) / 4) >= sony_toc->lead_out_start_lba)
+         {
+            end_request(0);
+            return;
+         }
+
+         while (nsect > 0)
+         {
+            /*
+             * If the requested sector is not currently in the read-ahead buffer,
+             * it must be read in.
+             */
+            if ((block < sony_first_block) || (block > sony_last_block))
+            {
+               sony_first_block = (block / 4) * 4;
+               log_to_msf(block/4, params);
+
+               /*
+                * If the full read-ahead would go beyond the end of the media, trim
+                * it back to read just till the end of the media.
+                */
+               if (((block / 4) + sony_buffer_sectors) >= sony_toc->lead_out_start_lba)
+               {
+                  sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1;
+                  read_size = sony_toc->lead_out_start_lba - (block / 4);
+               }
+               else
+               {
+                  sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1;
+                  read_size = sony_buffer_sectors;
+               }
+               size_to_buf(read_size, &params[3]);
+
+               /*
+                * Read the data.  If the drive was not spinning, spin it up and try
+                * once more.  I know, the goto is ugly, but I am too lazy to fix it.
+                */
+               spin_up_retry = 0;
+try_read_again:
+               get_data(sony_buffer, params, (read_size * 2048), res_reg, &res_size);
+               if ((res_size < 2) || (res_reg[0] != 0))
+               {
+                  if ((res_reg[1] == SONY_NOT_SPIN_ERR) && (!spin_up_retry))
+                  {
+                     do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+                     spin_up_retry = 1;
+                     goto try_read_again;
+                  }
+
+                  printk("Sony CDROM Read error: 0x%2.2x\n", res_reg[1]);
+                  sony_first_block = -1;
+                  sony_last_block = -1;
+                  end_request(0);
+                  return;
+               }
+            }
+   
+            /*
+             * The data is in memory now, copy it to the buffer and advance to the
+             * next block to read.
+             */
+            copyoff = (block - sony_first_block) * 512;
+            memcpy(CURRENT->buffer, sony_buffer+copyoff, 512);
+               
+            block += 1;
+            nsect -= 1;
+            CURRENT->buffer += 512;
+         }
+               
+         end_request(1);
+         break;
+            
+      case WRITE:
+         end_request(0);
+         break;
+            
+      default:
+         panic("Unkown SONY CD cmd");
+      }
+   }
+}
+
+
+/*
+ * Read the table of contents from the drive and set sony_toc_read if
+ * successful.
+ */
+static void
+sony_get_toc(void)
+{
+   unsigned int res_size;
+
+
+   if (!sony_toc_read)
+   {
+      do_sony_cd_cmd(SONY_REQ_TOC_DATA_CMD,
+                     NULL,
+                     0, 
+                     (unsigned char *) sony_toc, 
+                     &res_size);
+      if ((res_size < 2) || ((sony_toc->exec_status[0] & 0x20) == 0x20))
+      {
+         return;
+      }
+      sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
+      sony_toc_read = 1;
+   }
+}
+
+
+/*
+ * Search for a specific track in the table of contents.
+ */
+static int
+find_track(int track)
+{
+   int i;
+   int num_tracks;
+
+
+   num_tracks = sony_toc->last_track_num + sony_toc->first_track_num + 1;
+   for (i = 0; i < num_tracks; i++)
+   {
+      if (sony_toc->tracks[i].track == track)
+      {
+         return i;
+      }
+   }
+
+   return -1;
+}
+
+
+/*
+ * Read the subcode and put it int last_sony_subcode for future use.
+ */
+static int
+read_subcode(void)
+{
+   unsigned int res_size;
+
+
+   do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD,
+                  NULL,
+                  0, 
+                  (unsigned char *) last_sony_subcode, 
+                  &res_size);
+   if ((res_size < 2) || ((last_sony_subcode->exec_status[0] & 0x20) == 0x20))
+   {
+      printk("Sony CDROM error 0x%2.2x (read_subcode)\n",
+             last_sony_subcode->exec_status[1]);
+      return -EIO;
+   }
+
+   return 0;
+}
+
+
+/*
+ * Get the subchannel info like the CDROMSUBCHNL command wants to see it.  If
+ * the drive is playing, the subchannel needs to be read (since it would be
+ * changing).  If the drive is paused or completed, the subcode information has
+ * already been stored, just use that.  The ioctl call wants things in decimal
+ * (not BCD), so all the conversions are done.
+ */
+static int
+sony_get_subchnl_info(long arg)
+{
+   struct cdrom_subchnl schi;
+
+
+   /* Get attention stuff */
+   while (handle_sony_cd_attention())
+      ;
+
+   sony_get_toc();
+   if (!sony_toc_read)
+   {
+      return -EIO;
+   }
+
+   verify_area(VERIFY_READ, (char *) arg, sizeof(schi));
+   verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
+
+   memcpy_fromfs(&schi, (char *) arg, sizeof(schi));
+   
+   switch (sony_audio_status)
+   {
+   case CDROM_AUDIO_PLAY:
+      if (read_subcode() < 0)
+      {
+         return -EIO;
+      }
+      break;
+
+   case CDROM_AUDIO_PAUSED:
+   case CDROM_AUDIO_COMPLETED:
+      break;
+
+   case CDROM_AUDIO_NO_STATUS:
+      schi.cdsc_audiostatus = sony_audio_status;
+      memcpy_tofs((char *) arg, &schi, sizeof(schi));
+      return 0;
+      break;
+
+   case CDROM_AUDIO_INVALID:
+   case CDROM_AUDIO_ERROR:
+   default:
+      return -EIO;
+   }
+
+   schi.cdsc_audiostatus = sony_audio_status;
+   schi.cdsc_adr = last_sony_subcode->address;
+   schi.cdsc_ctrl = last_sony_subcode->control;
+   schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num);
+   schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num);
+   if (schi.cdsc_format == CDROM_MSF)
+   {
+      schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]);
+      schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]);
+      schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]);
+
+      schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]);
+      schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]);
+      schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]);
+   }
+   else if (schi.cdsc_format == CDROM_LBA)
+   {
+      schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf);
+      schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf);
+   }
+   
+   memcpy_tofs((char *) arg, &schi, sizeof(schi));
+   return 0;
+}
+
+
+/*
+ * The big ugly ioctl handler.
+ */
+static int
+scd_ioctl(struct inode *inode,
+          struct file  *file,
+          unsigned int cmd,
+          unsigned int arg)
+{
+   unsigned int dev;
+   unsigned char res_reg[2];
+   unsigned int res_size;
+   unsigned char params[7];
+   int i;
+
+
+   if (!inode)
+   {
+      return -EINVAL;
+   }
+   dev = MINOR(inode->i_rdev) >> 6;
+   if (dev != 0)
+   {
+      return -EINVAL;
+   }
+
+   switch (cmd)
+   {
+   case CDROMSTART:     /* Spin up the drive */
+      do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+      if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+      {
+         printk("Sony CDROM error 0x%2.2x (CDROMSTART)\n", res_reg[1]);
+         return -EIO;
+      }
+      return 0;
+      break;
+      
+   case CDROMSTOP:      /* Spin down the drive */
+      do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
+
+      /*
+       * Spin the drive down, ignoring the error if the disk was
+       * already not spinning.
+       */
+      sony_audio_status = CDROM_AUDIO_NO_STATUS;
+      do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+      if (   ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+          && (res_reg[1] != SONY_NOT_SPIN_ERR))
+      {
+         printk("Sony CDROM error 0x%2.2x (CDROMSTOP)\n", res_reg[1]);
+         return -EIO;
+      }
+      
+      return 0;
+      break;
+
+   case CDROMPAUSE:     /* Pause the drive */
+      do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
+      if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+      {
+         printk("Sony CDROM error 0x%2.2x (CDROMPAUSE)\n", res_reg[1]);
+         return -EIO;
+      }
+
+      /* Get the current position and save it for resuming */
+      if (read_subcode() < 0)
+      {
+         return -EIO;
+      }
+      cur_pos_msf[0] = last_sony_subcode->abs_msf[0];
+      cur_pos_msf[1] = last_sony_subcode->abs_msf[1];
+      cur_pos_msf[2] = last_sony_subcode->abs_msf[2];
+      sony_audio_status = CDROM_AUDIO_PAUSED;
+      return 0;
+      break;
+
+   case CDROMRESUME:    /* Start the drive after being paused */
+      if (sony_audio_status != CDROM_AUDIO_PAUSED)
+      {
+         return -EINVAL;
+      }
+      
+      do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+      
+      /* Start the drive at the saved position. */
+      params[1] = cur_pos_msf[0];
+      params[2] = cur_pos_msf[1];
+      params[3] = cur_pos_msf[2];
+      params[4] = final_pos_msf[0];
+      params[5] = final_pos_msf[1];
+      params[6] = final_pos_msf[2];
+      params[0] = 0x03;
+      do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
+      if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+      {
+         printk("Sony CDROM error 0x%2.2x (CDROMRESUME)\n", res_reg[1]);
+         return -EIO;
+      }
+      sony_audio_status = CDROM_AUDIO_PLAY;
+      return 0;
+      break;
+
+   case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
+      verify_area(VERIFY_READ, (char *) arg, 6);
+      do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+      memcpy_fromfs(&(params[1]), (void *) arg, 6);
+      
+      /* The parameters are given in int, must be converted */
+      for (i=1; i<7; i++)
+      {
+         params[i] = int_to_bcd(params[i]);
+      }
+      params[0] = 0x03;
+      do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
+      if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+      {
+         printk("Sony CDROM error 0x%2.2x (CDROMPLAYMSF)\n", res_reg[1]);
+         return -EIO;
+      }
+      
+      /* Save the final position for pauses and resumes */
+      final_pos_msf[0] = params[4];
+      final_pos_msf[1] = params[5];
+      final_pos_msf[2] = params[6];
+      sony_audio_status = CDROM_AUDIO_PLAY;
+      return 0;
+      break;
+
+   case CDROMREADTOCHDR:        /* Read the table of contents header */
+      {
+         struct cdrom_tochdr *hdr;
+         struct cdrom_tochdr loc_hdr;
+         
+         sony_get_toc();
+         if (!sony_toc_read)
+         {
+            return -EIO;
+         }
+         
+         hdr = (struct cdrom_tochdr *) arg;
+         verify_area(VERIFY_WRITE, hdr, sizeof(*hdr));
+         loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
+         loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
+         memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr));
+      }
+      return 0;
+      break;
+
+   case CDROMREADTOCENTRY:      /* Read a given table of contents entry */
+      {
+         struct cdrom_tocentry *entry;
+         struct cdrom_tocentry loc_entry;
+         int track_idx;
+         unsigned char *msf_val = NULL;
+         
+         sony_get_toc();
+         if (!sony_toc_read)
+         {
+            return -EIO;
+         }
+         
+         entry = (struct cdrom_tocentry *) arg;
+         verify_area(VERIFY_READ, entry, sizeof(*entry));
+         verify_area(VERIFY_WRITE, entry, sizeof(*entry));
+         
+         memcpy_fromfs(&loc_entry, entry, sizeof(loc_entry));
+         
+         /* Lead out is handled separately since it is special. */
+         if (loc_entry.cdte_track == CDROM_LEADOUT)
+         {
+            loc_entry.cdte_adr = sony_toc->address2;
+            loc_entry.cdte_ctrl = sony_toc->control2;
+            msf_val = sony_toc->lead_out_start_msf;
+         }
+         else
+         {
+            track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
+            if (track_idx < 0)
+            {
+               return -EINVAL;
+            }
+            
+            loc_entry.cdte_adr = sony_toc->tracks[track_idx].address;
+            loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control;
+            msf_val = sony_toc->tracks[track_idx].track_start_msf;
+         }
+         
+         /* Logical buffer address or MSF format requested? */
+         if (loc_entry.cdte_format == CDROM_LBA)
+         {
+            loc_entry.cdte_addr.lba = msf_to_log(msf_val);
+         }
+         else if (loc_entry.cdte_format == CDROM_MSF)
+         {
+            loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val);
+            loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1));
+            loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2));
+         }
+         memcpy_tofs(entry, &loc_entry, sizeof(*entry));
+      }
+      return 0;
+      break;
+
+   case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
+      {
+         struct cdrom_ti ti;
+         int track_idx;
+         
+         sony_get_toc();
+         if (!sony_toc_read)
+         {
+            return -EIO;
+         }
+         
+         verify_area(VERIFY_READ, (char *) arg, sizeof(ti));
+         
+         memcpy_fromfs(&ti, (char *) arg, sizeof(ti));
+         if (   (ti.cdti_trk0 < sony_toc->first_track_num)
+             || (ti.cdti_trk0 > sony_toc->last_track_num)
+             || (ti.cdti_trk1 < ti.cdti_trk0))
+         {
+            return -EINVAL;
+         }
+         
+         track_idx = find_track(int_to_bcd(ti.cdti_trk0));
+         if (track_idx < 0)
+         {
+            return -EINVAL;
+         }
+         params[1] = sony_toc->tracks[track_idx].track_start_msf[0];
+         params[2] = sony_toc->tracks[track_idx].track_start_msf[1];
+         params[3] = sony_toc->tracks[track_idx].track_start_msf[2];
+         
+         /*
+          * If we want to stop after the last track, use the lead-out
+          * MSF to do that.
+          */
+         if (ti.cdti_trk1 >= bcd_to_int(sony_toc->last_track_num))
+         {
+            log_to_msf(msf_to_log(sony_toc->lead_out_start_msf)-1,
+                       &(params[4]));
+         }
+         else
+         {
+            track_idx = find_track(int_to_bcd(ti.cdti_trk1+1));
+            if (track_idx < 0)
+            {
+               return -EINVAL;
+            }
+            log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf)-1,
+                       &(params[4]));
+         }
+         params[0] = 0x03;
+         
+         do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+         
+         do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
+         if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+         {
+            printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1],
+                   params[2], params[3], params[4], params[5], params[6]);
+            printk("Sony CDROM error 0x%2.2x (CDROMPLAYTRKIND\n", res_reg[1]);
+            return -EIO;
+         }
+         
+         /* Save the final position for pauses and resumes */
+         final_pos_msf[0] = params[4];
+         final_pos_msf[1] = params[5];
+         final_pos_msf[2] = params[6];
+         sony_audio_status = CDROM_AUDIO_PLAY;
+         return 0;
+      }
+     
+   case CDROMSUBCHNL:   /* Get subchannel info */
+      return sony_get_subchnl_info(arg);
+
+   case CDROMVOLCTRL:   /* Volume control.  What volume does this change, anyway? */
+      {
+         struct cdrom_volctrl volctrl;
+         
+         verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl));
+         
+         memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
+         params[0] = SONY_SD_AUDIO_VOLUME;
+         params[1] = volctrl.channel0;
+         params[2] = volctrl.channel1;
+         do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, params, 3, res_reg, &res_size);
+         if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+         {
+            printk("Sony CDROM error 0x%2.2x (CDROMVOLCTRL)\n", res_reg[1]);
+            return -EIO;
+         }
+      }
+      return 0;
+
+   case CDROMEJECT:     /* Eject the drive */
+      do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
+      do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+
+      sony_audio_status = CDROM_AUDIO_INVALID;
+      do_sony_cd_cmd(SONY_EJECT_CMD, NULL, 0, res_reg, &res_size);
+      if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+      {
+         printk("Sony CDROM error 0x%2.2x (CDROMEJECT)\n", res_reg[1]);
+         return -EIO;
+      }
+      return 0;
+      break;
+     
+   default:
+      return -EINVAL;
+   }
+}
+
+
+/*
+ * Open the drive for operations.  Spin the drive up and read the table of
+ * contents if these have not already been done.
+ */
+static int
+scd_open(struct inode *inode,
+         struct file *filp)
+{
+   unsigned char res_reg[2];
+   unsigned int res_size;
+
+
+   if (!sony_spun_up)
+   {
+      do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+
+      /* The drive sometimes returns error 0.  I don't know why, but ignore
+         it.  It seems to mean the drive has already done the operation. */
+      if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
+      {
+         printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]);
+         return -EIO;
+      }
+      
+      do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
+
+      /* The drive sometimes returns error 0.  I don't know why, but ignore
+         it.  It seems to mean the drive has already done the operation. */
+      if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
+      {
+         /* If the drive is already playing, its ok.  */
+         if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0))
+         {
+            goto drive_spinning;
+         }
+
+         printk("Sony CDROM error 0x%2.2x (scd_open, read toc)\n", res_reg[1]);
+         do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+         
+         return -EIO;
+      }
+
+      sony_get_toc();
+      if (!sony_toc_read)
+      {
+         do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+         return -EIO;
+      }
+
+      sony_spun_up = 1;
+   }
+
+drive_spinning:
+
+   if (inode)
+   {
+      check_disk_change(inode->i_rdev);
+   }
+
+   sony_usage++;
+
+   return 0;
+}
+
+
+/*
+ * Close the drive.  Spin it down if no task is using it.  The spin
+ * down will fail if playing audio, so audio play is OK.
+ */
+static void
+scd_release(struct inode *inode,
+         struct file *filp)
+{
+   unsigned char res_reg[2];
+   unsigned int  res_size;
+
+
+   if (sony_usage > 0)
+   {
+      sony_usage--;
+   }
+   if (sony_usage == 0)
+   {
+      sync_dev(inode->i_rdev);
+      do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+
+      sony_spun_up = 0;
+   }
+}
+
+
+static struct file_operations scd_fops = {
+   NULL,                   /* lseek - default */
+   block_read,             /* read - general block-dev read */
+   block_write,            /* write - general block-dev write */
+   NULL,                   /* readdir - bad */
+   NULL,                   /* select */
+   scd_ioctl,              /* ioctl */
+   NULL,                   /* mmap */
+   scd_open,               /* open */
+   scd_release             /* release */
+};
+
+
+/* The different types of disc loading mechanisms supported */
+static char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" };
+
+/* Read-ahead buffer sizes for different drives.  These are just arbitrary
+   values, I don't know what is really optimum. */
+static unsigned int mem_size[] = { 4096, 8192, 16384, 2048 };
+
+void
+get_drive_configuration(unsigned short base_io,
+                        unsigned char res_reg[],
+                        unsigned int *res_size)
+{
+   int retry_count;
+
+
+   /* Set the base address */
+   sony_cd_base_io = base_io;
+
+   /* Set up all the register locations */
+   sony_cd_cmd_reg = sony_cd_base_io + SONY_CMD_REG_OFFSET;
+   sony_cd_param_reg = sony_cd_base_io + SONY_PARAM_REG_OFFSET;
+   sony_cd_write_reg = sony_cd_base_io + SONY_WRITE_REG_OFFSET;
+   sony_cd_control_reg = sony_cd_base_io + SONY_CONTROL_REG_OFFSET;
+   sony_cd_status_reg = sony_cd_base_io + SONY_STATUS_REG_OFFSET;
+   sony_cd_result_reg = sony_cd_base_io + SONY_RESULT_REG_OFFSET;
+   sony_cd_read_reg = sony_cd_base_io + SONY_READ_REG_OFFSET;
+   sony_cd_fifost_reg = sony_cd_base_io + SONY_FIFOST_REG_OFFSET;
+
+   /*
+    * Check to see if anything exists at the status register location.
+    * I don't know if this is a good way to check, but it seems to work
+    * ok for me.
+    */
+   if (read_status_register() != 0xff)
+   {
+      /*
+       * Reset the drive and wait for attention from it (to say its reset).
+       * If you don't wait, the next operation will probably fail.
+       */
+      reset_drive();
+      retry_count = jiffies + SONY_RESET_TIMEOUT;
+      while ((retry_count > jiffies) && (!is_attention()))
+      {
+         sony_sleep();
+      }
+
+      /* If attention is never seen probably not a CDU31a present */
+      if (!is_attention())
+      {
+         res_reg[0] = 0x20;
+         return;
+      }
+
+      /*
+       * Get the drive configuration.
+       */
+      do_sony_cd_cmd(SONY_REQ_DRIVE_CONFIG_CMD,
+                     NULL,
+                     0,
+                     (unsigned char *) res_reg,
+                     res_size);
+      return;
+   }
+
+   /* Return an error */
+   res_reg[0] = 0x20;
+}
+
+
+/*
+ * Initialize the driver.
+ */
+unsigned long
+cdu31a_init(unsigned long mem_start, unsigned long mem_end)
+{
+   struct s_sony_drive_config drive_config;
+   unsigned char params[3];
+   unsigned char res_reg[2];
+   unsigned int res_size;
+   int i;
+   int drive_found;
+
+
+   /*
+    * According to Alex Freed (freed@europa.orion.adobe.com), this is
+    * required for the Fusion CD-16 package.  If the sound driver is
+    * loaded, it should work fine, but just in case...
+    *
+    * The following turn on the CD-ROM interface for a Fusion CD-16.
+    */
+   outb(0xbc, 0x9a01);
+   outb(0xe2, 0x9a01);
+
+   i = 0;
+   drive_found = 0;
+   while (   (cdu31a_addresses[i] != 0)
+          && (!drive_found))
+   {
+      get_drive_configuration(cdu31a_addresses[i],
+                               drive_config.exec_status,
+                               &res_size);
+      if ((res_size > 2) && ((drive_config.exec_status[0] & 0x20) == 0x00))
+      {
+         drive_found = 1;
+
+         if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
+         {
+            printk("Unable to get major %d for CDU-31a\n", MAJOR_NR);
+            return mem_start;
+         }
+
+         sony_buffer_size = mem_size[SONY_HWC_GET_BUF_MEM_SIZE(drive_config)];
+         sony_buffer_sectors = sony_buffer_size / 2048;
+
+         printk("Sony I/F CDROM : %8.8s %16.16s %8.8s with %s load mechanism\n",
+                drive_config.vendor_id,
+                drive_config.product_id,
+                drive_config.product_rev_level,
+                load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
+         printk("  using %d byte buffer", sony_buffer_size);
+         if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
+         {
+            printk(", capable of audio playback");
+         }
+         printk("\n");
+
+         params[0] = SONY_SD_MECH_CONTROL;
+         params[1] = 0x03;
+         do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
+                        params,
+                        2,
+                        res_reg,
+                        &res_size);
+         if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+         {
+            printk("  Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]);
+         }
+
+         blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+         read_ahead[MAJOR_NR] = 8;               /* 8 sector (4kB) read-ahead */
+
+         sony_toc = (struct s_sony_toc *) mem_start;
+         mem_start += sizeof(*sony_toc);
+         last_sony_subcode = (struct s_sony_subcode *) mem_start;
+         mem_start += sizeof(*last_sony_subcode);
+         sony_buffer = (unsigned char *) mem_start;
+         mem_start += sony_buffer_size;
+
+         initialized = 1;
+      }
+
+      i++;
+   }
+   
+   return mem_start;
+}
+
+#endif
diff --git a/kernel/blk_drv/floppy.c b/kernel/blk_drv/floppy.c
index 8890c36..493571e 100644
--- a/kernel/blk_drv/floppy.c
+++ b/kernel/blk_drv/floppy.c
@@ -121,8 +121,12 @@
 /*
  * The DMA channel used by the floppy controller cannot access data at
  * addresses >= 16MB
+ *
+ * Went back to the 1MB limit, as some people had problems with the floppy
+ * driver otherwise. It doesn't matter much for performance anyway, as most
+ * floppy accesses go through the track buffer.
  */
-#define LAST_DMA_ADDR	(0x1000000 - BLOCK_SIZE)
+#define LAST_DMA_ADDR	(0x100000 - BLOCK_SIZE)
 
 /*
  * globals used by 'result()'
@@ -1137,6 +1141,8 @@
 		case FDFMTTRK:
 			if (!suser())
 				return -EPERM;
+			if (fd_ref[drive & 3] != 1)
+				return -EBUSY;
 			cli();
 			while (format_status != FORMAT_NONE)
 				sleep_on(&format_done);
diff --git a/kernel/blk_drv/hd.c b/kernel/blk_drv/hd.c
index b1f547b..d1d2258 100644
--- a/kernel/blk_drv/hd.c
+++ b/kernel/blk_drv/hd.c
@@ -556,6 +556,13 @@
 			put_fs_long(hd[MINOR(inode->i_rdev)].nr_sects,
 				(long *) arg);
 			return 0;
+		case BLKFLSBUF:
+			if(!suser())  return -EACCES;
+			if(!inode->i_rdev) return -EINVAL;
+			sync_dev(inode->i_rdev);
+			invalidate_buffers(inode->i_rdev);
+			return 0;
+
 		case BLKRRPART: /* Re-read partition tables */
 			return revalidate_hddisk(inode->i_rdev, 1);
 		RO_IOCTLS(inode->i_rdev,arg);
diff --git a/kernel/blk_drv/ll_rw_blk.c b/kernel/blk_drv/ll_rw_blk.c
index f6b3cae..4920317 100644
--- a/kernel/blk_drv/ll_rw_blk.c
+++ b/kernel/blk_drv/ll_rw_blk.c
@@ -185,6 +185,22 @@
 				sti();
 				return;
 			      }
+			else if ( req->dev == bh->b_dev &&
+			    !req->waiting &&
+			    req->cmd == rw &&
+			    req->sector - count == sector &&
+			    req->nr_sectors < 254)
+			    	{
+			    	req->nr_sectors += count;
+			    	bh->b_reqnext = req->bh;
+			    	req->buffer = bh->b_data;
+			    	req->current_nr_sectors = count;
+			    	req->sector = sector;
+			    	bh->b_dirt = 0;
+			    	req->bh = bh;
+			    	sti();
+			    	return;
+			    }    
 			req = req->next;
 		      }
 	      }
@@ -415,6 +431,12 @@
 #ifdef CONFIG_BLK_DEV_XD
 	mem_start = xd_init(mem_start,mem_end);
 #endif
+#ifdef CONFIG_CDU31A
+	mem_start = cdu31a_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_MCD
+	mem_start = mcd_init(mem_start,mem_end);
+#endif
 	if (ramdisk_size)
 		mem_start += rd_init(mem_start, ramdisk_size*1024);
 	return mem_start;
diff --git a/kernel/blk_drv/mcd.c b/kernel/blk_drv/mcd.c
new file mode 100644
index 0000000..d05c872
--- /dev/null
+++ b/kernel/blk_drv/mcd.c
@@ -0,0 +1,1196 @@
+/*
+	linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver
+
+	Copyright (C) 1992  Martin Harriss
+
+	martin@bdsi.com
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2, or (at your option)
+	any later version.
+
+	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.
+
+	HISTORY
+
+	0.1	First attempt - internal use only
+	0.2	Cleaned up delays and use of timer - alpha release
+	0.3	Audio support added
+	0.3.1 Changes for mitsumi CRMC LU005S march version
+		   (stud11@cc4.kuleuven.ac.be)
+        0.3.2 bug fixes to the ioclts and merged with ALPHA0.99-pl12
+		   (Jon Tombs <jon@robots.ox.ac.uk>)
+*/
+
+#include <linux/config.h>
+#ifdef CONFIG_MCD
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+
+/* #define REALLY_SLOW_IO  */
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR 23
+#include "blk.h"
+#include <linux/mcd.h>
+
+#if 0
+static int mcd_sizes[] = { 0 };
+#endif
+
+static int mcdPresent = 0;
+
+static char mcd_buf[2048];	/* buffer for block size conversion */
+static int mcd_bn = -1;
+
+static int McdTimeout, McdTries;
+static struct wait_queue *mcd_waitq = NULL;
+
+static struct mcd_DiskInfo DiskInfo;
+static struct mcd_Toc Toc[MAX_TRACKS];
+static struct mcd_Play_msf mcd_Play;
+
+static int audioStatus;
+static char mcdDiskChanged;
+static char tocUpToDate;
+static char mcdVersion;
+
+static void mcd_transfer(void);
+static void mcd_start(void);
+static void mcd_status(void);
+static void mcd_read_cmd(void);
+static void mcd_data(void);
+static void do_mcd_request(void);
+static void hsg2msf(long hsg, struct msf *msf);
+static void bin2bcd(unsigned char *p);
+static int bcd2bin(unsigned char bcd);
+static int mcdStatus(void);
+static void sendMcdCmd(int cmd, struct mcd_Play_msf *params);
+static int getMcdStatus(int timeout);
+static int GetQChannelInfo(struct mcd_Toc *qp);
+static int updateToc(void);
+static int GetDiskInfo(void);
+static int GetToc(void);
+static int getValue(unsigned char *result);
+
+ 
+int
+check_mcd_media_change(int full_dev, int flag)
+{
+   int retval, target;
+
+
+   target = MINOR(full_dev);
+
+   if (target > 0) {
+      printk("Mitsumi CD-ROM request error: invalid device.\n");
+      return 0;
+   }
+
+   retval = mcdDiskChanged;
+   if (!flag)
+   {
+      mcdDiskChanged = 0;
+   }
+
+   return retval;
+}
+
+
+/*
+ * Do a 'get status' command and get the result.  Only use from the top half
+ * because it calls 'getMcdStatus' which sleeps.
+ */
+
+static int
+statusCmd(void)
+{
+	int st, retry;
+
+	for (retry = 0; retry < 3; retry++)
+	{
+		outb(MCMD_GET_STATUS, MCDPORT(0));	/* send get-status cmd */
+		st = getMcdStatus(100);
+		if (st != -1)
+			break;
+	}
+
+	return st;
+}
+
+
+/*
+ * Send a 'Play' command and get the status.  Use only from the top half.
+ */
+
+static int
+mcdPlay(struct mcd_Play_msf *arg)
+{
+	int retry, st;
+
+	for (retry = 0; retry < 3; retry++)
+	{
+		sendMcdCmd(MCMD_PLAY_READ, arg);
+		st = getMcdStatus(200);
+		if (st != -1)
+			break;
+	}
+
+	return st;
+}
+
+
+long
+msf2hsg(struct msf *mp)
+{
+	return bcd2bin(mp -> frame)
+		+ bcd2bin(mp -> sec) * 75
+		+ bcd2bin(mp -> min) * 4500
+		- 150;
+}
+
+
+static int
+mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
+						unsigned long arg)
+{
+	int i, st;
+	struct mcd_Toc qInfo;
+	struct cdrom_ti ti;
+	struct cdrom_tochdr tocHdr;
+	struct cdrom_msf msf;
+	struct cdrom_tocentry entry;
+	struct mcd_Toc *tocPtr;
+	struct cdrom_subchnl subchnl;
+#if 0
+	struct cdrom_volctrl volctrl;
+#endif
+
+	if (!ip)
+		return -EINVAL;
+
+	st = statusCmd();
+	if (st < 0)
+		return -EIO;
+
+	if (!tocUpToDate)
+	{
+		i = updateToc();
+		if (i < 0)
+			return i;	/* error reading TOC */
+	}
+
+	switch (cmd)
+	{
+	case CDROMSTART:     /* Spin up the drive */
+		/* Don't think we can do this.  Even if we could,
+ 		 * I think the drive times out and stops after a while
+		 * anyway.  For now, ignore it.
+		 */
+
+		return 0;
+
+	case CDROMSTOP:      /* Spin down the drive */
+		outb(MCMD_STOP, MCDPORT(0));
+		i = getMcdStatus(100);
+
+		/* should we do anything if it fails? */
+
+		audioStatus = CDROM_AUDIO_NO_STATUS;
+		return 0;
+
+	case CDROMPAUSE:     /* Pause the drive */
+		if (audioStatus != CDROM_AUDIO_PLAY)
+			return -EINVAL;
+
+		outb(MCMD_STOP, MCDPORT(0));
+		i = getMcdStatus(100);
+
+		if (GetQChannelInfo(&qInfo) < 0)
+		{
+			/* didn't get q channel info */
+
+			audioStatus = CDROM_AUDIO_NO_STATUS;
+			return 0;
+		}
+
+		mcd_Play.start = qInfo.diskTime;	/* remember restart point */
+
+		audioStatus = CDROM_AUDIO_PAUSED;
+		return 0;
+
+	case CDROMRESUME:    /* Play it again, Sam */
+		if (audioStatus != CDROM_AUDIO_PAUSED)
+			return -EINVAL;
+
+		/* restart the drive at the saved position. */
+
+		i = mcdPlay(&mcd_Play);
+		if (i < 0)
+		{
+			audioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+
+		audioStatus = CDROM_AUDIO_PLAY;
+		return 0;
+
+	case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
+
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
+		if (st)
+			return st;
+
+		memcpy_fromfs(&ti, (void *) arg, sizeof ti);
+
+		if (ti.cdti_trk0 < DiskInfo.first
+			|| ti.cdti_trk0 > DiskInfo.last
+			|| ti.cdti_trk1 < ti.cdti_trk0)
+		{
+			return -EINVAL;
+		}
+
+		if (ti.cdti_trk1 > DiskInfo.last)
+			ti. cdti_trk1 = DiskInfo.last;
+
+		mcd_Play.start = Toc[ti.cdti_trk0].diskTime;
+		mcd_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
+
+#ifdef MCD_DEBUG
+printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+	mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,
+	mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);
+#endif
+
+		i = mcdPlay(&mcd_Play);
+		if (i < 0)
+		{
+			audioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+
+		audioStatus = CDROM_AUDIO_PLAY;
+		return 0;
+
+	case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
+
+		if (audioStatus == CDROM_AUDIO_PLAY) {
+		  outb(MCMD_STOP, MCDPORT(0));
+		  i = getMcdStatus(100);
+		  audioStatus = CDROM_AUDIO_NO_STATUS;
+		}
+
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
+		if (st)
+			return st;
+
+		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+
+		/* convert to bcd */
+
+		bin2bcd(&msf.cdmsf_min0);
+		bin2bcd(&msf.cdmsf_sec0);
+		bin2bcd(&msf.cdmsf_frame0);
+		bin2bcd(&msf.cdmsf_min1);
+		bin2bcd(&msf.cdmsf_sec1);
+		bin2bcd(&msf.cdmsf_frame1);
+
+		mcd_Play.start.min = msf.cdmsf_min0;
+		mcd_Play.start.sec = msf.cdmsf_sec0;
+		mcd_Play.start.frame = msf.cdmsf_frame0;
+		mcd_Play.end.min = msf.cdmsf_min1;
+		mcd_Play.end.sec = msf.cdmsf_sec1;
+		mcd_Play.end.frame = msf.cdmsf_frame1;
+
+#ifdef MCD_DEBUG
+printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,
+mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);
+#endif
+
+		i = mcdPlay(&mcd_Play);
+		if (i < 0)
+		{
+			audioStatus = CDROM_AUDIO_ERROR;
+			return -EIO;
+		}
+
+		audioStatus = CDROM_AUDIO_PLAY;
+		return 0;
+
+	case CDROMREADTOCHDR:        /* Read the table of contents header */
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr);
+		if (st)
+			return st;
+
+		tocHdr.cdth_trk0 = DiskInfo.first;
+		tocHdr.cdth_trk1 = DiskInfo.last;
+		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
+		return 0;
+
+	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
+
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
+		if (st)
+			return st;
+
+		memcpy_fromfs(&entry, (void *) arg, sizeof entry);
+		if (entry.cdte_track == CDROM_LEADOUT)
+			/* XXX */
+			tocPtr = &Toc[DiskInfo.last + 1];
+
+		else if (entry.cdte_track > DiskInfo.last
+				|| entry.cdte_track < DiskInfo.first)
+			return -EINVAL;
+
+		else
+			tocPtr = &Toc[entry.cdte_track];
+
+		entry.cdte_adr = tocPtr -> ctrl_addr;
+		entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
+
+		if (entry.cdte_format == CDROM_LBA)
+			entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime);
+
+		else if (entry.cdte_format == CDROM_MSF)
+		{
+			entry.cdte_addr.msf.minute = bcd2bin(tocPtr -> diskTime.min);
+			entry.cdte_addr.msf.second = bcd2bin(tocPtr -> diskTime.sec);
+			entry.cdte_addr.msf.frame = bcd2bin(tocPtr -> diskTime.frame);
+		}
+
+		else
+			return -EINVAL;
+
+		memcpy_tofs((void *) arg, &entry, sizeof entry);
+		return 0;
+
+	case CDROMSUBCHNL:   /* Get subchannel info */
+
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl);
+		if (st)
+			return st;
+
+		memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
+
+		if (GetQChannelInfo(&qInfo) < 0)
+			return -EIO;
+
+		subchnl.cdsc_audiostatus = audioStatus;
+		subchnl.cdsc_adr = qInfo.ctrl_addr;
+		subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
+		subchnl.cdsc_trk = bcd2bin(qInfo.track);
+		subchnl.cdsc_ind = bcd2bin(qInfo.pointIndex);
+
+		if (subchnl.cdsc_format == CDROM_LBA)
+		{
+			subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime);
+			subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime);
+		}
+
+		else if (subchnl.cdsc_format == CDROM_MSF)
+		{
+			subchnl.cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min);
+			subchnl.cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec);
+			subchnl.cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame);
+
+			subchnl.cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min);
+			subchnl.cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec);
+			subchnl.cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame);
+		}
+
+		else
+			return -EINVAL;
+
+		memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
+		return 0;
+
+	case CDROMVOLCTRL:   /* Volume control */
+	/*
+	 * This is not working yet.  Setting the volume by itself does
+	 * nothing.  Following the 'set' by a 'play' results in zero
+	 * volume.  Something to work on for the next release.
+	 */
+#if 0
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl));
+		if (st)
+			return st;
+
+		memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
+printk("VOL %d %d\n", volctrl.channel0 & 0xFF, volctrl.channel1 & 0xFF);
+		outb(MCMD_SET_VOLUME, MCDPORT(0));
+		outb(volctrl.channel0, MCDPORT(0));
+		outb(0, MCDPORT(0));
+		outb(volctrl.channel1, MCDPORT(0));
+		outb(1, MCDPORT(0));
+
+		i = getMcdStatus(100);
+		if (i < 0)
+			return -EIO;
+
+		{
+			int a, b, c, d;
+
+			getValue(&a);
+			getValue(&b);
+			getValue(&c);
+			getValue(&d);
+			printk("%02X %02X %02X %02X\n", a, b, c, d);
+		}
+
+		outb(0xF8, MCDPORT(0));
+		i = getMcdStatus(100);
+		printk("F8 -> %02X\n", i & 0xFF);
+#endif
+		return 0;
+
+	case CDROMEJECT:     /* Eject the drive - N/A */
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+
+/*
+ * Take care of the different block sizes between cdrom and Linux.
+ * When Linux gets variable block sizes this will probably go away.
+ */
+
+static void
+mcd_transfer(void)
+{
+	long offs;
+
+	while (CURRENT -> nr_sectors > 0 && mcd_bn == CURRENT -> sector / 4)
+	{
+		offs = (CURRENT -> sector & 3) * 512;
+		memcpy(CURRENT -> buffer, mcd_buf + offs, 512);
+		CURRENT -> nr_sectors--;
+		CURRENT -> sector++;
+		CURRENT -> buffer += 512;
+	}
+}
+
+
+/*
+ * We only seem to get interrupts after an error.
+ * Just take the interrupt and clear out the status reg.
+ */
+
+static void
+mcd_interrupt(int unused)
+{
+	int st;
+
+	st = inb(MCDPORT(1)) & 0xFF;
+	if (st != 0xFF)
+	{
+		st = inb(MCDPORT(0)) & 0xFF;
+#if 0
+		printk("<int-%02X>", st);
+#endif
+	}
+}
+
+
+/*
+ * I/O request routine called from Linux kernel.
+ */
+
+static void
+do_mcd_request(void)
+{
+	unsigned int block,dev;
+	unsigned int nsect;
+
+repeat:
+	INIT_REQUEST;
+	dev = MINOR(CURRENT->dev);
+	block = CURRENT->sector;
+	nsect = CURRENT->nr_sectors;
+
+	if (CURRENT == NULL || CURRENT -> sector == -1)
+		return;
+
+	if (CURRENT -> cmd != READ)
+	{
+		printk("mcd: bad cmd %d\n", CURRENT -> cmd);
+		end_request(0);
+		goto repeat;
+	}
+
+	mcd_transfer();
+
+	/* if we satisfied the request from the buffer, we're done. */
+
+	if (CURRENT -> nr_sectors == 0)
+	{
+		end_request(1);
+		goto repeat;
+	}
+
+	McdTries = 3;
+	mcd_start();
+}
+
+
+/*
+ * Start the I/O for the cdrom. Handle retry count.
+ */
+
+static void
+mcd_start()
+{
+	if (McdTries == 0)
+	{
+		printk("mcd: read failed after 3 tries\n");
+		end_request(0);
+		SET_TIMER(do_mcd_request, 1);	/* wait a bit, try again */
+		return;
+	}
+
+	McdTries--;
+	outb(0x40, MCDPORT(0));		/* get status */
+	McdTimeout = 100;
+	SET_TIMER(mcd_status, 1);
+}
+
+
+/*
+ * Called from the timer to check the results of the get-status cmd.
+ * On success, send the set-mode command.
+ */
+
+static void
+mcd_status()
+{
+	int st;
+
+	McdTimeout--;
+	st = mcdStatus();
+	if (st == -1)
+	{
+		if (McdTimeout == 0)
+		{
+			printk("mcd: status timed out\n");
+			SET_TIMER(mcd_start, 1);	/* wait a bit, try again */
+			return;
+		}
+
+		SET_TIMER(mcd_status, 1);
+		return;
+	}
+
+	if (st & MST_DSK_CHG)
+	{
+		mcdDiskChanged = 1;
+	}
+	
+	if ((st & MST_READY) == 0)
+	{
+		printk("mcd: disk removed\n");
+		mcdDiskChanged = 1;		
+		end_request(0);
+		do_mcd_request();
+		return;
+	}
+
+	outb(0x50, MCDPORT(0));	/* set mode */
+	outb(0x01, MCDPORT(0));	/* mode = cooked data */
+	McdTimeout = 100;
+	SET_TIMER(mcd_read_cmd, 1);
+}
+
+
+/*
+ * Check the result of the set-mode command.  On success, send the
+ * read-data command.
+ */
+
+static void
+mcd_read_cmd()
+{
+	int st;
+	long block;
+	struct mcd_Play_msf mcdcmd;
+
+	McdTimeout--;
+	st = mcdStatus();
+
+	if (st & MST_DSK_CHG)
+	{
+		mcdDiskChanged = 1;
+	}
+	
+	if (st == -1)
+	{
+		if (McdTimeout == 0)
+		{
+			printk("mcd: set mode timed out\n");
+			SET_TIMER(mcd_start, 1);	/* wait a bit, try again */
+			return;
+		}
+
+		SET_TIMER(mcd_read_cmd, 1);
+		return;
+	}
+
+	mcd_bn = -1;			/* purge our buffer */
+	block = CURRENT -> sector / 4;
+	hsg2msf(block, &mcdcmd.start);	/* cvt to msf format */
+
+	mcdcmd.end.min = 0;
+	mcdcmd.end.sec = 0;
+	mcdcmd.end.frame = 1;
+
+	sendMcdCmd(MCMD_PLAY_READ, &mcdcmd);	/* read command */
+	McdTimeout = 200;
+	SET_TIMER(mcd_data, 1);
+}
+
+
+/*
+ * Check the completion of the read-data command.  On success, read
+ * the 2048 bytes of data from the disk into our buffer.
+ */
+
+static void
+mcd_data()
+{
+	int i;
+
+	McdTimeout--;
+	cli();
+	i =inb(MCDPORT(1)) & (MFL_STATUS | MFL_DATA);
+	if (i == MFL_DATA)
+	{
+		printk("mcd: read failed\n");
+#ifdef MCD_DEBUG
+		printk("got 0xB %02X\n", inb(MCDPORT(0)) & 0xFF);
+#endif
+		SET_TIMER(mcd_start, 1);
+		sti();
+		return;
+	}
+	
+	if (i == (MFL_STATUS | MFL_DATA))
+	{
+		if (McdTimeout == 0)
+		{
+			printk("mcd: data timeout, retrying\n");
+			SET_TIMER(mcd_start, 1);
+		}
+		
+		else
+			SET_TIMER(mcd_data, 1);
+		
+		sti();
+		return;
+	}
+
+	CLEAR_TIMER;
+	READ_DATA(MCDPORT(0), &mcd_buf[0], 2048);
+	sti();
+
+	mcd_bn = CURRENT -> sector / 4;
+	mcd_transfer();
+	end_request(1);
+	SET_TIMER(do_mcd_request, 1);
+}
+
+
+/*
+ * Open the device special file.  Check that a disk is in.
+ */
+
+int
+mcd_open(struct inode *ip, struct file *fp)
+{
+	int st;
+
+	if (mcdPresent == 0)
+		return -ENXIO;			/* no hardware */
+
+	st = statusCmd();			/* check drive status */
+	if (st == -1)
+		return -EIO;			/* drive doesn't respond */
+
+	if ((st & MST_READY) == 0)		/* no disk in drive */
+	{
+		printk("mcd: no disk in drive\n");
+		return -EIO;
+	}
+
+	if (updateToc() < 0)
+		return -EIO;
+
+	return 0;
+}
+
+
+/*
+ * On close, we flush all mcd blocks from the buffer cache.
+ */
+
+static void
+mcd_release(struct inode * inode, struct file * file)
+{
+	mcd_bn = -1;
+	sync_dev(inode->i_rdev);
+	invalidate_buffers(inode -> i_rdev);
+}
+
+
+static struct file_operations mcd_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	mcd_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	mcd_open,		/* open */
+	mcd_release		/* release */
+};
+
+
+/*
+ * MCD interrupt descriptor
+ */
+
+static struct sigaction mcd_sigaction = {
+	mcd_interrupt,
+	0,
+	SA_INTERRUPT,
+	NULL
+};
+
+
+/*
+ * Test for presence of drive and initialize it.  Called at boot time.
+ */
+
+unsigned long
+mcd_init(unsigned long mem_start, unsigned long mem_end)
+{
+	int count;
+	unsigned char result[3];
+
+	if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0)
+	{
+		printk("Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR);
+		return mem_start;
+	}
+
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 4;
+
+	/* check for card */
+
+	outb(0, MCDPORT(1));			/* send reset */
+	for (count = 0; count < 1000000; count++)
+		(void) inb(MCDPORT(1));		/* delay a bit */
+
+	outb(0x40, MCDPORT(0));			/* send get-stat cmd */
+	for (count = 0; count < 1000000; count++)
+		if (!(inb(MCDPORT(1)) & MFL_STATUS))
+			break;
+
+	if (count >= 1000000) {
+		printk("mitsumi init failed...\n");
+		return mem_start;
+	}
+	count = inb(MCDPORT(0));		/* pick up the status */
+	
+	outb(MCMD_GET_VERSION,MCDPORT(0));
+	for(count=0;count<3;count++)
+		if(getValue(result+count)) {
+			printk("mitsumi get version failed...\n");
+			return mem_start;
+		}	
+					
+	printk("Mitsumi version : %02X %c %x\n",result[0],result[1],result[2]);
+
+	mcdVersion=result[2];
+
+	if (mcdVersion >=4)
+		outb(4,MCDPORT(2)); 	/* magic happens */
+
+	/* don't get the IRQ until we know for sure the drive is there */
+
+	if (irqaction(MCD_INTR_NR,  &mcd_sigaction))
+	{
+		printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", MCD_INTR_NR);
+		return mem_start;
+	}
+
+	mcdPresent = 1;
+	printk("Mitsumi CD-ROM Drive present\n");
+	return mem_start;
+}
+
+
+static void
+hsg2msf(long hsg, struct msf *msf)
+{
+	hsg += 150;
+	msf -> min = hsg / 4500;
+	hsg %= 4500;
+	msf -> sec = hsg / 75;
+	msf -> frame = hsg % 75;
+
+	bin2bcd(&msf -> min);		/* convert to BCD */
+	bin2bcd(&msf -> sec);
+	bin2bcd(&msf -> frame);
+}
+
+
+static void
+bin2bcd(unsigned char *p)
+{
+	int u, t;
+
+	u = *p % 10;
+	t = *p / 10;
+	*p = u | (t << 4);
+}
+
+static int
+bcd2bin(unsigned char bcd)
+{
+	return (bcd >> 4) * 10 + (bcd & 0xF);
+}
+
+
+/*
+ * See if a status is ready from the drive and return it
+ * if it is ready.
+ */
+
+static int
+mcdStatus(void)
+{
+	int i;
+	int st;
+
+	st = inb(MCDPORT(1)) & MFL_STATUS;
+	if (!st)
+	{
+		i = inb(MCDPORT(0)) & 0xFF;
+		return i;
+	}
+	else
+		return -1;
+}
+
+
+/*
+ * Send a play or read command to the drive
+ */
+
+static void
+sendMcdCmd(int cmd, struct mcd_Play_msf *params)
+{
+	outb(cmd, MCDPORT(0));
+	outb(params -> start.min, MCDPORT(0));
+	outb(params -> start.sec, MCDPORT(0));
+	outb(params -> start.frame, MCDPORT(0));
+	outb(params -> end.min, MCDPORT(0));
+	outb(params -> end.sec, MCDPORT(0));
+	outb(params -> end.frame, MCDPORT(0));
+}
+
+
+/*
+ * Timer interrupt routine to test for status ready from the drive.
+ * (see the next routine)
+ */
+
+static void
+mcdStatTimer(void)
+{
+	if (!(inb(MCDPORT(1)) & MFL_STATUS))
+	{
+		wake_up(&mcd_waitq);
+		return;
+	}
+
+	McdTimeout--;
+	if (McdTimeout <= 0)
+	{
+		wake_up(&mcd_waitq);
+		return;
+	}
+
+	SET_TIMER(mcdStatTimer, 1);
+}
+
+
+/*
+ * Wait for a status to be returned from the drive.  The actual test
+ * (see routine above) is done by the timer interrupt to avoid
+ * excessive rescheduling.
+ */
+
+static int
+getMcdStatus(int timeout)
+{
+	int st;
+
+	McdTimeout = timeout;
+	SET_TIMER(mcdStatTimer, 1);
+	sleep_on(&mcd_waitq);
+	if (McdTimeout <= 0)
+		return -1;
+
+	st = inb(MCDPORT(0)) & 0xFF;
+	if (st == 0xFF)
+		return -1;
+
+	if ((st & MST_BUSY) == 0 && audioStatus == CDROM_AUDIO_PLAY)
+		/* XXX might be an error? look at q-channel? */
+		audioStatus = CDROM_AUDIO_COMPLETED;
+
+	if (st & MST_DSK_CHG)
+	{
+		mcdDiskChanged = 1;
+		tocUpToDate = 0;
+		audioStatus = CDROM_AUDIO_NO_STATUS;
+	}
+
+	return st;
+}
+
+
+/*
+ * Read a value from the drive.  Should return quickly, so a busy wait
+ * is used to avoid excessive rescheduling.
+ */
+
+static int
+getValue(unsigned char *result)
+{
+	int count;
+	int s;
+
+	for (count = 0; count < 2000; count++)
+		if (!(inb(MCDPORT(1)) & MFL_STATUS))
+			break;
+
+	if (count >= 2000)
+	{
+		printk("mcd: getValue timeout\n");
+		return -1;
+	}
+
+	s = inb(MCDPORT(0)) & 0xFF;
+	*result = (unsigned char) s;
+	return 0;
+}
+
+
+/*
+ * Read the current Q-channel info.  Also used for reading the
+ * table of contents.
+ */
+
+int
+GetQChannelInfo(struct mcd_Toc *qp)
+{
+	unsigned char notUsed;
+	int retry;
+
+	for (retry = 0; retry < 3; retry++)
+	{
+		outb(MCMD_GET_Q_CHANNEL, MCDPORT(0));
+		if (getMcdStatus(100) != -1)
+			break;
+	}
+
+	if (retry >= 3)
+		return -1;
+
+	if (getValue(&qp -> ctrl_addr) < 0) return -1;
+	if (getValue(&qp -> track) < 0) return -1;
+	if (getValue(&qp -> pointIndex) < 0) return -1;
+	if (getValue(&qp -> trackTime.min) < 0) return -1;
+	if (getValue(&qp -> trackTime.sec) < 0) return -1;
+	if (getValue(&qp -> trackTime.frame) < 0) return -1;
+	if (getValue(&notUsed) < 0) return -1;
+	if (getValue(&qp -> diskTime.min) < 0) return -1;
+	if (getValue(&qp -> diskTime.sec) < 0) return -1;
+	if (getValue(&qp -> diskTime.frame) < 0) return -1;
+
+	return 0;
+}
+
+
+/*
+ * Read the table of contents (TOC) and TOC header if neccessary
+ */
+
+static int
+updateToc()
+{
+	if (tocUpToDate)
+		return 0;
+
+	if (GetDiskInfo() < 0)
+		return -EIO;
+
+	if (GetToc() < 0)
+		return -EIO;
+
+	tocUpToDate = 1;
+	return 0;
+}
+
+
+/*
+ * Read the table of contents header
+ */
+
+static int
+GetDiskInfo()
+{
+	int retry;
+
+	for (retry = 0; retry < 3; retry++)
+	{
+		outb(MCMD_GET_DISK_INFO, MCDPORT(0));
+		if (getMcdStatus(100) != -1)
+			break;
+	}
+
+	if (retry >= 3)
+		return -1;
+
+	if (getValue(&DiskInfo.first) < 0) return -1;
+	if (getValue(&DiskInfo.last) < 0) return -1;
+
+	DiskInfo.first = bcd2bin(DiskInfo.first);
+	DiskInfo.last = bcd2bin(DiskInfo.last);
+
+	if (getValue(&DiskInfo.diskLength.min) < 0) return -1;
+	if (getValue(&DiskInfo.diskLength.sec) < 0) return -1;
+	if (getValue(&DiskInfo.diskLength.frame) < 0) return -1;
+	if (getValue(&DiskInfo.firstTrack.min) < 0) return -1;
+	if (getValue(&DiskInfo.firstTrack.sec) < 0) return -1;
+	if (getValue(&DiskInfo.firstTrack.frame) < 0) return -1;
+
+#ifdef MCD_DEBUG
+printk("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n",
+	DiskInfo.first,
+	DiskInfo.last,
+	DiskInfo.diskLength.min,
+	DiskInfo.diskLength.sec,
+	DiskInfo.diskLength.frame,
+	DiskInfo.firstTrack.min,
+	DiskInfo.firstTrack.sec,
+	DiskInfo.firstTrack.frame);
+#endif
+
+	return 0;
+}
+
+
+/*
+ * Read the table of contents (TOC)
+ */
+
+static int
+GetToc()
+{
+	int i, px;
+	int limit;
+	int retry;
+	struct mcd_Toc qInfo;
+
+	for (i = 0; i < MAX_TRACKS; i++)
+		Toc[i].pointIndex = 0;
+
+	i = DiskInfo.last + 3;
+
+	for (retry = 0; retry < 3; retry++)
+	{
+		outb(MCMD_STOP, MCDPORT(0));
+		if (getMcdStatus(100) != -1)
+			break;
+	}
+
+	if (retry >= 3)
+		return -1;
+
+	for (retry = 0; retry < 3; retry++)
+	{
+		outb(MCMD_SET_MODE, MCDPORT(0));
+		outb(0x05, MCDPORT(0));			/* mode: toc */
+		if (getMcdStatus(100) != -1)
+			break;
+	}
+
+	if (retry >= 3)
+		return -1;
+
+	for (limit = 300; limit > 0; limit--)
+	{
+		if (GetQChannelInfo(&qInfo) < 0)
+			break;
+
+		px = bcd2bin(qInfo.pointIndex);
+		if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
+			if (Toc[px].pointIndex == 0)
+			{
+				Toc[px] = qInfo;
+				i--;
+			}
+
+		if (i <= 0)
+			break;
+	}
+
+	Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
+
+	for (retry = 0; retry < 3; retry++)
+	{
+                outb(MCMD_SET_MODE, MCDPORT(0));
+                outb(0x01, MCDPORT(0));
+                if (getMcdStatus(100) != -1)
+                        break;
+	}
+
+#ifdef MCD_DEBUG
+for (i = 1; i <= DiskInfo.last; i++)
+printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X    %02X:%02X.%02X\n",
+i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+for (i = 100; i < 103; i++)
+printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X    %02X:%02X.%02X\n",
+i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+#endif
+
+	return limit > 0 ? 0 : -1;
+}
+
+#endif
diff --git a/kernel/blk_drv/ramdisk.c b/kernel/blk_drv/ramdisk.c
index 0bd237e..3f50ea0 100644
--- a/kernel/blk_drv/ramdisk.c
+++ b/kernel/blk_drv/ramdisk.c
@@ -104,7 +104,7 @@
 {
 	struct buffer_head *bh;
 	struct minix_super_block s;
-	int		block, try;
+	int		block, tries;
 	int		i = 1;
 	int		nblocks;
 	char		*cp;
@@ -125,8 +125,8 @@
 	 * case, we have to look at block 0.  Be intelligent about
 	 * this, and check both... - FvK
 	 */
-	for (try = 0; try < 1000; try += 512) {
-		block = try;
+	for (tries = 0; tries < 1000; tries += 512) {
+		block = tries;
 		bh = breada(ROOT_DEV,block+1,block,block+2,-1);
 		if (!bh) {
 			printk("RAMDISK: I/O error while looking for super block!\n");
diff --git a/kernel/blk_drv/scsi/Makefile b/kernel/blk_drv/scsi/Makefile
index 1b0ea8a..aecb001 100644
--- a/kernel/blk_drv/scsi/Makefile
+++ b/kernel/blk_drv/scsi/Makefile
@@ -6,12 +6,11 @@
 # unless its something special (ie not a .c file).
 #
 
-SCSI_HOSTS := 0
 SCSI_OBJS :=
 
 ifdef CONFIG_SCSI
 
-SCSI_OBJS := hosts.o scsi.o scsi_ioctl.o
+SCSI_OBJS := hosts.o scsi.o scsi_ioctl.o constants.o
 
 ifdef CONFIG_BLK_DEV_ST
 SCSI_OBJS := $(SCSI_OBJS) st.o
@@ -27,42 +26,34 @@
 
 ifdef CONFIG_SCSI_AHA1542
 SCSI_OBJS := $(SCSI_OBJS) aha1542.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
 endif
 
 ifdef CONFIG_SCSI_AHA1740
 SCSI_OBJS := $(SCSI_OBJS) aha1740.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
 endif
 
 ifdef CONFIG_SCSI_FUTURE_DOMAIN
 SCSI_OBJS := $(SCSI_OBJS) fdomain.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
 endif
 
 ifdef CONFIG_SCSI_ULTRASTOR
 SCSI_OBJS := $(SCSI_OBJS) ultrastor.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
 endif
 
 ifdef CONFIG_SCSI_7000FASST
 SCSI_OBJS := $(SCSI_OBJS) wd7000.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
 endif
 
 ifdef CONFIG_SCSI_SEAGATE
 SCSI_OBJS := $(SCSI_OBJS) seagate.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
 else
 ifdef CONFIG_SCSI_FD_88x
 SCSI_OBJS := $(SCSI_OBJS) seagate.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
 endif
 endif
 
 ifdef CONFIG_SCSI_DEBUG
 SCSI_OBJS := $(SCSI_OBJS) scsi_debug.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
 endif
 
 scsi.a: $(SCSI_OBJS)
@@ -79,10 +70,11 @@
 
 endif
 
-CFLAGS := $(CFLAGS) "-DMAX_SCSI_HOSTS=($(SCSI_HOSTS))"
-
 seagate.o: seagate.c
-	$(CC) -Wall -I$(KERNELHDRS) -x c++ -c seagate.c 
+	$(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c 
+
+constants.o: constants.c
+	$(CC) $(CFLAGS) -c constants.c
 
 clean:
 	rm -f core *.o *.a *.s
diff --git a/kernel/blk_drv/scsi/aha1542.c b/kernel/blk_drv/scsi/aha1542.c
index 4141c81..dc9ffe5 100644
--- a/kernel/blk_drv/scsi/aha1542.c
+++ b/kernel/blk_drv/scsi/aha1542.c
@@ -376,7 +376,7 @@
     
     if(*cmd == REQUEST_SENSE){
 #ifndef DEBUG
-      if (bufflen != 16) {
+      if (bufflen != sizeof(SCpnt->sense_buffer)) {
 	printk("Wrong buffer length supplied for request sense (%d)\n",bufflen);
 	panic("aha1542.c");
       };
@@ -472,9 +472,11 @@
 	  panic("Foooooooood fight!");
 	};
 	any2scsi(cptr[i].dataptr, sgpnt[i].address);
+	if(((unsigned  int) sgpnt[i].address) & 0xff000000) goto baddma;
 	any2scsi(cptr[i].datalen, sgpnt[i].length);
       };
       any2scsi(ccb[mbo].datalen, SCpnt->use_sg * sizeof(struct chain));
+      if(((unsigned int) buff & 0xff000000)) goto baddma;
       any2scsi(ccb[mbo].dataptr, cptr);
 #ifdef DEBUG
       printk("cptr %x: ",cptr);
@@ -511,6 +513,8 @@
       printk("aha1542_queuecommand: done can't be NULL\n");
     
     return 0;
+ baddma:
+    panic("Buffer at address  > 16Mb used for 1542B");
 }
 
 static volatile int internal_done_flag = 0;
diff --git a/kernel/blk_drv/scsi/aha1740.c b/kernel/blk_drv/scsi/aha1740.c
index b5b11ce..ef86500 100644
--- a/kernel/blk_drv/scsi/aha1740.c
+++ b/kernel/blk_drv/scsi/aha1740.c
@@ -247,7 +247,7 @@
     
     if(*cmd == REQUEST_SENSE)
     {
-        if (bufflen != 16)
+        if (bufflen != sizeof(SCpnt->sense_buffer))
 	{
 	    printk("Wrong buffer length supplied for request sense (%d)\n",bufflen);
 	    panic("aha1740.c");
diff --git a/kernel/blk_drv/scsi/constants.c b/kernel/blk_drv/scsi/constants.c
new file mode 100644
index 0000000..031a06c
--- /dev/null
+++ b/kernel/blk_drv/scsi/constants.c
@@ -0,0 +1,460 @@
+/* 
+ * ASCII values for a number of symbolic constants, printing functions,
+ * etc.
+ */
+
+#include <linux/config.h>
+#ifdef CONFIG_SCSI
+#include "../blk.h"
+#include <linux/kernel.h>
+#include "scsi.h"
+static const char reserved[] = "RESERVED";
+static const char unknown[] = "UNKNOWN";
+static const char vendor[] = "VENDOR SPECIFIC";
+
+#define CONST_COMMAND 	0x01
+#define CONST_STATUS 	0x02
+#define CONST_SENSE 	0x04
+#define CONST_XSENSE 	0x08
+
+#ifdef CONFIG_SCSI_CONSTANTS
+#ifdef CONSTANTS
+#undef CONSTANTS
+#endif
+#define CONSTANTS (CONST_CMD | CONST_STATUS | CONST_SENSE | CONST_XSENSE)
+#endif
+
+#if (CONSTANTS & CONST_COMMAND)
+static const char * group_0_commands[] = {
+/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
+/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
+/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
+/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",  
+/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve",
+/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
+/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic", 
+/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
+};
+
+
+static const char *group_1_commands[] = {
+/* 20-22 */  unknown, unknown, unknown,
+/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)", 
+/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown, 
+/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal", 
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position", 
+/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Deffect Data", 
+/* 38-3c */ unknown, "Compare","Copy Verify", "Write Buffer", "Read Buffer", 
+/* 3d-39 */ unknown, "Read Long",  unknown,
+};
+
+
+static const char *group_2_commands[] = {
+/* 40-41 */ "Change Definition", unknown, 
+/* 42-48 */ unknown, unknown, unknown, unknown, unknown, unknown, unknown,
+/* 49-4f */ unknown, unknown, unknown, "Log Select", "Log Sense", unknown,
+/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)",
+/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown,
+/* 5c-5f */ unknown, unknown, unknown,
+};
+
+
+
+#define group(opcode) (((opcode) >> 5) & 7)
+
+#define RESERVED_GROUP  0
+#define VENDOR_GROUP 	1
+#define NOTEXT_GROUP	2
+
+static const char **commands[] = {
+group_0_commands, group_1_commands, group_2_commands, 
+(const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP, 
+(const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP, 
+(const char **) VENDOR_GROUP};
+
+
+static void print_opcode(int opcode) {
+  char **table = commands[ group(opcode) ];
+  switch ((int) table) {
+  case RESERVED_GROUP:
+  	printk("%s(0x%02x) ", reserved, opcode); 
+  	break;
+  case NOTEXT_GROUP:
+  	printk("%s(0x%02x) ", unknown, opcode); 
+  	break;
+  case VENDOR_GROUP:
+  	printk("%s(0x%02x) ", vendor, opcode); 
+  	break;
+  default:
+  	printk("%s ",table[opcode & 0x31]);
+  }
+}
+#else /* CONST & CONST_COMMAND */
+static void print_opcode(int opcode) {
+  printk("0x%02x ", opcode);
+}
+#endif  
+
+void print_command (unsigned char *command) {
+  int i,s;
+  print_opcode(command[0]);
+  for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i) 
+  	printk("%02x ", command[i]);
+  printk("\n");
+}
+
+#if (CONSTANTS & CONST_STATUS)
+static const char * statuses[] = {
+/* 0-4 */ "Good", "Check Condition", "Condition Good", unknown, "Busy", 
+/* 5-9 */ unknown, unknown, unknown, "Intermediate Good", unknown, 
+/* a-d */ "Interemediate Good", unknown, "Reservation Conflict", unknown,
+/* e-f */ unknown, unknown,
+};
+#endif
+
+void print_status (int status) {
+  status = (status >> 1) & 0xf;
+#if (CONSTANTS & CONST_STATUS)
+  printk("%s ",statuses[status]);
+#else
+  printk("0x%0x ", status); 
+#endif 
+}
+
+#if (CONSTANTS & CONST_MSG)
+static const char * msgs[] = {
+};
+void print_msg (int msg) {
+  printk("%s ", msgs[msg]);
+}
+#else
+void print_msg (int msg) {
+  printk("0x%02x ", msg);    
+}
+#endif
+
+#if (CONSTANTS & CONST_XSENSE)
+#define D 0x001  /* DIRECT ACCESS DEVICE (disk) */
+#define T 0x002  /* SEQUENTIAL ACCESS DEVICE (tape) */
+#define L 0x004  /* PRINTER DEVICE */
+#define P 0x008  /* PROCESSOR DEVICE */
+#define W 0x010  /* WRITE ONCE READ MULTIPLE DEVICE */
+#define R 0x020  /* READ ONLY (CD-ROM) DEVICE */
+#define S 0x040  /* SCANNER DEVICE */
+#define O 0x080  /* OPTICAL MEMORY DEVICE */
+#define M 0x100  /* MEDIA CHANGER DEVICE */
+#define C 0x200  /* COMMUNICATION DEVICE */
+
+struct error_info{
+  unsigned char code1, code2;
+  unsigned short int devices;
+  char * text;
+};
+
+struct error_info2{
+  unsigned char code1, code2_min, code2_max;
+  unsigned short int devices;
+  char * text;
+};
+
+static struct error_info2 additional2[] =
+{
+  {0x40,0x00,0x7f,D,"Ram failure (%x)"},
+  {0x40,0x80,0xff,D|T|L|P|W|R|S|O|M|C,"Diagnostic failure on component (%x)"},
+  {0x41,0x00,0xff,D,"Data path failure (%x)"},
+  {0x42,0x00,0xff,D,"Power-on or self-test failure (%x)"},
+  {0, 0, 0, 0, NULL}
+};
+
+static struct error_info additional[] =
+{
+  {0x00,0x01,T,"Filemark detected"},
+  {0x00,0x02,T|S,"End-of-partition/medium detected"},
+  {0x00,0x03,T,"Setmark detected"},
+  {0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
+  {0x00,0x05,T|S,"End-of-data detected"},
+  {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
+  {0x00,0x11,R,"Audio play operation in progress"},
+  {0x00,0x12,R,"Audio play operation paused"},
+  {0x00,0x13,R,"Audio play operation successfully completed"},
+  {0x00,0x14,R,"Audio play operation stopped due to error"},
+  {0x00,0x15,R,"No current audio status to return"},
+  {0x01,0x00,D|W|O,"No index/sector signal"},
+  {0x02,0x00,D|W|R|O|M,"No seek complete"},
+  {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
+  {0x03,0x01,T,"No write current"},
+  {0x03,0x02,T,"Excessive write errors"},
+  {0x04,0x00,D|T|L|P|W|R|S|O|M|C,
+     "Logical unit not ready, cause not reportable"},
+  {0x04,0x01,D|T|L|P|W|R|S|O|M|C,
+     "Logical unit is in process of becoming ready"},
+  {0x04,0x02,D|T|L|P|W|R|S|O|M|C,
+     "Logical unit not ready, initializing command required"},
+  {0x04,0x03,D|T|L|P|W|R|S|O|M|C,
+     "Logical unit not ready, manual intervention required"},
+  {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
+  {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
+  {0x06,0x00,D|W|R|O|M,"No reference position found"},
+  {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
+  {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
+  {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
+  {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
+  {0x09,0x00,D|T|W|R|O,"Track following error"},
+  {0x09,0x01,W|R|O,"Tracking servo failure"},
+  {0x09,0x02,W|R|O,"Focus servo failure"},
+  {0x09,0x03,W|R|O,"Spindle servo failure"},
+  {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
+  {0x0C,0x00,T|S,"Write error"},
+  {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
+  {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
+  {0x10,0x00,D|W|O,"Id crc or ecc error"},
+  {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
+  {0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
+  {0x11,0x02,D|T|W|S|O,"Error too long to correct"},
+  {0x11,0x03,D|T|W|S|O,"Multiple read errors"},
+  {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
+  {0x11,0x05,W|R|O,"L-ec uncorrectable error"},
+  {0x11,0x06,W|R|O,"Circ unrecovered error"},
+  {0x11,0x07,W|O,"Data resychronization error"},
+  {0x11,0x08,T,"Incomplete block read"},
+  {0x11,0x09,T,"No gap found"},
+  {0x11,0x0A,D|T|O,"Miscorrected error"},
+  {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
+  {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
+  {0x12,0x00,D|W|O,"Address mark not found for id field"},
+  {0x13,0x00,D|W|O,"Address mark not found for data field"},
+  {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
+  {0x14,0x01,D|T|W|R|O,"Record not found"},
+  {0x14,0x02,T,"Filemark or setmark not found"},
+  {0x14,0x03,T,"End-of-data not found"},
+  {0x14,0x04,T,"Block sequence error"},
+  {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
+  {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
+  {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
+  {0x16,0x00,D|W|O,"Data synchronization mark error"},
+  {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
+  {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
+  {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
+  {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
+  {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
+  {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
+  {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
+  {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
+  {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
+  {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
+  {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
+  {0x18,0x03,R,"Recovered data with circ"},
+  {0x18,0x04,R,"Recovered data with lec"},
+  {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
+  {0x19,0x00,D|O,"Defect list error"},
+  {0x19,0x01,D|O,"Defect list not available"},
+  {0x19,0x02,D|O,"Defect list error in primary list"},
+  {0x19,0x03,D|O,"Defect list error in grown list"},
+  {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
+  {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
+  {0x1C,0x00,D|O,"Defect list not found"},
+  {0x1C,0x01,D|O,"Primary defect list not found"},
+  {0x1C,0x02,D|O,"Grown defect list not found"},
+  {0x1D,0x00,D|W|O,"Miscompare during verify operation"},
+  {0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
+  {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
+  {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
+  {0x21,0x01,M,"Invalid element address"},
+  {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
+  {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
+  {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
+  {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
+  {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
+  {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
+  {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
+  {0x27,0x00,D|T|W|O,"Write protected"},
+  {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
+  {0x28,0x01,M,"Import or export element accessed"},
+  {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
+  {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
+  {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
+  {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
+  {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
+  {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
+  {0x2C,0x01,S,"Too many windows specified"},
+  {0x2C,0x02,S,"Invalid combination of windows specified"},
+  {0x2D,0x00,T,"Overwrite error on update in place"},
+  {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
+  {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
+  {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
+  {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
+  {0x30,0x03,D|T,"Cleaning cartridge installed"},
+  {0x31,0x00,D|T|W|O,"Medium format corrupted"},
+  {0x31,0x01,D|L|O,"Format command failed"},
+  {0x32,0x00,D|W|O,"No defect spare location available"},
+  {0x32,0x01,D|W|O,"Defect list update failure"},
+  {0x33,0x00,T,"Tape length error"},
+  {0x36,0x00,L,"Ribbon, ink, or toner failure"},
+  {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
+  {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
+  {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
+  {0x3B,0x00,T|L,"Sequential positioning error"},
+  {0x3B,0x01,T,"Tape position error at beginning-of-medium"},
+  {0x3B,0x02,T,"Tape position error at end-of-medium"},
+  {0x3B,0x03,L,"Tape or electronic vertical forms unit not ready"},
+  {0x3B,0x04,L,"Slew failure"},
+  {0x3B,0x05,L,"Paper jam"},
+  {0x3B,0x06,L,"Failed to sense top-of-form"},
+  {0x3B,0x07,L,"Failed to sense bottom-of-form"},
+  {0x3B,0x08,T,"Reposition error"},
+  {0x3B,0x09,S,"Read past end of medium"},
+  {0x3B,0x0A,S,"Read past beginning of medium"},
+  {0x3B,0x0B,S,"Position past end of medium"},
+  {0x3B,0x0C,S,"Position past beginning of medium"},
+  {0x3B,0x0D,M,"Medium destination element full"},
+  {0x3B,0x0E,M,"Medium source element empty"},
+  {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
+  {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
+  {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
+  {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
+  {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
+  {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
+  {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
+  {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
+  {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
+  {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
+  {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
+  {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
+  {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
+  {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
+  {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
+  {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
+  {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
+  {0x50,0x00,T,"Write append error"},
+  {0x50,0x01,T,"Write append position error"},
+  {0x50,0x02,T,"Position error related to timing"},
+  {0x51,0x00,T|O,"Erase failure"},
+  {0x52,0x00,T,"Cartridge fault"},
+  {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
+  {0x53,0x01,T,"Unload tape failure"},
+  {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
+  {0x54,0x00,P,"Scsi to host system interface failure"},
+  {0x55,0x00,P,"System resource failure"},
+  {0x57,0x00,R,"Unable to recover table-of-contents"},
+  {0x58,0x00,O,"Generation does not exist"},
+  {0x59,0x00,O,"Updated block read"},
+  {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
+  {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
+  {0x5A,0x02,D|T|W|O,"Operator selected write protect"},
+  {0x5A,0x03,D|T|W|O,"Operator selected write permit"},
+  {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
+  {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
+  {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
+  {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
+  {0x5C,0x00,D|O,"Rpl status change"},
+  {0x5C,0x01,D|O,"Spindles synchronized"},
+  {0x5C,0x02,D|O,"Spindles not synchronized"},
+  {0x60,0x00,S,"Lamp failure"},
+  {0x61,0x00,S,"Video acquisition error"},
+  {0x61,0x01,S,"Unable to acquire video"},
+  {0x61,0x02,S,"Out of focus"},
+  {0x62,0x00,S,"Scan head positioning error"},
+  {0x63,0x00,R,"End of user area encountered on this track"},
+  {0x64,0x00,R,"Illegal mode for this track"},
+  {0, 0, 0, NULL}
+};
+#endif
+
+#if (CONSTANTS & CONST_SENSE)
+static char *snstext[] = {
+	"None","Recovered Error","Not Ready","Medium Error","Hardware Erro