diff -urNwbB linux/Makefile linux.arm/Makefile
--- linux/Makefile	Sun Mar  3 11:42:06 1996
+++ linux.arm/Makefile	Sun Feb 11 13:47:34 1996
@@ -2,7 +2,7 @@
 PATCHLEVEL = 3
 SUBLEVEL = 35
 
-ARCH = i386
+ARCH = arm
 
 .EXPORT_ALL_VARIABLES:
 
@@ -86,13 +86,19 @@
 # Include the make variables (CC, etc...)
 #
 
+# Modified 11/02/96 by Russell King
+#	- Doesn't include the drivers subdirectory as standard.
+#	- Modified the 'nm' line in vmlinux so that we remove local and absolute symbols.
+
 ARCHIVES	=kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o net/network.a
 FILESYSTEMS	=fs/filesystems.a
-DRIVERS		=drivers/block/block.a \
+DRIVERS		=
+LIBS		=$(TOPDIR)/lib/lib.a
+SUBDIRS		=kernel mm fs net ipc lib
+
+DRIVERS :=drivers/block/block.a \
 		 drivers/char/char.a \
 		 drivers/net/net.a
-LIBS		=$(TOPDIR)/lib/lib.a
-SUBDIRS		=kernel drivers mm fs net ipc lib
 
 ifdef CONFIG_SCSI
 DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
@@ -108,6 +114,10 @@
 
 include arch/$(ARCH)/Makefile
 
+ifndef CONFIG_ARM
+SUBDIRS := drivers $(SUBDIRS)
+endif
+
 .S.s:
 	$(CC) -D__ASSEMBLY__ -traditional -E -o $*.s $<
 .S.o:
@@ -125,7 +135,7 @@
 		$(FILESYSTEMS) \
 		$(DRIVERS) \
 		$(LIBS) -o vmlinux
-	$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | sort > System.map
+	$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)\|\( A \)\|\( t L\)' | sort > System.map
 
 symlinks:
 	rm -f include/asm
diff -urNwbB linux/arch/arm/Makefile linux.arm/arch/arm/Makefile
--- linux/arch/arm/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/Makefile	Sat Feb 24 13:08:03 1996
@@ -0,0 +1,70 @@
+#
+# arch/arm/Makefile
+#
+# This file is included by the global makefile so that you can add your own
+# architecture-specific flags and dependencies. Remember to do have actions
+# for "archclean" and "archdep" for cleaning up and making dependencies for
+# this architecture
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1995 by Russell King
+#
+
+#
+# Set these to indicate how to link it..
+# -qmagic (we need to remove the 32 byte header for bootup purposes)
+#
+
+SUBARCH		 = -m3
+
+CPP		 = $(CC) -E $(SUBARCH)
+CFLAGS		:= $(CFLAGS:-fomit-frame-pointer=) $(SUBARCH)
+LINKFLAGS	 = -Ttext 0x01800000
+ZLINKFLAGS	 = -N -Ttext 0x01800000
+
+HEAD		:= arch/arm/kernel/head.o
+SUBDIRS		:= arch/arm/drivers arch/arm/kernel arch/arm/mm arch/arm/lib $(SUBDIRS)
+ARCHIVES	:= arch/arm/kernel/kernel.o arch/arm/mm/mm.o arch/arm/lib/lib.o $(ARCHIVES)
+LIBS		:= $(LIBS) `gcc --print-libgcc-file-name`
+
+DRIVERS := arch/arm/drivers/block/block.a arch/arm/drivers/char/char.a \
+           arch/arm/drivers/net/net.a
+
+ifdef CONFIG_SOUND
+DRIVERS := $(DRIVERS) arch/arm/drivers/sound/sound.a
+endif
+
+ifdef CONFIG_SCSI
+DRIVERS := $(DRIVERS) arch/arm/drivers/scsi/scsi.a
+endif
+
+
+arch/arm/kernel: dummy
+	$(MAKE) linuxsubdirs SUBDIRS=arch/arm/kernel
+
+arch/arm/mm: dummy
+	$(MAKE) linuxsubdirs SUBDIRS=arch/arm/mm
+
+MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
+
+install:
+	@$(MAKEBOOT) install
+
+zinstall:
+	@$(MAKEBOOT) zinstall
+
+zImage:
+	@$(MAKEBOOT) zImage
+
+archclean:
+	@$(MAKEBOOT) clean
+	@$(MAKE) -C arch/$(ARCH)/drivers/block LINKCLEAN
+	@$(MAKE) -C arch/$(ARCH)/drivers/char LINKCLEAN
+	@$(MAKE) -C arch/$(ARCH)/drivers/net LINKCLEAN
+	@$(MAKE) -C arch/$(ARCH)/drivers/scsi LINKCLEAN
+
+archdep:
+	@$(MAKEBOOT) dep
diff -urNwbB linux/arch/arm/boot/Makefile linux.arm/arch/arm/boot/Makefile
--- linux/arch/arm/boot/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/Makefile	Sun Feb 11 21:59:54 1996
@@ -0,0 +1,47 @@
+#
+# arch/i386/boot/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1994 by Linus Torvalds
+#
+
+Image:	$(CONFIGURE) tools/build $(TOPDIR)/vmlinux
+	tools/build $(TOPDIR)/vmlinux > Image
+	sync
+
+zImage:	$(CONFIGURE) tools/build compressed/vmlinux
+	tools/build compressed/vmlinux > zImage
+
+compressed/vmlinux: $(TOPDIR)/vmlinux dep
+	@$(MAKE) -C compressed vmlinux
+
+install: $(CONFIGURE) Image
+	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
+
+zinstall: $(CONFIGURE) zImage
+	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
+
+tools/build: tools/build.c
+	$(HOSTCC) $(CFLAGS) -o $@ $< -I$(TOPDIR)/include
+
+clean:
+	rm -f Image zImage tools/build
+	@$(MAKE) -C compressed clean
+
+dep:
+
+#zdisk: zImage
+#	dd bs=8192 if=zImage of=/dev/fd0
+
+#zlilo: $(CONFIGURE) zImage
+#	if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
+#	if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi
+#	cat zImage > $(INSTALL_PATH)/vmlinuz
+#	cp $(TOPDIR)/System.map $(INSTALL_PATH)/
+#	if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
+
+#install: $(CONFIGURE) zImage
+#	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
diff -urNwbB linux/arch/arm/boot/compressed/Makefile linux.arm/arch/arm/boot/compressed/Makefile
--- linux/arch/arm/boot/compressed/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/Makefile	Sun Mar  3 13:35:37 1996
@@ -0,0 +1,56 @@
+#
+# linux/arch/arm/boot/compressed/Makefile
+#
+# create a compressed vmlinux image from the original vmlinux
+#
+
+CFLAGS = -O2 -DSTDC_HEADERS
+LOADADDR = 0x01800000
+RELADDR = 0x01960000
+
+SYSTEM = $(TOPDIR)/vmlinux
+DECOMP_OBJS = inflate.o unzip.o misc.o ../../lib/ll_char_wr.o
+
+
+all:		vmlinux
+
+vmlinux:	head.o piggy.o
+		$(LD) -Ttext=$(LOADADDR) -o vmlinux head.o piggy.o
+
+head.o: 	head.S
+		$(CC) -traditional -DRELADDR=$(RELADDR) -c head.S
+
+piggy.o:	decomphead.o decompress.a compressed.o
+		$(LD) -s -Ttext=$(RELADDR) -o a.out decomphead.o compressed.o decompress.a
+		strip a.out
+		./piggyback < a.out > piggy.o
+		rm -f a.out
+
+compressed.o:	$(SYSTEM) xtract piggyback
+		./xtract $(SYSTEM) | gzip -9 | ./piggyback > compressed.o
+
+
+# rules for decompression part of kernel
+
+decompress.a:	$(DECOMP_OBJS)
+		$(AR) $(ARFLAGS) decompress.a $(DECOMP_OBJS)
+
+decomphead.o:	decomphead.S
+		$(CC) -traditional -DLOADADDR=$(LOADADDR) -c decomphead.S
+
+../../lib/ll_char_wr.o:
+		make -C ../../lib ll_char_wr.o
+
+
+# rules for extracting & piggybacking the kernel
+
+xtract: 	xtract.c
+		$(HOSTCC) $(CFLAGS) -o xtract xtract.c
+
+piggyback:	piggyback.c
+		$(HOSTCC) $(CFLAGS) -o piggyback piggyback.c
+
+
+clean:
+		rm -f xtract piggyback vmlinux decompress.a a.out
+
diff -urNwbB linux/arch/arm/boot/compressed/crypt.h linux.arm/arch/arm/boot/compressed/crypt.h
--- linux/arch/arm/boot/compressed/crypt.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/crypt.h	Sun Feb 11 09:32:10 1996
@@ -0,0 +1,12 @@
+/* crypt.h (dummy version) -- do not perform encryption
+ * Hardly worth copyrighting :-)
+ */
+
+#ifdef CRYPT
+#  undef CRYPT      /* dummy version */
+#endif
+
+#define RAND_HEAD_LEN  12  /* length of encryption random header */
+
+#define zencode
+#define zdecode
diff -urNwbB linux/arch/arm/boot/compressed/decomphead.S linux.arm/arch/arm/boot/compressed/decomphead.S
--- linux/arch/arm/boot/compressed/decomphead.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/decomphead.S	Sun Feb 11 09:32:10 1996
@@ -0,0 +1,25 @@
+/*
+ *  linux/arch/arm/boot/decomphead.S
+ */
+
+.text
+	.global	__stext
+__stext:
+	b	start
+LC0:
+	.word	__edata
+	.word	__end
+	.word	LOADADDR + 0x23
+	.word	__stack_start+16384
+start:
+	adr	r1, LC0
+	ldmia	r1, {r2, r3, r4, sp}
+	mov	r1, #0
+clrzlp:	str	r1, [r2], #4
+	cmp	r2, r3
+	blt	clrzlp
+	bic	r0, r4, #3
+	bl	_decompress_kernel
+	mov	r0, #0
+	mov	pc, r4
+
diff -urNwbB linux/arch/arm/boot/compressed/gzip.h linux.arm/arch/arm/boot/compressed/gzip.h
--- linux/arch/arm/boot/compressed/gzip.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/gzip.h	Sun Feb 11 09:32:10 1996
@@ -0,0 +1,284 @@
+/* gzip.h -- common declarations for all gzip modules
+ * Copyright (C) 1992-1993 Jean-loup Gailly.
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+#if defined(__STDC__) || defined(PROTO)
+#  define OF(args)  args
+#else
+#  define OF(args)  ()
+#endif
+
+#ifdef __STDC__
+   typedef void *voidp;
+#else
+   typedef char *voidp;
+#endif
+
+/* I don't like nested includes, but the string functions are used too often */
+#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
+#  include <string.h>
+#  define memzero(s, n)     memset ((s), 0, (n))
+#else
+#  include <strings.h>
+#  define strchr            index 
+#  define strrchr           rindex
+#  define memcpy(d, s, n)   bcopy((s), (d), (n)) 
+#  define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) 
+#  define memzero(s, n)     bzero((s), (n))
+#endif
+
+#if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H)
+#  include <memory.h>
+#endif
+
+#ifndef RETSIGTYPE
+#  define RETSIGTYPE void
+#endif
+
+#define local static
+
+typedef unsigned char  uch;
+typedef unsigned short ush;
+typedef unsigned long  ulg;
+
+/* Return codes from gzip */
+#define OK      0
+#define ERROR   1
+#define WARNING 2
+
+/* Compression methods (see algorithm.doc) */
+#define STORED     0
+#define COMPRESSED 1
+#define PACKED     2
+/* methods 3 to 7 reserved */
+#define DEFLATED   8
+extern int method;         /* compression method */
+
+/* To save memory for 16 bit systems, some arrays are overlayed between
+ * the various modules:
+ * deflate:  prev+head   window      d_buf  l_buf  outbuf
+ * unlzw:    tab_prefix  tab_suffix  stack  inbuf  outbuf
+ * inflate:              window             inbuf
+ * unpack:               window             inbuf
+ * For compression, input is done in window[]. For decompression, output
+ * is done in window except for unlzw.
+ */
+
+#ifndef	INBUFSIZ
+#  define INBUFSIZ  0x8000  /* input buffer size */
+#endif
+#define INBUF_EXTRA  64     /* required by unlzw() */
+
+#ifndef	OUTBUFSIZ
+#  define OUTBUFSIZ  16384  /* output buffer size */
+#endif
+#define OUTBUF_EXTRA 2048   /* required by unlzw() */
+
+#define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */
+
+#ifdef DYN_ALLOC
+#  define EXTERN(type, array)  extern type * near array
+#  define DECLARE(type, array, size)  type * near array
+#  define ALLOC(type, array, size) { \
+      array = (type*)fcalloc((unsigned)(((size)+1L)/2), 2*sizeof(type)); \
+      if (array == NULL) error("insufficient memory"); \
+   }
+#  define FREE(array) {if (array != NULL) fcfree(array), array=NULL;}
+#else
+#  define EXTERN(type, array)  extern type array[]
+#  define DECLARE(type, array, size)  type array[size]
+#  define ALLOC(type, array, size)
+#  define FREE(array)
+#endif
+
+EXTERN(uch, inbuf);          /* input buffer */
+EXTERN(uch, outbuf);         /* output buffer */
+EXTERN(ush, d_buf);          /* buffer for distances, see trees.c */
+EXTERN(uch, window);         /* Sliding window and suffix table (unlzw) */
+#define tab_suffix window
+#ifndef MAXSEG_64K
+#  define tab_prefix prev    /* hash link (see deflate.c) */
+#  define head (prev+WSIZE)  /* hash head (see deflate.c) */
+   EXTERN(ush, tab_prefix);  /* prefix code (see unlzw.c) */
+#else
+#  define tab_prefix0 prev
+#  define head tab_prefix1
+   EXTERN(ush, tab_prefix0); /* prefix for even codes */
+   EXTERN(ush, tab_prefix1); /* prefix for odd  codes */
+#endif
+
+extern unsigned insize; /* valid bytes in inbuf */
+extern unsigned inptr;  /* index of next byte to be processed in inbuf */
+extern unsigned outcnt; /* bytes in output buffer */
+
+extern long bytes_in;   /* number of input bytes */
+extern long bytes_out;  /* number of output bytes */
+extern long overhead;   /* number of bytes in gzip header */
+
+#define isize bytes_in
+/* for compatibility with old zip sources (to be cleaned) */
+
+extern int  ifd;        /* input file descriptor */
+extern int  ofd;        /* output file descriptor */
+extern char ifname[];   /* input filename or "stdin" */
+extern char ofname[];   /* output filename or "stdout" */
+
+extern ulg time_stamp;  /* original time stamp (modification time) */
+extern long ifile_size; /* input file size, -1 for devices (debug only) */
+
+extern int exit_code;   /* program exit code */
+
+typedef int file_t;     /* Do not use stdio */
+#define NO_FILE  (-1)   /* in memory compression */
+
+
+#define	GZIP_MAGIC     "\037\213" /* Magic header for gzip files, 1F 8B */
+#define	OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */
+#define	PKZIP_MAGIC  "PK\003\004" /* Magic header for pkzip files */
+#define	PACK_MAGIC     "\037\036" /* Magic header for packed files */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
+#define RESERVED     0xC0 /* bit 6,7:   reserved */
+
+/* internal file attribute */
+#define UNKNOWN (-1)
+#define BINARY  0
+#define ASCII   1
+
+#ifndef WSIZE
+#  define WSIZE 0x8000     /* window size--must be a power of two, and */
+#endif                     /*  at least 32K for zip's deflate method */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST  (WSIZE-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+extern int decrypt;        /* flag to turn on decryption */
+extern int save_orig_name; /* set if original name must be saved */
+extern int verbose;        /* be verbose (-v) */
+extern int level;          /* compression level */
+extern int test;           /* check .z file integrity */
+extern int to_stdout;      /* output to stdout (-c) */
+
+#define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())
+
+/* put_byte is used for the compressed output, put_char for the
+ * uncompressed output. However unlzw() uses window for its
+ * suffix table instead of its output buffer, so it does not use put_char.
+ * (to be cleaned up).
+ */
+#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\
+   flush_outbuf();}
+#define put_char(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\
+   flush_window();}
+
+/* Output a 16 bit value, lsb first */
+#define put_short(w) \
+{ if (outcnt < OUTBUFSIZ-2) { \
+    outbuf[outcnt++] = (uch) ((w) & 0xff); \
+    outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \
+  } else { \
+    put_byte((uch)((w) & 0xff)); \
+    put_byte((uch)((ush)(w) >> 8)); \
+  } \
+}
+
+/* Output a 32 bit value to the bit stream, lsb first */
+#define put_long(n) { \
+    put_short((n) & 0xffff); \
+    put_short(((ulg)(n)) >> 16); \
+}
+
+#define seekable()    0  /* force sequential output */
+#define translate_eol 0  /* no option -a yet */
+
+#define tolow(c)  (isupper(c) ? (c)-'A'+'a' : (c))    /* force to lower case */
+
+/* Macros for getting two-byte and four-byte header values */
+#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8))
+#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16))
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  define Assert(cond,msg) {if(!(cond)) error(msg);}
+#  define Trace(x) fprintf x
+#  define Tracev(x) {if (verbose) fprintf x ;}
+#  define Tracevv(x) {if (verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+	/* in zip.c: */
+extern void zip    OF((int in, int out));
+extern int file_read  OF((char *buf,  unsigned size));
+
+	/* in unzip.c */
+extern void unzip        OF((int in, int out));
+extern int check_zipfile OF((int in));
+
+	/* in unpack.c */
+extern void unpack        OF((int in, int out));
+
+	/* in gzip.c */
+RETSIGTYPE abort_gzip   OF((void));
+
+        /* in deflate.c */
+void lm_init OF((int pack_level, ush *flags));
+ulg  deflate OF((void));
+
+        /* in trees.c */
+void ct_init     OF((ush *attr, int *method));
+int  ct_tally    OF((int dist, int lc));
+ulg  flush_block OF((char *buf, ulg stored_len, int eof));
+
+        /* in bits.c */
+void     bi_init    OF((file_t zipfile));
+void     send_bits  OF((int value, int length));
+unsigned bi_reverse OF((unsigned value, int length));
+void     bi_windup  OF((void));
+void     copy_block OF((char *buf, unsigned len, int header));
+extern   int (*read_buf) OF((char *buf, unsigned size));
+
+	/* in util.c: */
+extern ulg  updcrc        OF((uch *s, unsigned n));
+extern void clear_bufs    OF((void));
+extern int  fill_inbuf    OF((void));
+extern void flush_outbuf  OF((void));
+extern void flush_window  OF((void));
+extern char *strlwr       OF((char *s));
+extern char *basename     OF((char *fname));
+extern char *add_envopt   OF((int *argcp, char ***argvp, char *env));
+extern void error         OF((char *m));
+extern void warn          OF((char *a, char *b));
+extern void read_error    OF((void));
+extern void write_error   OF((void));
+extern void display_ratio OF((long num, long den));
+extern voidp xmalloc      OF((unsigned int size));
+
+	/* in inflate.c */
+extern int inflate OF((void));
diff -urNwbB linux/arch/arm/boot/compressed/head.S linux.arm/arch/arm/boot/compressed/head.S
--- linux/arch/arm/boot/compressed/head.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/head.S	Sun Feb 11 09:32:10 1996
@@ -0,0 +1,36 @@
+		.text
+		.align
+
+@ Entry point
+@ r0 = kernel info page
+@ r1 = kernel length (length of this code+data)
+
+		.global __stext
+__stext:
+		b	start
+LC0:
+		.word	_input_data
+		.word	_input_end
+		.word	RELADDR
+start:
+		teq	r0, #0
+		beq	newparams
+		mov	r4,     #0x02000000
+		add	r4, r4, #0x0007C000	@ = 0x0207C000 [new param location]
+		mov	r3,     #0x4000
+		sub	r3, r3, #4
+lp2:		ldmia	r0!, {r5, r6, r7, r8, r9, r10, r11, r12}
+		stmia	r4!, {r5, r6, r7, r8, r9, r10, r11, r12}
+		subs	r3, r3, #32
+		bpl	lp2
+newparams:	adr	r3, LC0
+		ldmia	r3, {r2, r3, r4}
+		sub	r3, r3, r2
+		mov	r1, r4
+lp:		ldmia	r2!, {r5, r6, r7, r8, r9, r10, r11, r12}
+		stmia	r4!, {r5, r6, r7, r8, r9, r10, r11, r12}
+		subs	r3, r3, #32
+		bpl	lp
+		ldr	r1, [r1, #0x14]
+		mov	pc, r1
+
diff -urNwbB linux/arch/arm/boot/compressed/inflate.c linux.arm/arch/arm/boot/compressed/inflate.c
--- linux/arch/arm/boot/compressed/inflate.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/inflate.c	Sat Feb 24 09:36:43 1996
@@ -0,0 +1,810 @@
+#define DEBG(x)
+#define DEBG1(x)
+/* inflate.c -- Not copyrighted 1992 by Mark Adler
+   version c10p1, 10 January 1993 */
+
+/* 
+ * Adapted for booting Linux by Hannu Savolainen 1993
+ * based on gzip-1.0.3 
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: inflate.c,v 0.10 1993/02/04 13:21:06 jloup Exp $";
+#endif
+
+#include "gzip.h"
+#define slide window
+
+#if defined(STDC_HEADERS) || defined(HAVE_STDLIB_H)
+#  include <sys/types.h>
+#  include <stdlib.h>
+#endif
+
+struct huft {
+  uch e;                /* number of extra bits or operation */
+  uch b;                /* number of bits in this code or subcode */
+  union {
+    ush n;              /* literal, length base, or distance base */
+    struct huft *t;     /* pointer to next level of table */
+  } v;
+};
+
+
+/* Function prototypes */
+int huft_build OF((unsigned *, unsigned, unsigned, ush *, ush *,
+                   struct huft **, int *));
+int huft_free OF((struct huft *));
+int inflate_codes OF((struct huft *, struct huft *, int, int));
+int inflate_stored OF((void));
+int inflate_fixed OF((void));
+int inflate_dynamic OF((void));
+int inflate_block OF((int *));
+int inflate OF((void));
+
+
+#define wp outcnt
+#define flush_output(w) (wp=(w),flush_window())
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+static unsigned border[] = {    /* Order of the bit length code lengths */
+        16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+static ush cplens[] = {         /* Copy lengths for literal codes 257..285 */
+        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+        /* note: see note #13 above about the 258 in this list. */
+static ush cplext[] = {         /* Extra bits for literal codes 257..285 */
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */
+static ush cpdist[] = {         /* Copy offsets for distance codes 0..29 */
+        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+        8193, 12289, 16385, 24577};
+static ush cpdext[] = {         /* Extra bits for distance codes */
+        0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+        7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+        12, 12, 13, 13};
+
+
+ulg bb;                         /* bit buffer */
+unsigned bk;                    /* bits in bit buffer */
+
+ush mask_bits[] = {
+    0x0000,
+    0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+    0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+#ifdef CRYPT
+  uch cc;
+#  define NEXTBYTE() \
+     (decrypt ? (cc = get_byte(), zdecode(cc), cc) : get_byte())
+#else
+#  define NEXTBYTE()  (uch)get_byte()
+#endif
+#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<<k;k+=8;}}
+#define DUMPBITS(n) {b>>=(n);k-=(n);}
+
+int lbits = 9;          /* bits in base literal/length lookup table */
+int dbits = 6;          /* bits in base distance lookup table */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+#define BMAX 16         /* maximum bit length of any code (16 for explode) */
+#define N_MAX 288       /* maximum number of codes in any set */
+
+
+unsigned hufts;         /* track memory usage */
+
+
+int huft_build(b, n, s, d, e, t, m)
+unsigned *b;            /* code lengths in bits (all assumed <= BMAX) */
+unsigned n;             /* number of codes (assumed <= N_MAX) */
+unsigned s;             /* number of simple-valued codes (0..s-1) */
+ush *d;                 /* list of base values for non-simple codes */
+ush *e;                 /* list of extra bits for non-simple codes */
+struct huft **t;        /* result: starting table */
+int *m;                 /* maximum lookup bits, returns actual */
+/* Given a list of code lengths and a maximum table size, make a set of
+   tables to decode that set of codes.  Return zero on success, one if
+   the given code set is incomplete (the tables are still built in this
+   case), two if the input is invalid (all zero length codes or an
+   oversubscribed set of lengths), and three if not enough memory. */
+{
+  unsigned a;                   /* counter for codes of length k */
+  unsigned c[BMAX+1];           /* bit length count table */
+  unsigned f;                   /* i repeats in table every f entries */
+  int g;                        /* maximum code length */
+  int h;                        /* table level */
+  register unsigned i;          /* counter, current code */
+  register unsigned j;          /* counter */
+  register int k;               /* number of bits in current code */
+  int l;                        /* bits per table (returned in m) */
+  register unsigned *p;         /* pointer into c[], b[], or v[] */
+  register struct huft *q;      /* points to current table */
+  struct huft r;                /* table entry for structure assignment */
+  struct huft *u[BMAX];         /* table stack */
+  unsigned v[N_MAX];            /* values in order of bit length */
+  register int w;               /* bits before this table == (l * h) */
+  unsigned x[BMAX+1];           /* bit offsets, then code stack */
+  unsigned *xp;                 /* pointer into x */
+  int y;                        /* number of dummy codes added */
+  unsigned z;                   /* number of entries in current table */
+
+DEBG("huft1 ");
+
+  /* Generate counts for each bit length */
+  memzero(c, sizeof(c));
+  p = b;  i = n;
+  do {
+    c[*p++]++;                  /* assume all entries <= BMAX */
+  } while (--i);
+  if (c[0] == n)                /* null input--all zero length codes */
+  {
+    *t = (struct huft *)NULL;
+    *m = 0;
+    return 0;
+  }
+
+DEBG("huft2 ");
+
+  /* Find minimum and maximum length, bound *m by those */
+  l = *m;
+  for (j = 1; j <= BMAX; j++)
+    if (c[j])
+      break;
+  k = j;                        /* minimum code length */
+  if ((unsigned)l < j)
+    l = j;
+  for (i = BMAX; i; i--)
+    if (c[i])
+      break;
+  g = i;                        /* maximum code length */
+  if ((unsigned)l > i)
+    l = i;
+  *m = l;
+
+DEBG("huft3 ");
+
+  /* Adjust last length count to fill out codes, if needed */
+  for (y = 1 << j; j < i; j++, y <<= 1)
+    if ((y -= c[j]) < 0)
+      return 2;                 /* bad input: more codes than bits */
+  if ((y -= c[i]) < 0)
+    return 2;
+  c[i] += y;
+
+DEBG("huft4 ");
+
+  /* Generate starting offsets into the value table for each length */
+  x[1] = j = 0;
+  p = c + 1;  xp = x + 2;
+  while (--i) {                 /* note that i == g from above */
+    *xp++ = (j += *p++);
+  }
+
+DEBG("huft5 ");
+
+  /* Make a table of values in order of bit lengths */
+  p = b;  i = 0;
+  do {
+    if ((j = *p++) != 0)
+      v[x[j]++] = i;
+  } while (++i < n);
+
+DEBG("h6 ");
+
+  /* Generate the Huffman codes and for each, make the table entries */
+  x[0] = i = 0;                 /* first Huffman code is zero */
+  p = v;                        /* grab values in bit order */
+  h = -1;                       /* no tables yet--level -1 */
+  w = -l;                       /* bits decoded == (l * h) */
+  u[0] = (struct huft *)NULL;   /* just to keep compilers happy */
+  q = (struct huft *)NULL;      /* ditto */
+  z = 0;                        /* ditto */
+DEBG("h6a ");
+
+  /* go through the bit lengths (k already is bits in shortest code) */
+  for (; k <= g; k++)
+  {
+DEBG("h6b ");
+    a = c[k];
+    while (a--)
+    {
+DEBG("h6b1 ");
+      /* here i is the Huffman code of length k bits for value *p */
+      /* make tables up to required level */
+      while (k > w + l)
+      {
+DEBG1("1 ");
+        h++;
+        w += l;                 /* previous table always l bits */
+
+        /* compute minimum size table less than or equal to l bits */
+        z = (z = g - w) > (unsigned)l ? l : z;  /* upper limit on table size */
+        if ((f = 1 << (j = k - w)) > a + 1)     /* try a k-w bit table */
+        {                       /* too few codes for k-w bit table */
+DEBG1("2 ");
+          f -= a + 1;           /* deduct codes from patterns left */
+          xp = c + k;
+          while (++j < z)       /* try smaller tables up to z bits */
+          {
+            if ((f <<= 1) <= *++xp)
+              break;            /* enough codes to use up j bits */
+            f -= *xp;           /* else deduct codes from patterns */
+          }
+        }
+DEBG1("3 ");
+        z = 1 << j;             /* table entries for j-bit table */
+
+        /* allocate and link in new table */
+        q = (struct huft *)malloc((z + 1)*sizeof(struct huft));
+DEBG1("4 ");
+        hufts += z + 1;         /* track memory usage */
+        *t = q + 1;             /* link to list for huft_free() */
+        *(t = &(q->v.t)) = (struct huft *)NULL;
+        u[h] = ++q;             /* table starts after link */
+
+DEBG1("5 ");
+        /* connect to last table, if there is one */
+        if (h)
+        {
+          x[h] = i;             /* save pattern for backing up */
+          r.b = (uch)l;         /* bits to dump before this table */
+          r.e = (uch)(16 + j);  /* bits in this table */
+          r.v.t = q;            /* pointer to this table */
+          j = i >> (w - l);     /* (get around Turbo C bug) */
+          u[h-1][j] = r;        /* connect to last table */
+        }
+DEBG1("6 ");
+      }
+DEBG("h6c ");
+
+      /* set up table entry in r */
+      r.b = (uch)(k - w);
+      if (p >= v + n)
+        r.e = 99;               /* out of values--invalid code */
+      else if (*p < s)
+      {
+        r.e = (uch)(*p < 256 ? 16 : 15);    /* 256 is end-of-block code */
+        r.v.n = *p++;           /* simple code is just the value */
+      }
+      else
+      {
+        r.e = (uch)e[*p - s];   /* non-simple--look up in lists */
+        r.v.n = d[*p++ - s];
+      }
+DEBG("h6d ");
+
+      /* fill code-like entries with r */
+      f = 1 << (k - w);
+      for (j = i >> w; j < z; j += f)
+        q[j] = r;
+
+      /* backwards increment the k-bit code i */
+      for (j = 1 << (k - 1); i & j; j >>= 1)
+        i ^= j;
+      i ^= j;
+
+      /* backup over finished tables */
+      while ((i & ((1 << w) - 1)) != x[h])
+      {
+        h--;                    /* don't need to update q */
+        w -= l;
+      }
+DEBG("h6e ");
+    }
+DEBG("h6f ");
+  }
+
+DEBG("huft7 ");
+
+  /* Return true (1) if we were given an incomplete table */
+  return y != 0 && g != 1;
+}
+
+
+
+int huft_free(t)
+struct huft *t;         /* table to free */
+/* Free the malloc'ed tables built by huft_build(), which makes a linked
+   list of the tables it made, with the links in a dummy first entry of
+   each table. */
+{
+  register struct huft *p, *q;
+
+
+  /* Go through linked list, freeing from the malloced (t[-1]) address. */
+  p = t;
+  while (p != (struct huft *)NULL)
+  {
+    q = (--p)->v.t;
+    free(p);
+    p = q;
+  } 
+  return 0;
+}
+
+
+int inflate_codes(tl, td, bl, bd)
+struct huft *tl, *td;   /* literal/length and distance decoder tables */
+int bl, bd;             /* number of bits decoded by tl[] and td[] */
+/* inflate (decompress) the codes in a deflated (compressed) block.
+   Return an error code or zero if it all goes ok. */
+{
+  register unsigned e;  /* table entry flag/number of extra bits */
+  unsigned n, d;        /* length and index for copy */
+  unsigned w;           /* current window position */
+  struct huft *t;       /* pointer to table entry */
+  unsigned ml, md;      /* masks for bl and bd bits */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+
+  /* make local copies of globals */
+  b = bb;                       /* initialize bit buffer */
+  k = bk;
+  w = wp;                       /* initialize window position */
+
+  /* inflate the coded data */
+  ml = mask_bits[bl];           /* precompute masks for speed */
+  md = mask_bits[bd];
+  for (;;)                      /* do until end of block */
+  {
+    NEEDBITS((unsigned)bl)
+    if ((e = (t = tl + ((unsigned)b & ml))->e) > 16)
+      do {
+        if (e == 99)
+          return 1;
+        DUMPBITS(t->b)
+        e -= 16;
+        NEEDBITS(e)
+      } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
+    DUMPBITS(t->b)
+    if (e == 16)                /* then it's a literal */
+    {
+      slide[w++] = (uch)t->v.n;
+      if (w == WSIZE)
+      {
+        flush_output(w);
+        w = 0;
+      }
+    }
+    else                        /* it's an EOB or a length */
+    {
+      /* exit if end of block */
+      if (e == 15)
+        break;
+
+      /* get length of block to copy */
+      NEEDBITS(e)
+      n = t->v.n + ((unsigned)b & mask_bits[e]);
+      DUMPBITS(e);
+
+      /* decode distance of block to copy */
+      NEEDBITS((unsigned)bd)
+      if ((e = (t = td + ((unsigned)b & md))->e) > 16)
+        do {
+          if (e == 99)
+            return 1;
+          DUMPBITS(t->b)
+          e -= 16;
+          NEEDBITS(e)
+        } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
+      DUMPBITS(t->b)
+      NEEDBITS(e)
+      d = w - t->v.n - ((unsigned)b & mask_bits[e]);
+      DUMPBITS(e)
+
+      /* do the copy */
+      do {
+        n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e);
+#if !defined(NOMEMCPY) && !defined(DEBUG)
+        if (w - d >= e)         /* (this test assumes unsigned comparison) */
+        {
+          memcpy(slide + w, slide + d, e);
+          w += e;
+          d += e;
+        }
+        else                      /* do it slow to avoid memcpy() overlap */
+#endif /* !NOMEMCPY */
+          do {
+            slide[w++] = slide[d++];
+          } while (--e);
+        if (w == WSIZE)
+        {
+          flush_output(w);
+          w = 0;
+        }
+      } while (n);
+    }
+  }
+
+
+  /* restore the globals from the locals */
+  wp = w;                       /* restore global window pointer */
+  bb = b;                       /* restore global bit buffer */
+  bk = k;
+
+  /* done */
+  return 0;
+}
+
+
+
+int inflate_stored()
+/* "decompress" an inflated type 0 (stored) block. */
+{
+  unsigned n;           /* number of bytes in block */
+  unsigned w;           /* current window position */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+DEBG("<stor");
+
+  /* make local copies of globals */
+  b = bb;                       /* initialize bit buffer */
+  k = bk;
+  w = wp;                       /* initialize window position */
+
+
+  /* go to byte boundary */
+  n = k & 7;
+  DUMPBITS(n);
+
+
+  /* get the length and its complement */
+  NEEDBITS(16)
+  n = ((unsigned)b & 0xffff);
+  DUMPBITS(16)
+  NEEDBITS(16)
+  if (n != (unsigned)((~b) & 0xffff))
+    return 1;                   /* error in compressed data */
+  DUMPBITS(16)
+
+
+  /* read and output the compressed data */
+  while (n--)
+  {
+    NEEDBITS(8)
+    slide[w++] = (uch)b;
+    if (w == WSIZE)
+    {
+      flush_output(w);
+      w = 0;
+    }
+    DUMPBITS(8)
+  }
+
+
+  /* restore the globals from the locals */
+  wp = w;                       /* restore global window pointer */
+  bb = b;                       /* restore global bit buffer */
+  bk = k;
+
+  DEBG(">");
+  return 0;
+}
+
+
+
+int inflate_fixed()
+/* decompress an inflated type 1 (fixed Huffman codes) block.  We should
+   either replace this with a custom decoder, or at least precompute the
+   Huffman tables. */
+{
+  int i;                /* temporary variable */
+  struct huft *tl;      /* literal/length code table */
+  struct huft *td;      /* distance code table */
+  int bl;               /* lookup bits for tl */
+  int bd;               /* lookup bits for td */
+  unsigned l[288];      /* length list for huft_build */
+
+DEBG("<fix");
+
+  /* set up literal table */
+  for (i = 0; i < 144; i++)
+    l[i] = 8;
+  for (; i < 256; i++)
+    l[i] = 9;
+  for (; i < 280; i++)
+    l[i] = 7;
+  for (; i < 288; i++)          /* make a complete, but wrong code set */
+    l[i] = 8;
+  bl = 7;
+  if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0)
+    return i;
+
+
+  /* set up distance table */
+  for (i = 0; i < 30; i++)      /* make an incomplete code set */
+    l[i] = 5;
+  bd = 5;
+  if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1)
+  {
+    huft_free(tl);
+
+    DEBG(">");
+    return i;
+  }
+
+
+  /* decompress until an end-of-block code */
+  if (inflate_codes(tl, td, bl, bd))
+    return 1;
+
+
+  /* free the decoding tables, return */
+  huft_free(tl);
+  huft_free(td);
+  return 0;
+}
+
+
+
+int inflate_dynamic()
+/* decompress an inflated type 2 (dynamic Huffman codes) block. */
+{
+  int i;                /* temporary variables */
+  unsigned j;
+  unsigned l;           /* last length */
+  unsigned m;           /* mask for bit lengths table */
+  unsigned n;           /* number of lengths to get */
+  struct huft *tl;      /* literal/length code table */
+  struct huft *td;      /* distance code table */
+  int bl;               /* lookup bits for tl */
+  int bd;               /* lookup bits for td */
+  unsigned nb;          /* number of bit length codes */
+  unsigned nl;          /* number of literal/length codes */
+  unsigned nd;          /* number of distance codes */
+#ifdef PKZIP_BUG_WORKAROUND
+  unsigned ll[288+32];  /* literal/length and distance code lengths */
+#else
+  unsigned ll[286+30];  /* literal/length and distance code lengths */
+#endif
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+DEBG("<dyn");
+
+  /* make local bit buffer */
+  b = bb;
+  k = bk;
+
+
+  /* read in table lengths */
+  NEEDBITS(5)
+  nl = 257 + ((unsigned)b & 0x1f);      /* number of literal/length codes */
+  DUMPBITS(5)
+  NEEDBITS(5)
+  nd = 1 + ((unsigned)b & 0x1f);        /* number of distance codes */
+  DUMPBITS(5)
+  NEEDBITS(4)
+  nb = 4 + ((unsigned)b & 0xf);         /* number of bit length codes */
+  DUMPBITS(4)
+#ifdef PKZIP_BUG_WORKAROUND
+  if (nl > 288 || nd > 32)
+#else
+  if (nl > 286 || nd > 30)
+#endif
+    return 1;                   /* bad lengths */
+
+DEBG("dyn1 ");
+
+  /* read in bit-length-code lengths */
+  for (j = 0; j < nb; j++)
+  {
+    NEEDBITS(3)
+    ll[border[j]] = (unsigned)b & 7;
+    DUMPBITS(3)
+  }
+  for (; j < 19; j++)
+    ll[border[j]] = 0;
+
+DEBG("dyn2 ");
+
+  /* build decoding table for trees--single level, 7 bit lookup */
+  bl = 7;
+  if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0)
+  {
+    if (i == 1)
+      huft_free(tl);
+    return i;                   /* incomplete code set */
+  }
+
+DEBG("dyn3 ");
+
+  /* read in literal and distance code lengths */
+  n = nl + nd;
+  m = mask_bits[bl];
+  i = l = 0;
+  while ((unsigned)i < n)
+  {
+    NEEDBITS((unsigned)bl)
+    j = (td = tl + ((unsigned)b & m))->b;
+    DUMPBITS(j)
+    j = td->v.n;
+    if (j < 16)                 /* length of code in bits (0..15) */
+      ll[i++] = l = j;          /* save last length in l */
+    else if (j == 16)           /* repeat last length 3 to 6 times */
+    {
+      NEEDBITS(2)
+      j = 3 + ((unsigned)b & 3);
+      DUMPBITS(2)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = l;
+    }
+    else if (j == 17)           /* 3 to 10 zero length codes */
+    {
+      NEEDBITS(3)
+      j = 3 + ((unsigned)b & 7);
+      DUMPBITS(3)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = 0;
+      l = 0;
+    }
+    else                        /* j == 18: 11 to 138 zero length codes */
+    {
+      NEEDBITS(7)
+      j = 11 + ((unsigned)b & 0x7f);
+      DUMPBITS(7)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = 0;
+      l = 0;
+    }
+  }
+
+DEBG("dyn4 ");
+
+  /* free decoding table for trees */
+  huft_free(tl);
+
+DEBG("dyn5 ");
+
+  /* restore the global bit buffer */
+  bb = b;
+  bk = k;
+
+DEBG("dyn5a ");
+
+  /* build the decoding tables for literal/length and distance codes */
+  bl = lbits;
+  if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0)
+  {
+DEBG("dyn5b ");
+    if (i == 1) {
+      error(" incomplete literal tree\n");
+      huft_free(tl);
+    }
+    return i;                   /* incomplete code set */
+  }
+DEBG("dyn5c ");
+  bd = dbits;
+  if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0)
+  {
+DEBG("dyn5d ");
+    if (i == 1) {
+      error(" incomplete distance tree\n");
+#ifdef PKZIP_BUG_WORKAROUND
+      i = 0;
+    }
+#else
+      huft_free(td);
+    }
+    huft_free(tl);
+    return i;                   /* incomplete code set */
+#endif
+  }
+
+DEBG("dyn6 ");
+
+  /* decompress until an end-of-block code */
+  if (inflate_codes(tl, td, bl, bd))
+    return 1;
+
+DEBG("dyn7 ");
+
+  /* free the decoding tables, return */
+  huft_free(tl);
+  huft_free(td);
+
+  DEBG(">");
+  return 0;
+}
+
+
+
+int inflate_block(e)
+int *e;                 /* last block flag */
+/* decompress an inflated block */
+{
+  unsigned t;           /* block type */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+  DEBG("<blk");
+
+  /* make local bit buffer */
+  b = bb;
+  k = bk;
+
+
+  /* read in last block bit */
+  NEEDBITS(1)
+  *e = (int)b & 1;
+  DUMPBITS(1)
+
+
+  /* read in block type */
+  NEEDBITS(2)
+  t = (unsigned)b & 3;
+  DUMPBITS(2)
+
+
+  /* restore the global bit buffer */
+  bb = b;
+  bk = k;
+
+  /* inflate that block type */
+  if (t == 2)
+    return inflate_dynamic();
+  if (t == 0)
+    return inflate_stored();
+  if (t == 1)
+    return inflate_fixed();
+
+  DEBG(">");
+
+  /* bad block type */
+  return 2;
+}
+
+
+
+int inflate()
+/* decompress an inflated entry */
+{
+  int e;                /* last block flag */
+  int r;                /* result code */
+  unsigned h;           /* maximum struct huft's malloc'ed */
+
+
+  /* initialize window, bit buffer */
+  wp = 0;
+  bk = 0;
+  bb = 0;
+
+
+  /* decompress until the last block */
+  h = 0;
+  do {
+    hufts = 0;
+    if ((r = inflate_block(&e)) != 0)
+      return r;
+    if (hufts > h)
+      h = hufts;
+  } while (!e);
+
+  /* Undo too much lookahead. The next read will be byte aligned so we
+   * can discard unused bits in the last meaningful byte.
+   */
+  while (bk >= 8) {
+    bk -= 8;
+    inptr--;
+  }
+
+  /* flush out slide */
+  flush_output(wp);
+
+
+  /* return success */
+#ifdef DEBUG
+  fprintf(stderr, "<%u> ", h);
+#endif /* DEBUG */
+  return 0;
+}
diff -urNwbB linux/arch/arm/boot/compressed/lzw.h linux.arm/arch/arm/boot/compressed/lzw.h
--- linux/arch/arm/boot/compressed/lzw.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/lzw.h	Sun Feb 11 09:32:11 1996
@@ -0,0 +1,42 @@
+/* lzw.h -- define the lzw functions.
+ * Copyright (C) 1992-1993 Jean-loup Gailly.
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+#if !defined(OF) && defined(lint)
+#  include "gzip.h"
+#endif
+
+#ifndef BITS
+#  define BITS 16
+#endif
+#define INIT_BITS 9              /* Initial number of bits per code */
+
+#define	LZW_MAGIC  "\037\235"   /* Magic header for lzw files, 1F 9D */
+
+#define BIT_MASK    0x1f /* Mask for 'number of compression bits' */
+/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free.
+ * It's a pity that old uncompress does not check bit 0x20. That makes
+ * extension of the format actually undesirable because old compress
+ * would just crash on the new format instead of giving a meaningful
+ * error message. It does check the number of bits, but it's more
+ * helpful to say "unsupported format, get a new version" than
+ * "can only handle 16 bits".
+ */
+
+#define BLOCK_MODE  0x80
+/* Block compression: if table is full and compression rate is dropping,
+ * clear the dictionary.
+ */
+
+#define LZW_RESERVED 0x60 /* reserved bits */
+
+#define	CLEAR  256       /* flush the dictionary */
+#define FIRST  (CLEAR+1) /* first free entry */
+
+extern int maxbits;      /* max bits per code for LZW */
+extern int block_mode;   /* block compress mode -C compatible with 2.0 */
+
+extern void lzw    OF((int in, int out));
+extern void unlzw  OF((int in, int out));
diff -urNwbB linux/arch/arm/boot/compressed/misc.c linux.arm/arch/arm/boot/compressed/misc.c
--- linux/arch/arm/boot/compressed/misc.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/misc.c	Sun Feb 11 09:32:12 1996
@@ -0,0 +1,534 @@
+/*
+ * misc.c
+ * 
+ * This is a collection of several routines from gzip-1.0.3 
+ * adapted for Linux.
+ *
+ * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
+ * puts by Nick Holloway 1993
+ */
+
+#include "gzip.h"
+#include "lzw.h"
+
+/*#include <asm/segment.h>*/
+
+/*
+ * These are set up by the setup-routine at boot-time:
+ */
+
+struct param_struct
+{
+	unsigned long page_size;
+	unsigned long nr_pages;
+	unsigned long ramdisk_size;
+	unsigned long mountrootrdonly;
+	unsigned long rootdev;
+	unsigned long video_num_cols;
+	unsigned long video_num_rows;
+	unsigned long video_x;
+	unsigned long video_y;
+	unsigned long memc_control_reg;
+	unsigned char sounddefault;
+	unsigned char adfsdrives;
+	unsigned char bytes_per_char_h;
+	unsigned char bytes_per_char_v;
+	unsigned long unused[256/4-11];
+	char paths[8][128];
+	char commandline[256];
+};
+
+static struct param_struct *params=(struct param_struct *)0x0207C000; 
+
+#define EOF -1
+
+DECLARE(uch, inbuf, INBUFSIZ);
+DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
+DECLARE(uch, window, WSIZE);
+
+unsigned outcnt;
+unsigned insize;
+unsigned inptr;
+
+extern char input_data[], input_end[];
+int input_len;
+
+int input_ptr;
+
+int method, exit_code, part_nb, last_member;
+int test = 0;
+int force = 0;
+int verbose = 1;
+long bytes_in, bytes_out;
+
+char *output_data;
+unsigned long output_ptr;
+
+extern int end;
+long free_mem_ptr = (long)&end;
+
+int to_stdout = 0;
+int hard_math = 0;
+
+void (*work)(int inf, int outf);
+void makecrc(void);
+
+local int get_method(int);
+
+char *vidmem;
+int video_num_lines, video_num_columns;
+int bytes_per_char_h;
+int white;
+
+static void puts(const char *);
+
+const char cmap_80[][8]=
+{
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /*   */
+ {0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x00}, /* ! */
+ {0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00}, /* " */
+ {0x36,0x36,0x7F,0x36,0x7F,0x36,0x36,0x00}, /* # */
+ {0x0C,0x3F,0x68,0x3E,0x0B,0x7E,0x18,0x00}, /* $ */
+ {0x60,0x66,0x0C,0x18,0x30,0x66,0x06,0x00}, /* % */
+ {0x38,0x6C,0x6C,0x38,0x6D,0x66,0x3B,0x00}, /* & */
+ {0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}, /* ' */
+ {0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00}, /* ( */
+ {0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00}, /* ) */
+ {0x00,0x18,0x7E,0x3C,0x7E,0x18,0x00,0x00}, /* * */
+ {0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00}, /* + */
+ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30}, /* , */
+ {0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00}, /* - */
+ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00}, /* . */
+ {0x00,0x06,0x0C,0x18,0x30,0x60,0x00,0x00}, /* / */
+ {0x3C,0x66,0x6E,0x7E,0x76,0x66,0x3C,0x00}, /* 0 */
+ {0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00}, /* 1 */
+ {0x3C,0x66,0x06,0x0C,0x18,0x30,0x7E,0x00}, /* 2 */
+ {0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 3 */
+ {0x0C,0x1C,0x3C,0x6C,0x7E,0x0C,0x0C,0x00}, /* 4 */
+ {0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 5 */
+ {0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 6 */
+ {0x7E,0x06,0x0C,0x18,0x30,0x30,0x30,0x00}, /* 7 */
+ {0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 8 */
+ {0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, /* 9 */
+ {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x00}, /* : */
+ {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x30}, /* ; */
+ {0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00}, /* < */
+ {0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00}, /* = */
+ {0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00}, /* > */
+ {0x3C,0x66,0x0C,0x18,0x18,0x00,0x18,0x00}, /* ? */
+ {0x3C,0x66,0x6E,0x6A,0x6E,0x60,0x3C,0x00}, /* @ */
+ {0x3C,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* A */
+ {0x7C,0x66,0x66,0x7C,0x66,0x66,0x7C,0x00}, /* B */
+ {0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x00}, /* C */
+ {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}, /* D */
+ {0x7E,0x60,0x60,0x7C,0x60,0x60,0x7E,0x00}, /* E */
+ {0x7E,0x60,0x60,0x7C,0x60,0x60,0x60,0x00}, /* F */
+ {0x3C,0x66,0x60,0x6E,0x66,0x66,0x3C,0x00}, /* G */
+ {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* H */
+ {0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00}, /* I */
+ {0x3E,0x0C,0x0C,0x0C,0x0C,0x6C,0x38,0x00}, /* J */
+ {0x66,0x6C,0x78,0x70,0x78,0x6C,0x66,0x00}, /* K */
+ {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}, /* L */
+ {0x63,0x77,0x7F,0x6B,0x6B,0x63,0x63,0x00}, /* M */
+ {0x66,0x66,0x76,0x7E,0x6E,0x66,0x66,0x00}, /* N */
+ {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* O */
+ {0x7C,0x66,0x66,0x7C,0x60,0x60,0x60,0x00}, /* P */
+ {0x3C,0x66,0x66,0x66,0x6A,0x6C,0x36,0x00}, /* Q */
+ {0x7C,0x66,0x66,0x7C,0x6C,0x66,0x66,0x00}, /* R */
+ {0x3C,0x66,0x60,0x3C,0x06,0x66,0x3C,0x00}, /* S */
+ {0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* T */
+ {0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* U */
+ {0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00}, /* V */
+ {0x63,0x63,0x6B,0x6B,0x7F,0x77,0x63,0x00}, /* W */
+ {0x66,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00}, /* X */
+ {0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00}, /* Y */
+ {0x7E,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00}, /* Z */
+ {0x7C,0x60,0x60,0x60,0x60,0x60,0x7C,0x00}, /* [ */
+ {0x00,0x60,0x30,0x18,0x0C,0x06,0x00,0x00}, /* \ */
+ {0x3E,0x06,0x06,0x06,0x06,0x06,0x3E,0x00}, /* ] */
+ {0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00}, /* ^ */
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF}, /* _ */
+ {0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00}, /* ` */
+ {0x00,0x00,0x3C,0x06,0x3E,0x66,0x3E,0x00}, /* a */
+ {0x60,0x60,0x7C,0x66,0x66,0x66,0x7C,0x00}, /* b */
+ {0x00,0x00,0x3C,0x66,0x60,0x66,0x3C,0x00}, /* c */
+ {0x06,0x06,0x3E,0x66,0x66,0x66,0x3E,0x00}, /* d */
+ {0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00}, /* e */
+ {0x1C,0x30,0x30,0x7C,0x30,0x30,0x30,0x00}, /* f */
+ {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x3C}, /* g */
+ {0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x00}, /* h */
+ {0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00}, /* i */
+ {0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x70}, /* j */
+ {0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00}, /* k */
+ {0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00}, /* l */
+ {0x00,0x00,0x36,0x7F,0x6B,0x6B,0x63,0x00}, /* m */
+ {0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00}, /* n */
+ {0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00}, /* o */
+ {0x00,0x00,0x7C,0x66,0x66,0x7C,0x60,0x60}, /* p */
+ {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x07}, /* q */
+ {0x00,0x00,0x6C,0x76,0x60,0x60,0x60,0x00}, /* r */
+ {0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00}, /* s */
+ {0x30,0x30,0x7C,0x30,0x30,0x30,0x1C,0x00}, /* t */
+ {0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x00}, /* u */
+ {0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00}, /* v */
+ {0x00,0x00,0x63,0x6B,0x6B,0x7F,0x36,0x00}, /* w */
+ {0x00,0x00,0x66,0x3C,0x18,0x3C,0x66,0x00}, /* x */
+ {0x00,0x00,0x66,0x66,0x66,0x3E,0x06,0x3C}, /* y */
+ {0x00,0x00,0x7E,0x0C,0x18,0x30,0x7E,0x00}, /* z */
+ {0x0C,0x18,0x18,0x70,0x18,0x18,0x0C,0x00}, /* { */
+ {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* | */
+ {0x30,0x18,0x18,0x0E,0x18,0x18,0x30,0x00}, /* } */
+ {0x31,0x6B,0x46,0x00,0x00,0x00,0x00,0x00}, /* ~ */
+ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}  /*  */
+};
+
+void *malloc(int size)
+{
+	void *p;
+
+	if (size <0) error("Malloc error\n");
+	if (free_mem_ptr <= 0) error("Memory error\n");
+
+	while(1) {
+		free_mem_ptr = (free_mem_ptr + 3) & ~3;	/* Align */
+
+		p = (void *)free_mem_ptr;
+		free_mem_ptr += size;
+
+		/*
+  		 * The part of the compressed kernel which has already been expanded
+		 * is no longer needed. Therefore we can reuse it for malloc.
+		 * With bigger kernels, this is necessary.
+		 */
+ 
+		if (free_mem_ptr < (long)&end) {
+			if (free_mem_ptr > (long)&input_data[input_ptr])
+				error("\nOut of memory\n");
+
+			return p;
+		}
+		if (free_mem_ptr < 0x01a00000)
+			return p;
+		puts("large kernel, memory tight...");
+		free_mem_ptr = (long)input_data;
+	}
+}
+
+void free(void *where)
+{	/* Don't care */
+}
+
+static void scroll()
+{
+#if 0
+	int i;
+
+	memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
+	for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
+		vidmem[i] = ' ';
+#endif
+}
+
+static void puts(const char *s)
+{
+	extern void ll_char_write(char *, char, char, char, char);
+	int x,y;
+	char c;
+	char *ptr;
+
+	x = params->video_x;
+	y = params->video_y;
+
+	while ( ( c = *s++ ) != '\0' ) {
+		if ( c == '\n' ) {
+			x = 0;
+			if ( ++y >= video_num_lines ) {
+				scroll();
+				y--;
+			}
+		} else {
+			ptr = vidmem + (y*video_num_columns*params->bytes_per_char_v+x)*bytes_per_char_h;
+			ll_char_write(ptr, c, white, 0, 0);
+			if ( ++x >= video_num_columns ) {
+				x = 0;
+				if ( ++y >= video_num_lines ) {
+					scroll();
+					y--;
+				}
+			}
+		}
+	}
+
+	params->video_x = x;
+	params->video_y = y;
+}
+
+__ptr_t memset(__ptr_t s, int c, size_t n)
+{
+	int i;
+	char *ss = (char*)s;
+
+	for (i=0;i<n;i++) ss[i] = c;
+}
+
+__ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src,
+			    size_t __n)
+{
+	int i;
+	char *d = (char *)__dest, *s = (char *)__src;
+
+	for (i=0;i<__n;i++) d[i] = s[i];
+}
+
+extern ulg crc_32_tab[];   /* crc table, defined below */
+
+/* ===========================================================================
+ * Run a set of bytes through the crc shift register.  If s is a NULL
+ * pointer, then initialize the crc shift register contents instead.
+ * Return the current crc in either case.
+ */
+ulg updcrc(s, n)
+    uch *s;                 /* pointer to bytes to pump through */
+    unsigned n;             /* number of bytes in s[] */
+{
+    register ulg c;         /* temporary variable */
+
+    static ulg crc = (ulg)0xffffffffL; /* shift register contents */
+
+    if (s == NULL) {
+	c = 0xffffffffL;
+    } else {
+	c = crc;
+	while (n--) {
+	    c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8);
+	}
+    }
+    crc = c;
+    return c ^ 0xffffffffL;       /* (instead of ~c for 64-bit machines) */
+}
+
+/* ===========================================================================
+ * Clear input and output buffers
+ */
+void clear_bufs()
+{
+    outcnt = 0;
+    insize = inptr = 0;
+    bytes_in = bytes_out = 0L;
+}
+
+/* ===========================================================================
+ * Fill the input buffer. This is called only when the buffer is empty
+ * and at least one byte is really needed.
+ */
+int fill_inbuf()
+{
+    int len, i;
+
+    /* Read as much as possible */
+    insize = 0;
+    do {
+	len = INBUFSIZ-insize;
+	if (len > (input_len-input_ptr+1)) len=input_len-input_ptr+1;
+        if (len == 0 || len == EOF) break;
+
+        for (i=0;i<len;i++) inbuf[insize+i] = input_data[input_ptr+i];
+	insize += len;
+	input_ptr += len;
+    } while (insize < INBUFSIZ);
+
+    if (insize == 0) {
+	error("unable to fill buffer\n");
+    }
+    bytes_in += (ulg)insize;
+    inptr = 1;
+    return inbuf[0];
+}
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+void flush_window()
+{
+    if (outcnt == 0) return;
+    updcrc(window, outcnt);
+
+    memcpy(&output_data[output_ptr], (char *)window, outcnt);
+
+    bytes_out += (ulg)outcnt;
+    output_ptr += (ulg)outcnt;
+    outcnt = 0;
+}
+
+/*
+ * Code to compute the CRC-32 table. Borrowed from 
+ * gzip-1.0.3/makecrc.c.
+ */
+
+ulg crc_32_tab[256];
+
+void
+makecrc(void)
+{
+/* Not copyrighted 1990 Mark Adler	*/
+
+  unsigned long c;      /* crc shift register */
+  unsigned long e;      /* polynomial exclusive-or pattern */
+  int i;                /* counter for all possible eight bit values */
+  int k;                /* byte being shifted into crc apparatus */
+
+  /* terms of polynomial defining this crc (except x^32): */
+  static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+  /* Make exclusive-or pattern from polynomial */
+  e = 0;
+  for (i = 0; i < sizeof(p)/sizeof(int); i++)
+    e |= 1L << (31 - p[i]);
+
+  crc_32_tab[0] = 0;
+
+  for (i = 1; i < 256; i++)
+  {
+    c = 0;
+    for (k = i | 256; k != 1; k >>= 1)
+    {
+      c = c & 1 ? (c >> 1) ^ e : c >> 1;
+      if (k & 1)
+        c ^= e;
+    }
+    crc_32_tab[i] = c;
+  }
+}
+
+void error(char *x)
+{
+	puts("\n\n");
+	puts(x);
+	puts("\n\n -- System halted");
+
+	while(1);	/* Halt */
+}
+
+#define STACK_SIZE (4096)
+
+long _stack_start [STACK_SIZE];
+
+void decompress_kernel(int output_start)
+{
+	input_len = input_end - input_data;
+	vidmem = (char *)0x02000000;
+
+	video_num_lines = params->video_num_rows;
+	video_num_columns  = params->video_num_cols;
+	bytes_per_char_h = params->bytes_per_char_h;
+	white = bytes_per_char_h == 8 ? 0xfc : 7;
+	
+	if (params->nr_pages * params->page_size < 2048*1024) error("<2M of mem\n");
+
+	output_data = (char *)output_start;	/* Points to kernel start */
+	output_ptr = 0;
+
+	exit_code = 0;
+	test = 0;
+	input_ptr = 0;
+	part_nb = 0;
+
+	clear_bufs();
+	makecrc();
+
+	puts("Uncompressing Linux...");
+
+	method = get_method(0);
+
+	work(0, 0);
+
+	puts("done.\n");
+
+	puts("Now booting the kernel\n");
+}
+
+/* ========================================================================
+ * Check the magic number of the input file and update ofname if an
+ * original name was given and to_stdout is not set.
+ * Return the compression method, -1 for error, -2 for warning.
+ * Set inptr to the offset of the next byte to be processed.
+ * This function may be called repeatedly for an input file consisting
+ * of several contiguous gzip'ed members.
+ * IN assertions: there is at least one remaining compressed member.
+ *   If the member is a zip file, it must be the only one.
+ */
+
+union magic
+{
+	char magic[2];
+	short s_magic;
+};
+
+local int get_method(in)
+    int in;        /* input file descriptor */
+{
+    uch flags;
+    union magic magic; /* magic header */
+    union magic gzip_magic = { GZIP_MAGIC };
+    union magic old_gzip_magic = { OLD_GZIP_MAGIC };
+
+    magic.magic[0] = (char)get_byte();
+    magic.magic[1] = (char)get_byte();
+
+    method = -1;                 /* unknown yet */
+    part_nb++;                   /* number of parts in gzip file */
+    last_member = 0;
+    /* assume multiple members in gzip file except for record oriented I/O */
+
+    if (magic.s_magic == gzip_magic.s_magic
+        || magic.s_magic == old_gzip_magic.s_magic) {
+
+	work = unzip;
+	method = (int)get_byte();
+	flags  = (uch)get_byte();
+
+	if ((flags & ENCRYPTED) != 0)
+		error("Input is encrypted\n");
+
+	if ((flags & CONTINUATION) != 0)
+		error("Multi part input\n");
+
+	if ((flags & RESERVED) != 0) {
+		error("Input has invalid flags\n");
+		exit_code = ERROR;
+		if (force <= 1)
+			return -1;
+	}
+
+	(ulg)get_byte();	/* Get timestamp */
+	((ulg)get_byte()) << 8;
+	((ulg)get_byte()) << 16;
+	((ulg)get_byte()) << 24;
+
+	(void)get_byte();  /* Ignore extra flags for the moment */
+	(void)get_byte();  /* Ignore OS type for the moment */
+
+	if ((flags & EXTRA_FIELD) != 0) {
+	    unsigned len = (unsigned)get_byte();
+	    len |= ((unsigned)get_byte())<<8;
+	    while (len--) (void)get_byte();
+	}
+
+	/* Get original file name if it was truncated */
+	if ((flags & ORIG_NAME) != 0) {
+		if (to_stdout || part_nb > 1) {
+			/* Discard the old name */
+			while (get_byte() != 0) /* null */ ;
+		} else {
+		} /* to_stdout */
+	} /* orig_name */
+
+	/* Discard file comment if any */
+	if ((flags & COMMENT) != 0) {
+		while (get_byte() != 0) /* null */ ;
+	}
+    } else
+	error("unknown compression method");
+    return method;
+}
diff -urNwbB linux/arch/arm/boot/compressed/piggyback.c linux.arm/arch/arm/boot/compressed/piggyback.c
--- linux/arch/arm/boot/compressed/piggyback.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/piggyback.c	Sun Feb 11 09:32:12 1996
@@ -0,0 +1,83 @@
+/*
+ *	linux/zBoot/piggyback.c
+ *
+ *	(C) 1993 Hannu Savolainen
+ */
+
+/*
+ *	This program reads the compressed system image from stdin and
+ *	encapsulates it into an object file written to the stdout.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <a.out.h>
+
+int main(int argc, char *argv[])
+{
+	int c, n=0, len=0;
+	char tmp_buf[512*1024];
+	
+	struct exec obj = {0x00670107};	/* object header */
+	char string_names[] = {"_input_data\0_input_end\0"};
+
+	struct nlist var_names[2] = /* Symbol table */
+		{	
+			{	/* _input_data	*/
+				(char *)4, 7, 0, 0, 0
+			},
+			{	/* _input_len */
+				(char *)16, 7, 0, 0, 0
+			}
+		};
+
+
+	len = 0;
+	while ((n = read(0, &tmp_buf[len], sizeof(tmp_buf)-len+1)) > 0)
+	      len += n;
+
+	len = (len + 3) & ~3;
+
+	if (n==-1)
+	{
+		perror("stdin");
+		exit(-1);
+	}
+
+	if (len >= sizeof(tmp_buf))
+	{
+		fprintf(stderr, "%s: Input too large\n", argv[0]);
+		exit(-1);
+	}
+
+	fprintf(stderr, "Compressed size %d.\n", len);
+
+/*
+ *	Output object header
+ */
+	obj.a_data = len /* + sizeof(long) */;
+	obj.a_syms = sizeof(var_names);
+	write(1, (char *)&obj, sizeof(obj));
+
+/*
+ *	Output data segment (compressed system & len)
+ */
+	write(1, tmp_buf, len);
+/*	write(1, (char *)&len, sizeof(len));*/
+
+/*
+ *	Output symbol table
+ */
+	var_names[1].n_value = len;
+	write(1, (char *)&var_names, sizeof(var_names));
+
+/*
+ *	Output string table
+ */
+	len = sizeof(string_names) + sizeof(len);
+	write(1, (char *)&len, sizeof(len));
+	write(1, string_names, sizeof(string_names));
+
+	exit(0);
+
+}
diff -urNwbB linux/arch/arm/boot/compressed/unzip.c linux.arm/arch/arm/boot/compressed/unzip.c
--- linux/arch/arm/boot/compressed/unzip.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/unzip.c	Sat Feb 24 09:36:43 1996
@@ -0,0 +1,180 @@
+/* unzip.c -- decompress files in gzip or pkzip format.
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ *
+ * Adapted for Linux booting by Hannu Savolainen 1993
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ *
+ * The code in this file is derived from the file funzip.c written
+ * and put in the public domain by Mark Adler.
+ */
+
+/*
+   This version can extract files in gzip or pkzip format.
+   For the latter, only the first entry is extracted, and it has to be
+   either deflated or stored.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: unzip.c,v 0.9 1993/02/10 16:07:22 jloup Exp $";
+#endif
+
+#include "gzip.h"
+#include "crypt.h"
+
+#include <stdio.h>
+
+/* PKZIP header definitions */
+#define LOCSIG 0x04034b50L      /* four-byte lead-in (lsb first) */
+#define LOCFLG 6                /* offset of bit flag */
+#define  CRPFLG 1               /*  bit for encrypted entry */
+#define  EXTFLG 8               /*  bit for extended local header */
+#define LOCHOW 8                /* offset of compression method */
+#define LOCTIM 10               /* file mod time (for decryption) */
+#define LOCCRC 14               /* offset of crc */
+#define LOCSIZ 18               /* offset of compressed size */
+#define LOCLEN 22               /* offset of uncompressed length */
+#define LOCFIL 26               /* offset of file name field length */
+#define LOCEXT 28               /* offset of extra field length */
+#define LOCHDR 30               /* size of local header, including sig */
+#define EXTHDR 16               /* size of extended local header, inc sig */
+
+
+/* Globals */
+
+int decrypt;      /* flag to turn on decryption */
+char *key;        /* not used--needed to link crypt.c */
+int pkzip = 0;    /* set for a pkzip file */
+int extended = 0; /* set if extended local header */
+
+/* ===========================================================================
+ * Check zip file and advance inptr to the start of the compressed data.
+ * Get ofname from the local header if necessary.
+ */
+int check_zipfile(in)
+    int in;   /* input file descriptors */
+{
+    uch *h = inbuf + inptr; /* first local header */
+
+    /* ifd = in; */
+
+    /* Check validity of local header, and skip name and extra fields */
+    inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT);
+
+    if (inptr > insize || LG(h) != LOCSIG) {
+	error("input not a zip");
+    }
+    method = h[LOCHOW];
+    if (method != STORED && method != DEFLATED) {
+	error("first entry not deflated or stored--can't extract");
+    }
+
+    /* If entry encrypted, decrypt and validate encryption header */
+    if ((decrypt = h[LOCFLG] & CRPFLG) != 0) {
+	error("encrypted file\n");
+	exit_code = ERROR;
+	return -1;
+    }
+
+    /* Save flags for unzip() */
+    extended = (h[LOCFLG] & EXTFLG) != 0;
+    pkzip = 1;
+
+    /* Get ofname and time stamp from local header (to be done) */
+    return 0;
+}
+
+/* ===========================================================================
+ * Unzip in to out.  This routine works on both gzip and pkzip files.
+ *
+ * IN assertions: the buffer inbuf contains already the beginning of
+ *   the compressed data, from offsets inptr to insize-1 included.
+ *   The magic header has already been checked. The output buffer is cleared.
+ */
+void unzip(in, out)
+    int in, out;   /* input and output file descriptors */
+{
+    ulg orig_crc = 0;       /* original crc */
+    ulg orig_len = 0;       /* original uncompressed length */
+    int n;
+    uch buf[EXTHDR];        /* extended local header */
+
+    /* ifd = in;
+    ofd = out; */
+
+    updcrc(NULL, 0);           /* initialize crc */
+
+    if (pkzip && !extended) {  /* crc and length at the end otherwise */
+	orig_crc = LG(inbuf + LOCCRC);
+	orig_len = LG(inbuf + LOCLEN);
+    }
+
+    /* Decompress */
+    if (method == DEFLATED)  {
+
+	int res = inflate();
+
+	if (res == 3) {
+	    error("out of memory");
+	} else if (res != 0) {
+	    error("invalid compressed format");
+	}
+
+    } else if (pkzip && method == STORED) {
+
+	register ulg n = LG(inbuf + LOCLEN);
+
+	if (n != LG(inbuf + LOCSIZ) - (decrypt ? RAND_HEAD_LEN : 0)) {
+
+	    error("length mismatch");
+	}
+	while (n--) {
+	    uch c = (uch)get_byte();
+#ifdef CRYPT
+	    if (decrypt) zdecode(c);
+#endif
+	    if (!test) put_char(c);
+	}
+    } else {
+	error("internal error, invalid method");
+    }
+
+    /* Get the crc and original length */
+    if (!pkzip) {
+        /* crc32  (see algorithm.doc)
+	 * uncompressed input size modulo 2^32
+         */
+	for (n = 0; n < 8; n++) {
+	    buf[n] = (uch)get_byte(); /* may cause an error if EOF */
+	}
+	orig_crc = LG(buf);
+	orig_len = LG(buf+4);
+
+    } else if (extended) {  /* If extended header, check it */
+	/* signature - 4bytes: 0x50 0x4b 0x07 0x08
+	 * CRC-32 value
+         * compressed size 4-bytes
+         * uncompressed size 4-bytes
+	 */
+	for (n = 0; n < EXTHDR; n++) {
+	    buf[n] = (uch)get_byte(); /* may cause an error if EOF */
+	}
+	orig_crc = LG(buf+4);
+	orig_len = LG(buf+12);
+    }
+
+    /* Validate decompression */
+    if (orig_crc != updcrc(outbuf, 0)) {
+	error("crc error");
+    }
+    if (orig_len != bytes_out) {
+	error("length error");
+    }
+
+    /* Check if there are more entries in a pkzip file */
+    if (pkzip && inptr + 4 < insize && LG(inbuf+inptr) == LOCSIG) {
+	    error("zip file has more than one entry");
+    }
+    extended = pkzip = 0; /* for next file */
+}
diff -urNwbB linux/arch/arm/boot/compressed/xtract.c linux.arm/arch/arm/boot/compressed/xtract.c
--- linux/arch/arm/boot/compressed/xtract.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/xtract.c	Sun Feb 11 09:32:12 1996
@@ -0,0 +1,86 @@
+/*
+ *  linux/zBoot/xtract.c
+ *
+ *  Copyright (C) 1993  Hannu Savolainen
+ *
+ *	Extracts the system image and writes it to the stdout.
+ *	based on tools/build.c by Linus Torvalds
+ */
+
+#include <stdio.h>	/* fprintf */
+#include <string.h>
+#include <stdlib.h>	/* contains exit */
+#include <sys/types.h>	/* unistd.h needs this */
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>	/* contains read/write */
+#include <fcntl.h>
+#include <a.out.h>
+
+#define N_MAGIC_OFFSET 1024
+
+static int GCC_HEADER = sizeof(struct exec);
+
+#define STRINGIFY(x) #x
+
+void die(char * str)
+{
+	fprintf(stderr,"%s\n",str);
+	exit(1);
+}
+
+void usage(void)
+{
+	die("Usage: xtract system [ | gzip | piggyback > piggy.s]");
+}
+
+int main(int argc, char ** argv)
+{
+	int i,c,id, sz;
+	char buf[1024];
+	char major_root, minor_root;
+	struct stat sb;
+
+	struct exec *ex = (struct exec *)buf;
+
+	if (argc  != 2)
+		usage();
+	
+	if ((id=open(argv[1],O_RDONLY,0))<0)
+		die("Unable to open 'system'");
+	if (read(id,buf,GCC_HEADER) != GCC_HEADER)
+		die("Unable to read header of 'system'");
+	if (N_MAGIC(*ex) == ZMAGIC) {
+		GCC_HEADER = N_MAGIC_OFFSET;
+		lseek(id, GCC_HEADER, SEEK_SET);
+	} else if (N_MAGIC(*ex) != QMAGIC)
+		die("Non-GCC header of 'system'");
+
+	sz = N_SYMOFF(*ex) - GCC_HEADER + 4;	/* +4 to get the same result than tools/build */
+
+	fprintf(stderr, "System size is %d\n", sz);
+
+	while (sz)
+	{
+		int l, n;
+
+		l = sz;
+		if (l > sizeof(buf)) l = sizeof(buf);
+
+		if ((n=read(id, buf, l)) !=l)
+		{
+			if (n == -1) 
+			   perror(argv[1]);
+			else
+			   fprintf(stderr, "Unexpected EOF\n");
+
+			die("Can't read system");
+		}
+
+		write(1, buf, l);
+		sz -= l;
+	}
+
+	close(id);
+	return(0);
+}
diff -urNwbB linux/arch/arm/boot/install.sh linux.arm/arch/arm/boot/install.sh
--- linux/arch/arm/boot/install.sh	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/install.sh	Sun Mar  3 12:16:58 1996
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# arch/i386/boot/install.sh
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1995 by Linus Torvalds
+#
+# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
+#
+# "make install" script for i386 architecture
+#
+# Arguments:
+#   $1 - kernel version
+#   $2 - kernel image file
+#   $3 - kernel map file
+#   $4 - default install path (blank if root directory)
+#
+
+# User may have a custom install script
+
+if [ -x /sbin/installkernel ]; then
+  exec /sbin/installkernel "$@"
+fi
+
+if [ "$2" = "zImage" ]; then
+# Compressed install
+  echo "Installing compressed kernel"
+  if [ -f $4/vmlinuz-$1 ]; then
+    mv $4/vmlinuz-$1 $4/vmlinuz.old
+  fi
+
+  if [ -f $4/System.map-$1 ]; then
+    mv $4/System.map-$1 $4/System.old
+  fi
+
+  cat $2 > $4/vmlinuz-$1
+  cp $3 $4/System.map-$1
+else
+# Normal install
+  echo "Installing normal kernel"
+  if [ -f $4/vmlinux-$1 ]; then
+    mv $4/vmlinux-$1 $4/vmlinux.old
+  fi
+
+  if [ -f $4/System.map ]; then
+    mv $4/System.map $4/System.old
+  fi
+
+  cat $2 > $4/vmlinux-$1
+  cp $3 $4/System.map
+fi
+
+if [ -x /sbin/loadmap ]; then
+  /sbin/loadmap
+else
+  echo "You have to install it yourself"
+fi
diff -urNwbB linux/arch/arm/boot/tools/build.c linux.arm/arch/arm/boot/tools/build.c
--- linux/arch/arm/boot/tools/build.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/tools/build.c	Sun Feb 11 22:01:19 1996
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <a.out.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+	void *data;
+	struct exec ex;
+	FILE *f;
+	int totlen;
+
+	if (argc < 2) {
+		fprintf(stderr, "Usage: build kernel-name\n");
+		exit(1);
+	}
+
+	f = fopen(argv[1], "rb");
+
+	fread(&ex, 1, sizeof(ex), f);
+
+	if(N_MAGIC(ex) == ZMAGIC) {
+		fseek(f, 4096, SEEK_SET);
+		totlen = ex.a_text + ex.a_data + ex.a_bss;
+	} else
+	if(N_MAGIC(ex) == QMAGIC) {
+		unsigned long my_header[8];
+		
+		fseek(f, 0x20, SEEK_SET);
+
+		memset(my_header, 0, 0x20);
+
+		my_header[0] = 0xea000006;
+		
+		fwrite(my_header, 4, 8, stdout);
+		
+		totlen = ex.a_text + ex.a_data + ex.a_bss - 0x20;
+	} else {
+		fprintf(stderr, "Unacceptable a.out header on kernel\n");
+		fclose(f);
+		exit(1);
+	}
+
+	fprintf(stderr, "Kernel is %dk (%dk text, %dk data, %dk bss)\n",
+		(ex.a_text + ex.a_data + ex.a_bss)/1024,
+		 ex.a_text/1024, ex.a_data/1024, ex.a_bss/1024);
+
+	data = malloc(totlen);
+	fread(data, 1, totlen, f);
+	fwrite(data, 1, totlen, stdout);
+
+	free(data);
+	fclose(f);
+	fflush(stdout);
+	return 0;
+}
diff -urNwbB linux/arch/arm/config.in linux.arm/arch/arm/config.in
--- linux/arch/arm/config.in	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/config.in	Sat Feb 24 21:32:58 1996
@@ -0,0 +1,202 @@
+#
+# For a description of the syntax of this configuration file,
+# see the Configure script.
+#
+# This is to tell configure not to execute the sound configuration.
+#
+CONFIG_ARM=1
+
+mainmenu_name "Linux kernel configuration"
+
+echo "/* Automatically generated by configure.  Use Make config to change */" >> $CONFIG_H
+#
+# These are to alter the behaviour of various Makefiles and init/main.c
+# in the architecture-independent areas.
+#
+echo "#define CONFIG_ARM 1" >> $CONFIG_H
+echo "CONFIG_ARM=1" >> $CONFIG
+
+mainmenu_option next_comment
+comment 'General setup'
+bool 'Compile for arm3' CONFIG_ARM3 y
+if [ "$CONFIG_ARM3" = "n" ]; then
+  bool 'Compile for arm610/710' CONFIG_ARM610 y
+  if [ "$CONFIG_ARM610" = "n" ]; then
+    bool 'Compile for StrongARM' CONFIG_ARMSTRONG y
+  fi
+else
+  bool 'Page size as 32K' CONFIG_PAGE_32K y
+  if [ "$CONFIG_PAGE_32K" = "n" ]; then
+    bool 'Page size as 16K' CONFIG_PAGE_16K y
+    if [ "$CONFIG_PAGE_16K" = "n" ]; then
+      echo "Auto detected page size"
+    fi
+  fi
+fi
+
+comment 'Math emulation is provided by a run-time loadable module'
+
+tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD y
+bool 'Normal IDE disk/cdrom support' CONFIG_ST506 y
+if [ "$CONFIG_ST506" = "y" ]; then
+  comment 'Please see drivers/block/README.ide for help/info on IDE drives'
+  bool '   Use old disk-only driver for primary i/f' CONFIG_BLK_DEV_HD y
+#  if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then
+#    bool '   Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE n
+#  else
+#    bool '   Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE y
+#  fi
+#  if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
+#    bool '   Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD n
+#  fi
+fi
+tristate 'MFM harddisk support' CONFIG_BLK_DEV_XD n
+
+bool 'Networking support' CONFIG_NET y
+bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
+bool 'System V IPC' CONFIG_SYSVIPC y
+#tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF n
+
+mainmenu_option next_comment
+comment 'Loadable module support'
+bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS y
+
+if [ "$CONFIG_NET" = "y" ]; then
+  mainmenu_option next_comment
+  comment 'Networking options'
+  bool 'TCP/IP networking' CONFIG_INET y
+  if [ "$CONFIG_INET" = "y" ]; then
+    bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD n
+    bool 'IP: multicasting' CONFIG_IP_MULTICAST n
+    bool 'IP: firewalling' CONFIG_IP_FIREWALL n
+    bool 'IP: accounting' CONFIG_IP_ACCT n
+    tristate 'IP: tunneling' CONFIG_NET_IPIP n
+    if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_FIREWALL" = "y" ]; then
+      bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE y
+      bool 'IP: masquerading (ALPHA)' CONFIG_IP_MASQUERADE n
+    fi
+    comment '(it is safe to leave these untouched)'
+    bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP n
+    tristate 'IP: Reverse ARP' CONFIG_INET_RARP n
+    bool 'IP: Assume subnets are local' CONFIG_INET_SNARL y
+    bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n
+    bool 'IP: Drop source routed frames' CONFIG_IP_NOSR y
+    bool 'IP: Allow large windows (not recommended if <16MB of memory)' CONFIG_SKB_LARGE n
+  fi
+  bool 'The IPX protocol' CONFIG_IPX n
+  bool 'Appletalk DDP' CONFIG_ATALK n
+  bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n
+  if [ "$CONFIG_AX25" = "y" ]; then
+    bool 'Amateur Radio NET/ROM' CONFIG_NETROM n
+  fi
+  bool 'Kernel/User network link driver(ALPHA)' CONFIG_NETLINK n
+  if [ "$CONFIG_NETLINK" = "y" ]; then
+    bool 'Routing messages' CONFIG_RTNETLINK y
+  fi
+fi
+
+mainmenu_option next_comment
+comment 'SCSI support'
+
+tristate 'SCSI support?' CONFIG_SCSI n
+
+if [ "$CONFIG_SCSI" = "n" ]; then
+
+  comment 'Skipping SCSI configuration options...'
+
+else
+
+  comment 'SCSI support type (disk, tape, CDrom)'
+
+  dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD y $CONFIG_SCSI
+  dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST y $CONFIG_SCSI
+  dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR y $CONFIG_SCSI
+  dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG n $CONFIG_SCSI
+
+  comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs'
+
+  bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN n
+
+  mainmenu_option next_comment
+  comment 'SCSI low-level drivers'
+
+  dep_tristate 'Acorn SCSI card (aka30) support' CONFIG_SCSI_ACORNSCSI_3 y $CONFIG_SCSI
+  dep_tristate 'Cumana 1 support' CONFIG_SCSI_CUMANA_1 n $CONFIG_SCSI
+  dep_tristate 'EcoScsi support' CONFIG_SCSI_ECOSCSI n $CONFIG_SCSI
+  dep_tristate 'Oak SCSI support' CONFIG_SCSI_OAK n $CONFIG_SCSI
+  dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n $CONFIG_SCSI
+fi
+
+if [ "$CONFIG_NET" = "y" ]; then
+
+  mainmenu_option next_comment
+  comment 'Network device support'
+
+  bool 'Network device support?' CONFIG_NETDEVICES y
+  if [ "$CONFIG_NETDEVICES" = "n" ]; then
+
+  comment 'Skipping network driver configuration options...'
+
+  else
+    tristate 'Dummy net driver support' CONFIG_DUMMY y
+    tristate 'SLIP (serial line) support' CONFIG_SLIP y
+    if [ "$CONFIG_SLIP" = "y" ]; then
+      bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y
+    fi
+    tristate 'PPP (point-to-point) support' CONFIG_PPP y
+    if [ ! "$CONFIG_PPP" = "n" ]; then
+      comment 'CCP compressors for PPP are only built as modules.'
+    fi
+    tristate 'PLIP (parallel port) support' CONFIG_PLIP y
+    tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER n
+    tristate 'Ether1 (82586) support' CONFIG_ETHER1 n
+    tristate 'Ether3 (NQ8005) support' CONFIG_ETHER3 n
+  fi
+fi
+
+mainmenu_option next_comment
+comment 'Filesystems'
+
+tristate 'Standard (minix) fs support' CONFIG_MINIX_FS n
+tristate 'Extended fs support' CONFIG_EXT_FS n
+tristate 'Second extended fs support' CONFIG_EXT2_FS y
+tristate 'xiafs filesystem support' CONFIG_XIA_FS n
+tristate 'msdos fs support' CONFIG_MSDOS_FS y
+if [ "$CONFIG_MSDOS_FS" = "y" ]; then
+  tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
+fi
+tristate '/proc filesystem support' CONFIG_PROC_FS y
+tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
+tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
+tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
+if [ "$CONFIG_INET" = "y" ]; then
+  tristate 'NFS filesystem support' CONFIG_NFS_FS y
+  tristate 'SMB filesystem (to mount WfW shares etc...) support' CONFIG_SMB_FS n
+fi
+
+mainmenu_option next_comment
+comment 'Character devices'
+
+tristate 'Parallel printer support' CONFIG_PRINTER y
+tristate 'Mouse support' CONFIG_MOUSE y
+if [ "$CONFIG_MOUSE" = "n" ]; then
+  comment 'Skipping mouse options...'
+else
+  tristate 'Keyboard mouse [all but RiscPC]' CONFIG_KBDMOUSE y
+fi
+
+mainmenu_option next_comment
+comment 'Sound'
+
+tristate 'Sound support' CONFIG_SOUND n
+
+mainmenu_option next_comment
+comment 'Kernel hacking'
+
+bool 'Kernel profiling support' CONFIG_PROFILE n
+if [ "$CONFIG_PROFILE" = "y" ]; then
+  int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
+fi
+if [ "$CONFIG_SCSI" = "y" ]; then
+bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y
+fi
diff -urNwbB linux/arch/arm/drivers/Makefile linux.arm/arch/arm/drivers/Makefile
--- linux/arch/arm/drivers/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/Makefile	Sun Feb 11 09:32:13 1996
@@ -0,0 +1,33 @@
+#
+# Makefile for the linux kernel device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+SUB_DIRS     := block char net #streams
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS) scsi sound
+
+# If CONFIG_SCSI is set, the core of scsi support will be added to the kernel,
+# but some of the low-level things may also be modules.
+ifeq ($(CONFIG_SCSI),y)
+SUB_DIRS += scsi
+MOD_SUB_DIRS += scsi
+else
+  ifeq ($(CONFIG_SCSI),m)
+  MOD_SUB_DIRS += scsi
+  endif
+endif
+
+ifeq ($(CONFIG_SOUND),y)
+SUB_DIRS += sound
+else
+  ifeq ($(CONFIG_SOUND),m)
+  MOD_SUB_DIRS += sound
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff -urNwbB linux/arch/arm/drivers/block/Makefile linux.arm/arch/arm/drivers/block/Makefile
--- linux/arch/arm/drivers/block/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/Makefile	Sat Feb 24 13:17:05 1996
@@ -0,0 +1,63 @@
+#
+# Makefile for the kernel block device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+all: links first_rule
+
+L_TARGET := block.a
+L_OBJS   := ll_rw_blk.o ramdisk.o genhd.o hdsrch.o
+M_OBJS   :=
+MOD_LIST_NAME := BLOCK_MODULES
+
+ifeq ($(CONFIG_BLK_DEV_FD),y)
+L_OBJS += floppy.o
+else
+  ifeq ($(CONFIG_BLK_DEV_FD),m)
+  M_OBJS += floppy.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HD),y)
+L_OBJS += hd.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE),y)
+L_OBJS += ide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_MFM),y)
+L_OBJS += mfmhd.o
+else
+  ifeq ($(CONFIG_BLK_DEV_MFM),m)
+  M_OBJS += mfmhd_mod.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+fastdep: links
+
+mfmhd_mod.o: mfmhd.o
+	ld -r -o $@ mfmhd.o `gcc --print-libgcc-file-name`
+
+LK = blk.h ll_rw_blk.c ramdisk.c README.fd README.hd
+
+links:
+	-@for f in $(LK); do \
+		ln -s ../../../../drivers/block/$$f .; \
+	done
+	touch links
+
+LINKCLEAN:
+	-@for f in $(LK); do \
+		if [ -L $$f ]; then rm -f $$f; fi; \
+	done
+	rm -f links
+
diff -urNwbB linux/arch/arm/drivers/block/floppy.c linux.arm/arch/arm/drivers/block/floppy.c
--- linux/arch/arm/drivers/block/floppy.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/floppy.c	Sun Mar  3 12:20:41 1996
@@ -0,0 +1,4120 @@
+/*
+ *  linux/arch/arm/drivers/block/floppy.c
+ *  [ was linux/drivers/block/floppy.c ]
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Copyright (C) 1993, 1994  Alain Knaff
+ *  Modifications Copyright (C) 1995 Russell King
+ */
+/*
+ * 02.12.91 - Changed to static variables to indicate need for reset
+ * and recalibrate. This makes some things easier (output_byte reset
+ * checking etc), and means less interrupt jumping in case of errors,
+ * so the code is hopefully easier to understand.
+ */
+
+/*
+ * This file is certainly a mess. I've tried my best to get it working,
+ * but I don't like programming floppies, and I have only one anyway.
+ * Urgel. I should check for more errors, and do more graceful error
+ * recovery. Seems there are problems with several drives. I've tried to
+ * correct them. No promises.
+ */
+
+/*
+ * As with hd.c, all routines within this file can (and will) be called
+ * by interrupts, so extreme caution is needed. A hardware interrupt
+ * handler may not sleep, or a kernel panic will happen. Thus I cannot
+ * call "floppy-on" directly, but have to set a special timer interrupt
+ * etc.
+ */
+
+/*
+ * 28.02.92 - made track-buffering routines, based on the routines written
+ * by entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
+ */
+
+/*
+ * Automatic floppy-detection and formatting written by Werner Almesberger
+ * (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with
+ * the floppy-change signal detection.
+ */
+
+/*
+ * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed
+ * FDC data overrun bug, added some preliminary stuff for vertical
+ * recording support.
+ *
+ * 1992/9/17: Added DMA allocation & DMA functions. -- hhb.
+ *
+ * TODO: Errors are still not counted properly.
+ */
+
+/* 1992/9/20
+ * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl)
+ * modelled after the freeware MS/DOS program fdformat/88 V1.8 by
+ * Christoph H. Hochst\"atter.
+ * I have fixed the shift values to the ones I always use. Maybe a new
+ * ioctl() should be created to be able to modify them.
+ * There is a bug in the driver that makes it impossible to format a
+ * floppy as the first thing after bootup.
+ */
+
+/*
+ * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and
+ * this helped the floppy driver as well. Much cleaner, and still seems to
+ * work.
+ */
+
+/* 1994/6/24 --bbroad-- added the floppy table entries and made
+ * minor modifications to allow 2.88 floppies to be run.
+ */
+
+/* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more
+ * disk types.
+ */
+
+/*
+ * 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger
+ * format bug fixes, but unfortunately some new bugs too...
+ */
+
+/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
+ * errors to allow safe writing by specialized programs.
+ */
+
+/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
+ * by defining bit 1 of the "stretch" parameter to mean put sectors on the
+ * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
+ * drives are "upside-down").
+ */
+
+/*
+ * 1995/8/16 -- Russell King -- altered method for turning floppy drives on
+ * for the arm.
+ */
+#define CONFIG_FLOPPY_SANITY
+#undef  CONFIG_FLOPPY_SILENT_DCL_CLEAR
+
+#define DEBUGT 2
+#define DCL_DEBUG /* debug disk change line */
+
+#include <linux/config.h>
+
+/* do print messages for unexpected interrupts */
+static int print_unex=1;
+
+#ifdef MODULE
+#define FD_MODULE
+
+#include <linux/module.h>
+/*
+ * NB. we must include the kernel identification string to install the module.
+ */
+#include <linux/version.h>
+char kernel_version[] = UTS_RELEASE;
+
+int FLOPPY_IRQ=12;
+int FLOPPY_DMA=2;
+int ALLOWED_DRIVE_MASK = 0x33;
+int FDC1 = 0x3f0;
+int FDC2 = -1;
+
+#endif
+
+#ifndef FD_MODULE
+/* the following is the mask of allowed drives. By default units 2 and
+ * 3 of both floppy controllers are disabled, because switching on the
+ * motor of these drives causes system hangs on some PCI computers. drive
+ * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
+ * a drive is allowed. */
+static int ALLOWED_DRIVE_MASK=0x33;
+
+#define FLOPPY_IRQ 12
+#define FLOPPY_DMA 2
+#define FDC1 0x3f0
+static int FDC2=-1;
+#endif
+
+#define MODULE_AWARE_DRIVER
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+#define FDPATCHES
+#include <linux/fdreg.h>
+
+/* ============================================ *
+ * old fd.h                                     *
+ * =========================================== */
+
+#define OLDFDCLRPRM 0 /* clear user-defined parameters */
+#define OLDFDSETPRM 1 /* set user-defined parameters for current media */
+#define OLDFDSETMEDIAPRM 1
+#define OLDFDDEFPRM 2 /* set user-defined parameters until explicitly cleared */
+#define OLDFDDEFMEDIAPRM 2
+#define OLDFDGETPRM 3 /* get disk parameters */
+#define OLDFDGETMEDIAPRM 3
+#define OLDFDMSGON  4 /* issue kernel messages on media type change */
+#define OLDFDMSGOFF 5 /* don't issue kernel messages on media type change */
+#define OLDFDFMTBEG 6 /* begin formatting a disk */
+#define OLDFDFMTTRK 7 /* format the specified track */
+#define OLDFDFMTEND 8 /* end formatting a disk */
+#define OLDFDSETEMSGTRESH       10      /* set fdc error reporting threshold */
+#define OLDFDFLUSH  11 /* flush buffers for media; either for verifying media, or for
+                       handling a media change without closing the file
+                                              descriptor */
+#define OLDFDSETMAXERRS 12 /* set abortion and read_track threshold */
+#define OLDFDGETMAXERRS 14 /* get abortion and read_track threshold */
+#define OLDFDGETDRVTYP 16          /* get drive type: 5 1/4 or 3 1/2 */
+
+#define OLDFDSETDRVPRM 20 /* set drive parameters */
+#define OLDFDGETDRVPRM 21 /* get drive parameters */
+#define OLDFDGETDRVSTAT 22 /* get drive state */
+#define OLDFDPOLLDRVSTAT 23 /* get drive state */
+#define OLDFDRESET 24 /* reset FDC */
+
+#define OLDFD_RESET_IF_NEEDED 0
+#define OLDFD_RESET_IF_RAWCMD 1
+#define OLDFD_RESET_ALWAYS 2
+
+#define OLDFDGETFDCSTAT 25 /* get fdc state */
+#define OLDFDWERRORCLR  27 /* clear write error and badness information */
+#define OLDFDWERRORGET  28 /* get write error and badness information */
+
+#define OLDFDRAWCMD 30 /* send a raw command to the fdc */
+
+#define OLDFDTWADDLE 40 /* flicker motor-on bit before reading a sector */
+
+struct old_floppy_raw_cmd {
+  void *data;
+  long length;
+
+  unsigned char rate;
+  unsigned char flags;
+  unsigned char cmd_count;
+  unsigned char cmd[9];
+  unsigned char reply_count;
+  unsigned char reply[7];
+  int track;
+};
+
+struct old_floppy_fdc_state {
+	int spec1; /* spec1 value last used */
+	int spec2; /* spec2 value last used */
+	int dtr;
+	unsigned char version;  /* FDC version code */
+	unsigned char dor;
+	int address; /* io address */
+	unsigned int rawcmd:2;
+	unsigned int reset:1;
+	unsigned int need_configure:1;
+	unsigned int perp_mode:2;
+	unsigned int has_fifo:1;
+};
+
+
+#include <linux/fd.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+
+#include <linux/ioport.h>
+
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR FLOPPY_MAJOR
+#include "blk.h"
+
+
+/* Dma Memory related stuff */
+
+/* Pure 2^n version of get_order */
+static inline int __get_order (int size)
+{
+	int order;
+
+	for (order = 0; order < NR_MEM_LISTS; ++order)
+		if (size <= (PAGE_SIZE << order))
+			return order;
+	return NR_MEM_LISTS;
+}
+
+static unsigned long dma_mem_alloc(int size)
+{
+	int order = __get_order(size);
+
+	if (order >= NR_MEM_LISTS)
+		return(0);
+	return __get_dma_pages(GFP_KERNEL,order);
+}
+
+/* End dma memory related stuff */
+
+static unsigned int fake_change = 0;
+static int initialising=1;
+
+#define FLOPPY0_TYPE 4
+#define FLOPPY1_TYPE 0
+
+#define N_FDC 2
+#define N_DRIVE 8
+
+static inline int TYPE(kdev_t x) {
+	return  (MINOR(x)>>2) & 0x1f;
+}
+static inline int DRIVE(kdev_t x) {
+	return (MINOR(x)&0x03) | ((MINOR(x)&0x80)>>5);
+}
+#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
+#define UNIT(x) ( (x) & 0x03 )		/* drive on fdc */
+#define FDC(x) ( ((x) & 0x04) >> 2 )  /* fdc of drive */
+#define REVDRIVE(fdc, unit) ( (unit) + ((fdc) << 2 ))
+				/* reverse mapping from unit and fdc to drive */
+#define DP (&drive_params[current_drive])
+#define DRS (&drive_state[current_drive])
+#define DRWE (&write_errors[current_drive])
+#define FDCS (&fdc_state[fdc])
+#define CLEARF(x) (clear_bit(x##_BIT, &DRS->flags))
+#define SETF(x) (set_bit(x##_BIT, &DRS->flags))
+#define TESTF(x) (test_bit(x##_BIT, &DRS->flags))
+
+#define UDP (&drive_params[drive])
+#define UDRS (&drive_state[drive])
+#define UDRWE (&write_errors[drive])
+#define UFDCS (&fdc_state[FDC(drive)])
+#define UCLEARF(x) (clear_bit(x##_BIT, &UDRS->flags))
+#define USETF(x) (set_bit(x##_BIT, &UDRS->flags))
+#define UTESTF(x) (test_bit(x##_BIT, &UDRS->flags))
+
+#define DPRINT(x) printk(DEVICE_NAME "%d: " x,current_drive)
+
+#define DPRINT1(x,x1) printk(DEVICE_NAME "%d: " x,current_drive,(x1))
+
+#define DPRINT2(x,x1,x2) printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2))
+
+#define DPRINT3(x,x1,x2,x3) printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3))
+
+#define PH_HEAD(floppy,head) (((((floppy)->stretch & 2) >>1) ^ head) << 2)
+#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
+
+/* read/write */
+#define COMMAND raw_cmd->cmd[0]
+#define DR_SELECT raw_cmd->cmd[1]
+#define TRACK raw_cmd->cmd[2]
+#define HEAD raw_cmd->cmd[3]
+#define SECTOR raw_cmd->cmd[4]
+#define SIZECODE raw_cmd->cmd[5]
+#define SECT_PER_TRACK raw_cmd->cmd[6]
+#define GAP raw_cmd->cmd[7]
+#define SIZECODE2 raw_cmd->cmd[8]
+#define NR_RW 9
+
+/* format */
+#define F_SIZECODE raw_cmd->cmd[2]
+#define F_SECT_PER_TRACK raw_cmd->cmd[3]
+#define F_GAP raw_cmd->cmd[4]
+#define F_FILL raw_cmd->cmd[5]
+#define NR_F 6
+
+/*
+ * Maximum disk size (in kilobytes). This default is used whenever the
+ * current disk size is unknown.
+ * [Now it is rather a minimum]
+ */
+#define MAX_DISK_SIZE 2 /* 3984*/
+
+
+#define K_64	0x10000		/* 64kB */
+
+/*
+ * 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 CROSS_64KB(a,s) (0)
+#undef LAST_DMA_ADDR
+#define LAST_DMA_ADDR	(0x1)
+
+/*
+ * globals used by 'result()'
+ */
+#define MAX_REPLIES 10
+static unsigned char reply_buffer[MAX_REPLIES];
+static int inr; /* size of reply buffer, when called from interrupt */
+#define ST0 (reply_buffer[0])
+#define ST1 (reply_buffer[1])
+#define ST2 (reply_buffer[2])
+#define ST3 (reply_buffer[0]) /* result of GETSTATUS */
+#define R_TRACK (reply_buffer[3])
+#define R_HEAD (reply_buffer[4])
+#define R_SECTOR (reply_buffer[5])
+#define R_SIZECODE (reply_buffer[6])
+
+#define SEL_DLY (2*HZ/100)
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof( (x)[0] ))
+/*
+ * this struct defines the different floppy drive types.
+ */
+static struct {
+	struct floppy_drive_params params;
+	const char *name; /* name printed while booting */
+} default_drive_params[]= {
+/* NOTE: the time values in jiffies should be in msec!
+ CMOS drive type
+  |     Maximum data rate supported by drive type
+  |     |   Head load time, msec
+  |     |   |   Head unload time, msec (not used)
+  |     |   |   |     Step rate interval, usec
+  |     |   |   |     |       Time needed for spinup time (jiffies)
+  |     |   |   |     |       |      Timeout for spinning down (jiffies)
+  |     |   |   |     |       |      |   Spindown offset (where disk stops)
+  |     |   |   |     |       |      |   |     Select delay
+  |     |   |   |     |       |      |   |     |     RPS
+  |     |   |   |     |       |      |   |     |     |    Max number of tracks
+  |     |   |   |     |       |      |   |     |     |    |     Interrupt timeout
+  |     |   |   |     |       |      |   |     |     |    |     |   Max nonintlv. sectors
+  |     |   |   |     |       |      |   |     |     |    |     |   | -Max Errors- flags */
+{{0,  500, 16, 16, 8000,    1*HZ, 3*HZ,  0, SEL_DLY, 5,  80, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
+
+{{1,  300, 16, 16, 8000,    1*HZ, 3*HZ,  0, SEL_DLY, 5,  40, 3*HZ, 17, {3,1,2,0,2}, 0,
+      0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
+
+{{2,  500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6,  83, 3*HZ, 17, {3,1,2,0,2}, 0,
+      0, { 2, 5, 6,23,10,20,11, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
+
+{{3,  250, 16, 16, 3000,    1*HZ, 3*HZ,  0, SEL_DLY, 5,  83, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
+#if 0
+{{4,  500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5,  83, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
+#else
+{{4,  500, 16, 16, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5,  83, 3*HZ, 20, {3,1,2,0,0}, FTD_MSG,
+      0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
+#endif
+{{5, 1000, 15,  8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5,  83, 3*HZ, 40, {3,1,2,0,2}, 0,
+      0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
+
+{{6, 1000, 15,  8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5,  83, 3*HZ, 40, {3,1,2,0,2}, 0,
+      0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
+/*    |  --autodetected formats---   |      |      |
+ *    read_track                     |      |    Name printed when booting
+ *                                   |     Native format
+ *                                 Frequency of disk change checks */
+};
+
+static struct floppy_drive_params drive_params[N_DRIVE];
+static struct floppy_drive_struct drive_state[N_DRIVE];
+static struct floppy_write_errors write_errors[N_DRIVE];
+static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
+
+/*
+ * This struct defines the different floppy types.
+ *
+ * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
+ * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
+ * tells if the disk is in Commodore 1581 format, which means side 0 sectors
+ * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
+ * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
+ * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
+ * side 0 is on physical side 0 (but with the misnamed sector IDs).
+ * 'stretch' should probably be renamed to something more general, like
+ * 'options'.  Other parameters should be self-explanatory (see also
+ * setfdprm(8)).
+ */
+/*
+             Size
+             |  Sectors per track
+             |  | Head
+             |  | |  Tracks
+             |  | |  | Stretch
+             |  | |  | |  Gap 1 size
+             |  | |  | |    |  Data rate, | 0x40 for perp
+	     |  | |  | |    |    |  Spec1 (stepping rate, head unload
+             |  | |  | |    |    |    |    /fmt gap (gap2) */
+static struct floppy_struct floppy_type[32] = {
+	{    0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL    },	/*  0 no testing    */
+	{  720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360"  }, /*  1 360KB PC      */
+	{ 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" },	/*  2 1.2MB AT      */
+	{  720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360"  },	/*  3 360KB SS 3.5" */
+	{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720"  },	/*  4 720KB 3.5"    */
+	{  720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360"  },	/*  5 360KB AT      */
+	{ 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720"  },	/*  6 720KB AT      */
+	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" },	/*  7 1.44MB 3.5"   */
+	{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" },	/*  8 2.88MB 3.5"   */
+	{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"CompaQ"},	/*  9 2.88MB 3.5"   */
+
+	{ 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25"  */
+	{ 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5"   */
+	{  820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410"  },	/* 12 410KB 5.25"   */
+	{ 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820"  },	/* 13 820KB 3.5"    */
+	{ 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" },	/* 14 1.48MB 5.25"  */
+	{ 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" },	/* 15 1.72MB 3.5"   */
+	{  840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420"  },	/* 16 420KB 5.25"   */
+	{ 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830"  },	/* 17 830KB 3.5"    */
+	{ 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" },	/* 18 1.49MB 5.25"  */
+	{ 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5"  */
+
+	{ 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880"  }, /* 20 880KB 5.25"   */
+	{ 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5"   */
+	{ 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5"   */
+	{ 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25"   */
+	{ 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5"   */
+	{ 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5"   */
+	{ 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5"   */
+	{ 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5"   */
+	{ 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5"   */
+
+	{ 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5"   */
+	{ 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800"  },	/* 30 800KB 3.5"    */
+	{ 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5"    */
+};
+
+#define	NUMBER(x)	(sizeof(x) / sizeof(*(x)))
+#define SECTSIZE ( _FD_SECTSIZE(*floppy))
+
+/* Auto-detection: Disk type used until the next media change occurs. */
+struct floppy_struct *current_type[N_DRIVE] = {
+	NULL, NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL
+};
+
+/*
+ * User-provided type information. current_type points to
+ * the respective entry of this array.
+ */
+struct floppy_struct user_params[N_DRIVE];
+
+static int floppy_sizes[256];
+static int floppy_blocksizes[256] = { 0, };
+
+/*
+ * The driver is trying to determine the correct media format
+ * while probing is set. rw_interrupt() clears it after a
+ * successful access.
+ */
+static int probing = 0;
+
+/* Synchronization of FDC access. */
+#define FD_COMMAND_DETECT -2
+#define FD_COMMAND_NONE -1
+#define FD_COMMAND_ERROR 2
+#define FD_COMMAND_OKAY 3
+
+static volatile int command_status = FD_COMMAND_NONE, fdc_busy = 0;
+static struct wait_queue *fdc_wait = NULL, *command_done = NULL;
+#define NO_SIGNAL (!(current->signal & ~current->blocked) || !interruptible)
+#define CALL(x) if( (x) == -EINTR) return -EINTR
+#define ECALL(x) if ((ret = (x))) return ret;
+#define _WAIT(x,i) CALL(ret=wait_til_done((x),i))
+#define WAIT(x) _WAIT((x),interruptible)
+#define IWAIT(x) _WAIT((x),1)
+
+/* Errors during formatting are counted here. */
+static int format_errors;
+
+/* Format request descriptor. */
+static struct format_descr format_req;
+
+/*
+ * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
+ * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
+ * H is head unload time (1=16ms, 2=32ms, etc)
+ */
+
+/*
+ * Track buffer
+ * Because these are written to by the DMA controller, they must
+ * not contain a 64k byte boundary crossing, or data will be
+ * corrupted/lost. Alignment of these is enforced in boot/head.S.
+ * Note that you must not change the sizes below without updating head.S.
+ */
+char *floppy_track_buffer=NULL;
+int max_buffer_sectors=0;
+
+int *errors;
+typedef void (*done_f)(int);
+struct cont_t {
+	void (*interrupt)(void); /* this is called after the interrupt of the
+				  * main command */
+	void (*redo)(void); /* this is called to retry the operation */
+	void (*error)(void); /* this is called to tally an error */
+	done_f done; /* this is called to say if the operation has
+		      * succeeded/failed */
+} *cont=NULL;
+
+static void floppy_ready(void);
+static void floppy_start(void);
+static void process_fd_request(void);
+static void recalibrate_floppy(void);
+static void floppy_shutdown(void);
+
+static int floppy_grab_irq_and_dma(void);
+static void floppy_release_irq_and_dma(void);
+
+/*
+ * The "reset" variable should be tested whenever an interrupt is scheduled,
+ * after the commands have been sent. This is to ensure that the driver doesn't
+ * get wedged when the interrupt doesn't come because of a failed command.
+ * reset doesn't need to be tested before sending commands, because
+ * output_byte is automatically disabled when reset is set.
+ */
+#define CHECK_RESET { if ( FDCS->reset ){ reset_fdc(); return ; } }
+static void reset_fdc(void);
+
+/*
+ * These are global variables, as that's the easiest way to give
+ * information to interrupts. They are the data used for the current
+ * request.
+ */
+#define NO_TRACK -1
+#define NEED_1_RECAL -2
+#define NEED_2_RECAL -3
+
+/* */
+static int usage_count = 0;
+
+
+/* buffer related variables */
+static int buffer_track = -1;
+static int buffer_drive = -1;
+static int buffer_min = -1;
+static int buffer_max = -1;
+
+/* fdc related variables, should end up in a struct */
+static struct floppy_fdc_state fdc_state[N_FDC];
+static int fdc; /* current fdc */
+
+static struct floppy_struct * floppy = floppy_type;
+static unsigned char current_drive = 0;
+static long current_count_sectors = 0;
+static unsigned char sector_t; /* sector in track */
+
+#ifdef DEBUGT
+long unsigned debugtimer;
+#endif
+
+/*
+ * Floppy_selects1 is the list of DOR's to select a drive n
+ * Floppy_selects2 is the list of DOR's to select drive fd
+ * On initialisation, the floppy list is scanned, and the drives allocated
+ * in the order that they are found.  This is done by seeking the drive
+ * to a non-zero track, and then restoring it to track 0.  If an error occurs,
+ * then there is no floppy drive present.
+ */
+/*extern*/ int no_floppies;
+unsigned char floppy_selects1[]={ 0x10, 0x21, 0x23, 0x33 };
+unsigned char floppy_selects2[]={ 0   , 0   , 0   , 0    };
+
+/*
+ * Debugging
+ * =========
+ */
+static inline void set_debugt(void)
+{
+#ifdef DEBUGT
+	debugtimer = jiffies;
+#endif
+}
+
+static inline void debugt(const char *message)
+{
+#ifdef DEBUGT
+  if ( DP->flags & DEBUGT )
+	printk("%s dtime=%lu\n", message, jiffies-debugtimer );
+#endif
+}
+
+typedef void (*timeout_fn)(unsigned long);
+static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
+					(timeout_fn) floppy_shutdown };
+
+static const char *timeout_message;
+
+#ifdef CONFIG_FLOPPY_SANITY
+static void is_alive(const char *message)
+{
+	/* this routine checks whether the floppy driver is "alive" */
+	if (fdc_busy && command_status < 2 && !fd_timeout.prev){
+		DPRINT1("timeout handler died: %s\n",message);
+	}
+}
+
+
+#define OLOGSIZE 20
+
+void (*lasthandler)(void) = NULL;
+int interruptjiffies=0;
+int resultjiffies=0;
+int resultsize=0;
+int lastredo=0;
+
+static struct output_log {
+	unsigned char data;
+	unsigned char status;
+	unsigned long jiffies;
+} output_log[OLOGSIZE];
+
+static int output_log_pos=0;
+#endif
+
+#define CURRENTD -1
+#define MAXTIMEOUT -2
+
+
+
+static void reschedule_timeout(int drive, const char *message, int marg)
+{
+	if (drive == CURRENTD )
+		drive = current_drive;
+	del_timer(&fd_timeout);
+	if (drive < 0 || drive > N_DRIVE) {
+		fd_timeout.expires = jiffies + 20*HZ;
+		drive = 0;
+	} else
+		fd_timeout.expires = jiffies + UDP->timeout;
+	add_timer(&fd_timeout);
+	if (UDP->flags & FD_DEBUG) {
+		DPRINT("reschedule timeout ");
+		printk(message, marg);
+		printk("\n");
+	}
+	timeout_message = message;
+}
+
+/*
+ * Bottom half floppy driver.
+ * ==========================
+ *
+ * This part of the file contains the code talking directly to the hardware,
+ * and also the main service loop (seek-configure-spinup-command)
+ */
+
+/*
+ * disk change.
+ * This routine is responsible for maintaining the FD_DISK_CHANGE flag,
+ * and the last_checked date.
+ *
+ * last_checked is the date of the last check which showed 'no disk change'
+ * FD_DISK_CHANGE is set under two conditions:
+ * 1. The floppy has been changed after some i/o to that floppy already
+ *    took place.
+ * 2. No floppy disk is in the drive. This is done in order to ensure that
+ *    requests are quickly flushed in case there is no disk in the drive. It
+ *    follows that FD_DISK_CHANGE can only be cleared if there is a disk in
+ *    the drive.
+ *
+ * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
+ * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
+ *  each seek. If a disk is present, the disk change line should also be
+ *  cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
+ *  change line is set, this means either that no disk is in the drive, or
+ *  that it has been removed since the last seek.
+ *
+ * This means that we really have a third possibility too:
+ *  The floppy has been changed after the last seek.
+ */
+
+static int disk_change(int drive)
+{
+	int fdc=FDC(drive);
+#ifdef CONFIG_FLOPPY_SANITY
+	if(jiffies < UDP->select_delay + UDRS->select_date)
+		DPRINT("WARNING disk change called early\n");
+	if(! (FDCS->dor & (0x10 << UNIT(drive))) ||
+	   (FDCS->dor & 3) != UNIT(drive) ||
+	   fdc != FDC(drive)){
+	   	DPRINT("probing disk change on unselected drive\n");
+	   	DPRINT3("drive=%d fdc=%d dor=%x\n",drive, FDC(drive),
+	   		FDCS->dor);
+	}
+#endif
+
+#ifdef DCL_DEBUG
+	if (UDP->flags & FD_DEBUG){
+		DPRINT1("checking disk change line for drive %d\n",drive);
+		DPRINT1("jiffies=%ld\n", jiffies);
+		DPRINT1("disk change line=%x\n", inb_p(FD_DIR)&0x80);
+		DPRINT1("flags=%x\n",UDRS->flags);
+	}
+#endif
+	if (UDP->flags & FD_BROKEN_DCL)
+		return UTESTF(FD_DISK_CHANGED);
+	if( (inb_p(FD_DIR) ^ UDP->flags) & 0x80){
+		USETF(FD_VERIFY); /* verify write protection */
+		if(UDRS->maxblock){
+			/* mark it changed */
+			USETF(FD_DISK_CHANGED);
+
+			/* invalidate its geometry */
+			if (UDRS->keep_data >= 0) {
+				if ((UDP->flags & FTD_MSG) &&
+				    current_type[drive] != NULL)
+					DPRINT("Disk type is undefined after "
+					       "disk change\n");
+				current_type[drive] = NULL;
+				floppy_sizes[TOMINOR(current_drive)] = MAX_DISK_SIZE;
+			}
+		}
+		/*USETF(FD_DISK_NEWCHANGE);*/
+		return 1;
+	} else {
+		UDRS->last_checked=jiffies;
+		UCLEARF(FD_DISK_NEWCHANGE);
+	}
+	return 0;
+}
+
+static inline int is_selected(int dor, int unit)
+{
+	return ( (dor  & (0x10 << unit)) && (dor &3) == unit);
+}
+
+static inline void arm_set_dor(int dor)
+{
+	if(dor & 0xf0)
+		outb_p((dor & 0x0c) | floppy_selects1[dor & 3], FD_DOR);
+	else
+		outb_p((dor & 0x0c), FD_DOR);
+}
+
+static int set_dor(int fdc, char mask, char data)
+{
+	register unsigned char drive, unit, newdor,olddor;
+
+	if (FDCS->address == -1)
+		return -1;
+
+	olddor = FDCS->dor;
+	newdor =  (olddor & mask) | data;
+	if ( newdor != olddor ){
+		unit = olddor & 0x3;
+		if(is_selected(olddor, unit) && !is_selected(newdor, unit)){
+			drive = REVDRIVE(fdc,unit);
+#ifdef DCL_DEBUG
+			if (UDP->flags & FD_DEBUG){
+				DPRINT("calling disk change from set_dor\n");
+			}
+#endif
+			disk_change(drive);
+		}
+		FDCS->dor = newdor;
+		arm_set_dor(newdor);
+
+		unit = newdor & 0x3;
+		if(!is_selected(olddor, unit) && is_selected(newdor, unit)){
+			drive = REVDRIVE(fdc,unit);
+			UDRS->select_date = jiffies;
+		}
+	}
+	if ( newdor & 0xf0 )
+		floppy_grab_irq_and_dma();
+	if( olddor & 0xf0 )
+		floppy_release_irq_and_dma();
+	return olddor;
+}
+
+static void twaddle(void)
+{
+	if (DP->select_delay)
+		return;
+	arm_set_dor(FDCS->dor & ~(0x10<<UNIT(current_drive)));
+	arm_set_dor(FDCS->dor);
+	DRS->select_date = jiffies;
+}
+
+/* reset all driver information about the current fdc. This is needed after
+ * a reset, and after a raw command. */
+static void reset_fdc_info(int mode)
+{
+	int drive;
+
+	FDCS->spec1 = FDCS->spec2 = -1;
+	FDCS->need_configure = 1;
+	FDCS->perp_mode = 1;
+	FDCS->rawcmd = 0;
+	for ( drive = 0; drive < N_DRIVE; drive++)
+		if (FDC(drive) == fdc &&
+		    ( mode || UDRS->track != NEED_1_RECAL))
+			UDRS->track = NEED_2_RECAL;
+}
+
+/* selects the fdc and drive, and enables the fdc's input/dma. */
+static void set_fdc(int drive)
+{
+	if ( drive >= 0 && drive < N_DRIVE){
+		fdc = FDC(drive);
+		current_drive = drive;
+	}
+	set_dor(fdc,~0,8);
+	set_dor(1-fdc, ~8, 0);
+	if ( FDCS->rawcmd == 2 )
+		reset_fdc_info(1);
+	if( inb_p(FD_STATUS) != STATUS_READY )
+		FDCS->reset = 1;
+}
+
+/* locks the driver */
+static int lock_fdc(int drive, int interruptible)
+{
+	if(!usage_count){
+		printk("trying to lock fdc while usage count=0\n");
+		return -1;
+	}
+	floppy_grab_irq_and_dma();
+	cli();
+	while (fdc_busy && NO_SIGNAL)
+		interruptible_sleep_on(&fdc_wait);
+	if(fdc_busy){
+		sti();
+		return -EINTR;
+	}
+	fdc_busy = 1;
+	sti();
+	command_status = FD_COMMAND_NONE;
+	set_fdc(drive);
+	reschedule_timeout(drive, "lock fdc", 0);
+	return 0;
+}
+
+#define LOCK_FDC(drive,interruptible) \
+if(lock_fdc(drive,interruptible)) return -EINTR;
+
+
+/* unlocks the driver */
+static inline void unlock_fdc(void)
+{
+	raw_cmd = NULL;
+	if (!fdc_busy)
+		DPRINT("FDC access conflict!\n");
+
+	if ( DEVICE_INTR )
+		DPRINT1("device interrupt still active at FDC release: %p!\n",
+			DEVICE_INTR);
+	command_status = FD_COMMAND_NONE;
+	del_timer(&fd_timeout);
+	cont = NULL;
+	fdc_busy = 0;
+	floppy_release_irq_and_dma();
+	wake_up(&fdc_wait);
+}
+
+/* switches the motor off after a given timeout */
+static void motor_off_callback(unsigned long nr)
+{
+	unsigned char mask = ~(0x10 << UNIT(nr));
+
+	set_dor( FDC(nr), mask, 0 );
+}
+
+static struct timer_list motor_off_timer[N_DRIVE] = {
+	{ NULL, NULL, 0, 0, motor_off_callback },
+	{ NULL, NULL, 0, 1, motor_off_callback },
+	{ NULL, NULL, 0, 2, motor_off_callback },
+	{ NULL, NULL, 0, 3, motor_off_callback },
+	{ NULL, NULL, 0, 4, motor_off_callback },
+	{ NULL, NULL, 0, 5, motor_off_callback },
+	{ NULL, NULL, 0, 6, motor_off_callback },
+	{ NULL, NULL, 0, 7, motor_off_callback }
+};
+
+/* schedules motor off */
+static void floppy_off(unsigned int drive)
+{
+	unsigned long volatile delta;
+	register int fdc=FDC(drive);
+
+	if( !(FDCS->dor & ( 0x10 << UNIT(drive))))
+		return;
+
+	del_timer(motor_off_timer+drive);
+
+	/* make spindle stop in a position which minimizes spinup time
+	 * next time */
+	if ( UDP->rps ){
+		delta = jiffies - UDRS->first_read_date + HZ -
+			UDP->spindown_offset;
+		delta = (( delta * UDP->rps) % HZ ) / UDP->rps;
+		motor_off_timer[drive].expires = jiffies + UDP->spindown - delta;
+	}
+	add_timer(motor_off_timer+drive);
+}
+
+/*
+ * cycle through all N_DRIVE floppy drives, for disk change testing.
+ * stopping at current drive. This is done before any long operation, to
+ * be sure to have up to date disk change information.
+ */
+static void scandrives(void)
+{
+	int i, drive, saved_drive;
+
+	if (DP->select_delay)
+		return;
+
+	saved_drive = current_drive;
+	for(i=0; i< N_DRIVE; i++){
+		drive = (saved_drive + i + 1 ) % N_DRIVE;
+		if ( UDRS->fd_ref == 0 || UDP->select_delay != 0)
+			continue; /* skip closed drives */
+		set_fdc(drive);
+		if(! (set_dor( fdc, ~3, UNIT(drive) | ( 0x10 << UNIT(drive))) &
+		      (0x10 << UNIT(drive))))
+			/* switch the motor off again, if it was off to
+			 * begin with */
+			set_dor( fdc, ~( 0x10 << UNIT(drive) ), 0 );
+	}
+	set_fdc(saved_drive);
+}
+
+static struct timer_list fd_timer ={ NULL, NULL, 0, 0, 0 };
+
+/* this function makes sure that the disk stays in the drive during the
+ * transfer */
+static void fd_watchdog(void)
+{
+#ifdef DCL_DEBUG
+	if (DP->flags & FD_DEBUG){
+		DPRINT("calling disk change from watchdog\n");
+	}
+#endif
+
+	if ( disk_change(current_drive) ){
+		DPRINT("disk removed during i/o\n");
+		floppy_shutdown();
+	} else {
+		del_timer(&fd_timer);
+		fd_timer.function = (timeout_fn) fd_watchdog;
+		fd_timer.expires = jiffies + HZ / 10;
+		add_timer(&fd_timer);
+	}
+}
+
+static void main_command_interrupt(void)
+{
+	del_timer(&fd_timer);
+	cont->interrupt();
+}
+
+/* waits for a delay (spinup or select) to pass */
+static int wait_for_completion(int delay, timeout_fn function)
+{
+	if ( FDCS->reset ){
+		reset_fdc(); /* do the reset during sleep to win time
+			      * if we don't need to sleep, it's a good
+			      * occasion anyways */
+		return 1;
+	}
+
+	if ( jiffies < delay ){
+		del_timer(&fd_timer);
+		fd_timer.function = function;
+		fd_timer.expires = delay;
+		add_timer(&fd_timer);
+		return 1;
+	}
+	return 0;
+}
+
+static int hlt_disabled=0;
+static void floppy_disable_hlt(void)
+{
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	if(!hlt_disabled){
+		hlt_disabled=1;
+#ifdef HAVE_DISABLE_HLT
+		disable_hlt();
+#endif
+	}
+	restore_flags(flags);
+}
+
+static void floppy_enable_hlt(void)
+{
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	if(hlt_disabled){
+		hlt_disabled=0;
+#ifdef HAVE_DISABLE_HLT
+		enable_hlt();
+#endif
+	}
+	restore_flags(flags);
+}
+
+
+static void setup_DMA(void)
+{
+#ifdef CONFIG_FLOPPY_SANITY
+	if (raw_cmd->length == 0){
+		int i;
+
+		printk("zero dma transfer size:");
+		for(i=0; i< raw_cmd->cmd_count; i++)
+			printk("%x,", raw_cmd->cmd[i]);
+		printk("\n");
+		cont->done(0);
+		FDCS->reset = 1;
+		return;
+	}
+#if 0
+	/* disabled because of new buffer location for raw cmd */
+	if ((!CURRENT ||
+	     CURRENT->buffer != raw_cmd->kernel_data ||
+	     raw_cmd->length > 512 * CURRENT->nr_sectors) &&
+	    (raw_cmd->kernel_data < floppy_track_buffer ||
+	     raw_cmd->kernel_data + raw_cmd->length >
+	     floppy_track_buffer + 1024 * max_buffer_sectors)){
+		printk("bad address. start=%p lg=%lx tb=%p\n",
+		       raw_cmd->kernel_data, raw_cmd->length,
+		       floppy_track_buffer);
+		if ( CURRENT ){
+			printk("buffer=%p nr=%lx cnr=%lx\n",
+			       CURRENT->buffer, CURRENT->nr_sectors,
+			       CURRENT->current_nr_sectors);
+		}
+		cont->done(0);
+		FDCS->reset=1;
+		return;
+	}
+	if ((long) raw_cmd->kernel_data % 512 ){
+		printk("non aligned address: %p\n", raw_cmd->kernel_data );
+		cont->done(0);
+		FDCS->reset=1;
+		return;
+	}
+	if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) {
+		printk("DMA crossing 64-K boundary %p-%p\n",
+		       raw_cmd->kernel_data,
+		       raw_cmd->kernel_data + raw_cmd->length);
+		cont->done(0);
+		FDCS->reset=1;
+		return;
+	}
+#endif
+#endif
+	cli();
+	disable_dma(FLOPPY_DMA);
+	clear_dma_ff(FLOPPY_DMA);
+	set_dma_mode(FLOPPY_DMA,
+		     (raw_cmd->flags & FD_RAW_READ)?
+		     DMA_MODE_READ : DMA_MODE_WRITE);
+	set_dma_addr(FLOPPY_DMA, virt_to_bus(raw_cmd->kernel_data));
+	set_dma_count(FLOPPY_DMA, raw_cmd->length);
+	enable_dma(FLOPPY_DMA);
+	sti();
+	floppy_disable_hlt();
+}
+
+/* sends a command byte to the fdc */
+static int output_byte(char byte)
+{
+	int counter;
+	unsigned char status=0;
+	unsigned char rstatus;
+
+	if (FDCS->reset)
+		return -1;
+	for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
+		rstatus = inb_p(FD_STATUS);
+		status = rstatus &(STATUS_READY|STATUS_DIR|STATUS_DMA);
+		if (!(status & STATUS_READY))
+			continue;
+		if (status == STATUS_READY){
+			outb_p(byte,FD_DATA);
+
+#ifdef CONFIG_FLOPPY_SANITY
+			output_log[output_log_pos].data = byte;
+			output_log[output_log_pos].status = rstatus;
+			output_log[output_log_pos].jiffies = jiffies;
+			output_log_pos = (output_log_pos + 1) % OLOGSIZE;
+#endif
+			return 0;
+		} else
+			break;
+	}
+	FDCS->reset = 1;
+	if ( !initialising )
+		DPRINT2("Unable to send byte %x to FDC. Status=%x\n",
+			byte, status);
+	return -1;
+}
+#define LAST_OUT(x) if(output_byte(x)){ reset_fdc();return;}
+
+/* gets the response from the fdc */
+static int result(void)
+{
+	int i = 0, counter, status = 0;
+
+	if (FDCS->reset)
+		return -1;
+	for (counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
+		status = inb_p(FD_STATUS)&
+			(STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA);
+		if (!(status & STATUS_READY))
+			continue;
+		if (status == STATUS_READY){
+#ifdef CONFIG_FLOPPY_SANITY
+			resultjiffies = jiffies;
+			resultsize = i;
+#endif
+			return i;
+		}
+		if (status & STATUS_DMA )
+			break;
+		if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
+			if (i >= MAX_REPLIES) {
+				DPRINT("floppy_stat reply overrun\n");
+				break;
+			}
+			reply_buffer[i++] = inb_p(FD_DATA);
+		}
+	}
+	FDCS->reset = 1;
+	if ( !initialising )
+		DPRINT3("Getstatus times out (%x) on fdc %d [%d]\n",
+			status, fdc, i);
+	return -1;
+}
+
+/* Set perpendicular mode as required, based on data rate, if supported.
+ * 82077 Now tested. 1Mbps data rate only possible with 82077-1.
+ */
+static inline void perpendicular_mode(void)
+{
+	unsigned char perp_mode;
+
+	if (!floppy)
+		return;
+	if (floppy->rate & 0x40){
+		switch(raw_cmd->rate){
+			case 0:
+				perp_mode=2;
+				break;
+			case 3:
+				perp_mode=3;
+				break;
+			default:
+				DPRINT("Invalid data rate for perpendicular mode!\n");
+				cont->done(0);
+				FDCS->reset = 1; /* convenient way to return to
+						  * redo without to much hassle (deep
+						  * stack et al. */
+				return;
+		}
+	} else
+		perp_mode = 0;
+
+	if ( FDCS->perp_mode == perp_mode )
+		return;
+	if (FDCS->version >= FDC_82077_ORIG && FDCS->has_fifo) {
+		output_byte(FD_PERPENDICULAR);
+		output_byte(perp_mode);
+		FDCS->perp_mode = perp_mode;
+	} else if (perp_mode) {
+		DPRINT("perpendicular mode not supported by this FDC.\n");
+	}
+} /* perpendicular_mode */
+
+#define NOMINAL_DTR 500
+
+/* Issue a "SPECIFY" command to set the step rate time, head unload time,
+ * head load time, and DMA disable flag to values needed by floppy.
+ *
+ * The value "dtr" is the data transfer rate in Kbps.  It is needed
+ * to account for the data rate-based scaling done by the 82072 and 82077
+ * FDC types.  This parameter is ignored for other types of FDCs (i.e.
+ * 8272a).
+ *
+ * Note that changing the data transfer rate has a (probably deleterious)
+ * effect on the parameters subject to scaling for 82072/82077 FDCs, so
+ * fdc_specify is called again after each data transfer rate
+ * change.
+ *
+ * srt: 1000 to 16000 in microseconds
+ * hut: 16 to 240 milliseconds
+ * hlt: 2 to 254 milliseconds
+ *
+ * These values are rounded up to the next highest available delay time.
+ */
+static void fdc_specify(void)
+{
+	unsigned char spec1, spec2;
+	int srt, hlt, hut;
+	unsigned long dtr = NOMINAL_DTR;
+	unsigned long scale_dtr = NOMINAL_DTR;
+	int hlt_max_code = 0x7f;
+	int hut_max_code = 0xf;
+
+	if (FDCS->need_configure && FDCS->has_fifo) {
+		if ( FDCS->reset )
+			return;
+		/* Turn on FIFO for 82077-class FDC (improves performance) */
+		/* TODO: lock this in via LOCK during initialization */
+		output_byte(FD_CONFIGURE);
+		output_byte(0);
+		output_byte(0x2A);	/* FIFO on, polling off, 10 byte threshold */
+		output_byte(0);		/* precompensation from track 0 upwards */
+		if ( FDCS->reset ){
+			FDCS->has_fifo=0;
+			return;
+		}
+		FDCS->need_configure = 0;
+		/*DPRINT("FIFO enabled\n");*/
+	}
+
+	switch (raw_cmd->rate & 0x03) {
+		case 3:
+			dtr = 1000;
+			break;
+		case 1:
+			dtr = 300;
+			break;
+		case 2:
+			dtr = 250;
+			break;
+	}
+
+	if (FDCS->version >= FDC_82072) {
+		scale_dtr = dtr;
+		hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */
+		hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */
+	}
+
+	/* Convert step rate from microseconds to milliseconds and 4 bits */
+	srt = 16 - (DP->srt*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+	if (srt > 0xf)
+		srt = 0xf;
+	else if (srt < 0)
+		srt = 0;
+
+	hlt = (DP->hlt*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+	if (hlt < 0x01)
+		hlt = 0x01;
+	else if (hlt > 0x7f)
+		hlt = hlt_max_code;
+
+	hut = (DP->hut*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+	if (hut < 0x1)
+		hut = 0x1;
+	else if (hut > 0xf)
+		hut = hut_max_code;
+
+	spec1 = (srt << 4) | hut;
+	spec2 = (hlt << 1);
+
+	/* If these parameters did not change, just return with success */
+	if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) {
+		/* Go ahead and set spec1 and spec2 */
+		output_byte(FD_SPECIFY);
+		output_byte(FDCS->spec1 = spec1);
+		output_byte(FDCS->spec2 = spec2);
+	}
+} /* fdc_specify */
+
+/* Set the FDC's data transfer rate on behalf of the specified drive.
+ * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
+ * of the specify command (i.e. using the fdc_specify function).
+ */
+static int fdc_dtr(void)
+{
+	/* If data rate not already set to desired value, set it. */
+	if ( raw_cmd->rate == FDCS->dtr)
+		return 0;
+
+	/* Set dtr */
+	outb_p(raw_cmd->rate, FD_DCR);
+
+	/* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
+	 * need a stabilization period of several milliseconds to be
+	 * enforced after data rate changes before R/W operations.
+	 * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
+	 */
+	FDCS->dtr = raw_cmd->rate;
+	return(wait_for_completion(jiffies+2*HZ/100,
+				   (timeout_fn) floppy_ready));
+} /* fdc_dtr */
+
+static void tell_sector(void)
+{
+	printk(": track %d, head %d, sector %d, size %d",
+	       R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE);
+} /* tell_sector */
+
+
+/*
+ * Ok, this error interpreting routine is called after a
+ * DMA read/write has succeeded
+ * or failed, so we check the results, and copy any buffers.
+ * hhb: Added better error reporting.
+ * ak: Made this into a separate routine.
+ */
+static int interpret_errors(void)
+{
+	char bad;
+int res = get_dma_residue(FLOPPY_DMA);
+if(res) {printk("\n-- DMA residue (%d)",res); tell_sector(); printk("\n");}
+	if (inr!=7) {
+		DPRINT("-- FDC reply error");
+		FDCS->reset = 1;
+		return 1;
+	}
+
+	/* check IC to find cause of interrupt */
+	switch (ST0 & ST0_INTR) {
+		case 0x40:	/* error occurred during command execution */
+			bad = 1;
+			if (ST1 & ST1_WP) {
+				DPRINT("Drive is write protected\n");
+				CLEARF(FD_DISK_WRITABLE);
+				cont->done(0);
+				bad = 2;
+			} else if (ST1 & ST1_ND) {
+				SETF(FD_NEED_TWADDLE);
+			} else if (ST1 & ST1_OR) {
+				if (DP->flags & FTD_MSG )
+					DPRINT("Over/Underrun - retrying\n");
+				bad = 0;
+			}else if(*errors >= DP->max_errors.reporting){
+				DPRINT("");
+				if (ST0 & ST0_ECE) {
+					printk("Recalibrate failed!");
+				} else if (ST2 & ST2_CRC) {
+					printk("data CRC error");
+					tell_sector();
+				} else if (ST1 & ST1_CRC) {
+					printk("CRC error");
+					tell_sector();
+				} else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) {
+					if (!probing) {
+						printk("sector not found");
+						tell_sector();
+					} else
+						printk("probe failed...");
+				} else if (ST2 & ST2_WC) {	/* seek error */
+					printk("wrong cylinder");
+				} else if (ST2 & ST2_BC) {	/* cylinder marked as bad */
+					printk("bad cylinder");
+				} else {
+					printk("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x", ST0, ST1, ST2);
+					tell_sector();
+				}
+				printk("\n");
+
+			}
+			if ( ST2 & ST2_WC || ST2 & ST2_BC)
+				/* wrong cylinder => recal */
+				DRS->track = NEED_2_RECAL;
+			return bad;
+		case 0x80: /* invalid command given */
+			DPRINT("Invalid FDC command given!\n");
+			cont->done(0);
+			return 2;
+		case 0xc0:
+			DPRINT("Abnormal termination caused by polling\n");
+			cont->error();
+			return 2;
+		default: /* (0) Normal command termination */
+			return 0;
+	}
+}
+
+/*
+ * This routine is called when everything should be correctly set up
+ * for the transfer (ie floppy motor is on, the correct floppy is
+ * selected, and the head is sitting on the right track).
+ */
+static void setup_rw_floppy(void)
+{
+	int i,ready_date,r, flags,dflags;
+	timeout_fn function;
+
+	flags = raw_cmd->flags;
+	if ( flags & ( FD_RAW_READ | FD_RAW_WRITE))
+		flags |= FD_RAW_INTR;
+
+	if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)){
+		ready_date = DRS->spinup_date + DP->spinup;
+		/* If spinup will take a long time, rerun scandrives
+		 * again just before spinup completion. Beware that
+		 * after scandrives, we must again wait for selection.
+		 */
+		if ( ready_date > jiffies + DP->select_delay){
+			ready_date -= DP->select_delay;
+			function = (timeout_fn) floppy_start;
+		} else
+			function = (timeout_fn) setup_rw_floppy;
+
+		/* wait until the floppy is spinning fast enough */
+		if (wait_for_completion(ready_date,function))
+			return;
+	}
+	dflags = DRS->flags;
+
+	if ( (flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
+		setup_DMA();
+
+	if ( flags & FD_RAW_INTR )
+		SET_INTR(main_command_interrupt);
+
+	r=0;
+	for(i=0; i< raw_cmd->cmd_count; i++)
+		r|=output_byte( raw_cmd->cmd[i] );
+
+#ifdef DEBUGT
+	debugt("rw_command: ");
+#endif
+	if ( r ){
+		reset_fdc();
+		return;
+	}
+
+	if ( ! ( flags & FD_RAW_INTR )){
+		inr = result();
+		cont->interrupt();
+	} else if ( flags & FD_RAW_NEED_DISK )
+		fd_watchdog();
+}
+
+static int blind_seek;
+
+/*
+ * This is the routine called after every seek (or recalibrate) interrupt
+ * from the floppy controller.
+ */
+static void seek_interrupt(void)
+{
+#ifdef DEBUGT
+	debugt("seek interrupt:");
+#endif
+	if (inr != 2 || (ST0 & 0xF8) != 0x20 ) {
+		DPRINT("seek failed\n");
+		DRS->track = NEED_2_RECAL;
+		cont->error();
+		cont->redo();
+		return;
+	}
+	if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek){
+#ifdef DCL_DEBUG
+		if (DP->flags & FD_DEBUG){
+			DPRINT("clearing NEWCHANGE flag because of effective seek\n");
+			DPRINT1("jiffies=%ld\n", jiffies);
+		}
+#endif
+		CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
+		DRS->select_date = jiffies;
+	}
+	DRS->track = ST1;
+	floppy_ready();
+}
+
+static void check_wp(void)
+{
+	if (TESTF(FD_VERIFY)) {
+		/* check write protection */
+		output_byte( FD_GETSTATUS );
+		output_byte( UNIT(current_drive) );
+		if ( result() != 1 ){
+			FDCS->reset = 1;
+			return;
+		}
+		CLEARF(FD_VERIFY);
+		CLEARF(FD_NEED_TWADDLE);
+#ifdef DCL_DEBUG
+		if (DP->flags & FD_DEBUG){
+			DPRINT("checking whether disk is write protected\n");
+			DPRINT1("wp=%x\n",ST3 & 0x40);
+		}
+#endif
+		if (!( ST3  & 0x40))
+			SETF(FD_DISK_WRITABLE);
+		else
+			CLEARF(FD_DISK_WRITABLE);
+	}
+}
+
+static void seek_floppy(void)
+{
+	int track;
+
+	blind_seek=0;
+
+#ifdef DCL_DEBUG
+	if (DP->flags & FD_DEBUG){
+		DPRINT("calling disk change from seek\n");
+	}
+#endif
+
+	if (!TESTF(FD_DISK_NEWCHANGE) &&
+	    disk_change(current_drive) &&
+	    (raw_cmd->flags & FD_RAW_NEED_DISK)){
+		/* the media changed flag should be cleared after the seek.
+		 * If it isn't, this means that there is really no disk in
+		 * the drive.
+		 */
+		SETF(FD_DISK_CHANGED);
+		cont->done(0);
+		cont->redo();
+		return;
+	}
+	if ( DRS->track <= NEED_1_RECAL ){
+		recalibrate_floppy();
+		return;
+	} else if (TESTF(FD_DISK_NEWCHANGE) &&
+		   (raw_cmd->flags & FD_RAW_NEED_DISK) &&
+		   (DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) {
+		/* we seek to clear the media-changed condition. Does anybody
+		 * know a more elegant way, which works on all drives? */
+		if ( raw_cmd->track )
+			track = raw_cmd->track - 1;
+		else {
+			if(DP->flags & FD_SILENT_DCL_CLEAR){
+				set_dor(fdc, ~ (0x10 << UNIT(current_drive)), 0);
+				blind_seek = 1;
+				raw_cmd->flags |= FD_RAW_NEED_SEEK;
+			}
+			track = 1;
+		}
+	} else {
+		check_wp();
+		if (raw_cmd->track != DRS->track &&
+		    (raw_cmd->flags & FD_RAW_NEED_SEEK))
+			track = raw_cmd->track;
+		else {
+			setup_rw_floppy();
+			return;
+		}
+	}
+
+	SET_INTR(seek_interrupt);
+	output_byte(FD_SEEK);
+	output_byte(UNIT(current_drive));
+	LAST_OUT(track);
+#ifdef DEBUGT
+	debugt("seek command:");
+#endif
+}
+
+static void recal_interrupt(void)
+{
+#ifdef DEBUGT
+	debugt("recal interrupt:");
+#endif
+	if (inr !=2 )
+		FDCS->reset = 1;
+	else if (ST0 & ST0_ECE) {
+	       	switch(DRS->track){
+		       	case NEED_1_RECAL:
+#ifdef DEBUGT
+				debugt("recal interrupt need 1 recal:");
+#endif
+				/* after a second recalibrate, we still haven't
+				 * reached track 0. Probably no drive. Raise an
+				 * error, as failing immediately might upset
+				 * computers possessed by the Devil :-) */
+				cont->error();
+				cont->redo();
+				return;
+			case NEED_2_RECAL:
+#ifdef DEBUGT
+				debugt("recal interrupt need 2 recal:");
+#endif
+				/* If we already did a recalibrate,
+				 * and we are not at track 0, this
+				 * means we have moved. (The only way
+				 * not to move at recalibration is to
+				 * be already at track 0.) Clear the
+				 * new change flag */
+#ifdef DCL_DEBUG
+				if (DP->flags & FD_DEBUG){
+					DPRINT("clearing NEWCHANGE flag because of second recalibrate\n");
+				}
+#endif
+
+				CLEARF(FD_DISK_NEWCHANGE);
+				DRS->select_date = jiffies;
+				/* fall through */
+			default:
+#ifdef DEBUGT
+				debugt("recal interrupt default:");
+#endif
+				/* Recalibrate moves the head by at
+				 * most 80 steps. If after one
+				 * recalibrate we don't have reached
+				 * track 0, this might mean that we
+				 * started beyond track 80.  Try
+				 * again.  */
+				DRS->track = NEED_1_RECAL;
+				break;
+		}
+	} else
+		DRS->track = ST1;
+	floppy_ready();
+}
+
+/*
+ * Unexpected interrupt - Print as much debugging info as we can...
+ * All bets are off...
+ */
+static void unexpected_floppy_interrupt(void)
+{
+	int i;
+	if ( initialising )
+		return;
+	if(print_unex){
+		DPRINT("unexpected interrupt\n");
+		if ( inr >= 0 )
+			for(i=0; i<inr; i++)
+				printk("%d %x\n", i, reply_buffer[i] );
+	}
+	while(1){
+		output_byte(FD_SENSEI);
+		inr=result();
+		if ( inr != 2 )
+			break;
+		if(print_unex){
+			printk("sensei\n");
+			for(i=0; i<inr; i++)
+				printk("%d %x\n", i, reply_buffer[i] );
+		}
+	}
+	FDCS->reset = 1;
+}
+
+struct tq_struct floppy_tq =
+{ 0, 0, (void *) (void *) unexpected_floppy_interrupt, 0 };
+
+/* interrupt handler */
+static void floppy_interrupt(int irq, struct pt_regs *regs)
+{
+	void (*handler)(void) = DEVICE_INTR;
+
+	lasthandler = handler;
+	interruptjiffies = jiffies;
+
+	floppy_enable_hlt();
+	CLEAR_INTR;
+	if ( fdc >= N_FDC || FDCS->address == -1){
+		/* we don't even know which FDC is the culprit */
+		printk("DOR0=%x\n", fdc_state[0].dor);
+		printk("floppy interrupt on bizarre fdc %d\n",fdc);
+		printk("handler=%p\n", handler);
+		is_alive("bizarre fdc");
+		return;
+	}
+	inr = result();
+	if (!handler){
+		unexpected_floppy_interrupt();
+		is_alive("unexpected");
+		return;
+	}
+	if ( inr == 0 ){
+		do {
+			output_byte(FD_SENSEI);
+			inr = result();
+		} while ( (ST0 & 0x83) != UNIT(current_drive) && inr == 2);
+	}
+	floppy_tq.routine = (void *)(void *) handler;
+	queue_task_irq(&floppy_tq, &tq_timer);
+	is_alive("normal interrupt end");
+}
+
+static void recalibrate_floppy(void)
+{
+#ifdef DEBUGT
+	debugt("recalibrate floppy:");
+#endif
+	SET_INTR(recal_interrupt);
+	output_byte(FD_RECALIBRATE);
+	LAST_OUT(UNIT(current_drive));
+}
+
+/*
+ * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
+ */
+static void reset_interrupt(void)
+{
+#ifdef DEBUGT
+	debugt("reset interrupt:");
+#endif
+	/* fdc_specify();	   reprogram fdc */
+	result();		/* get the status ready for set_fdc */
+	if ( FDCS->reset )
+		cont->error(); /* a reset just after a reset. BAD! */
+	cont->redo();
+}
+
+/*
+ * reset is done by pulling bit 2 of DOR low for a while (old FDC's),
+ * or by setting the self clearing bit 7 of STATUS (newer FDC's)
+ */
+static void reset_fdc(void)
+{
+	SET_INTR(reset_interrupt);
+	FDCS->reset = 0;
+	reset_fdc_info(0);
+	if ( FDCS->version >= FDC_82077 )
+		outb_p(0x80 | ( FDCS->dtr &3), FD_STATUS);
+	else {
+		arm_set_dor(FDCS->dor & ~0x04);
+		udelay(FD_RESET_DELAY);
+		arm_set_dor(FDCS->dor);
+	}
+}
+
+static void empty(void)
+{
+}
+
+void show_floppy(void)
+{
+	int i;
+
+	printk("\n");
+	printk("floppy driver state\n");
+	printk("-------------------\n");
+	printk("now=%ld last interrupt=%d last called handler=%p\n",
+		jiffies, interruptjiffies, lasthandler);
+
+
+#ifdef CONFIG_FLOPPY_SANITY
+	printk("timeout_message=%s\n", timeout_message);
+	printk("last output bytes:\n");
+	for(i=0; i < OLOGSIZE; i++)
+		printk("%2x %2x %ld\n",
+			output_log[(i+output_log_pos) % OLOGSIZE].data,
+			output_log[(i+output_log_pos) % OLOGSIZE].status,
+			output_log[(i+output_log_pos) % OLOGSIZE].jiffies);
+	printk("last result at %d\n", resultjiffies);
+	printk("last redo_fd_request at %d\n", lastredo);
+	for(i=0; i<resultsize; i++){
+		printk("%2x ", reply_buffer[i]);
+	}
+	printk("\n");
+#endif
+
+#if 0
+	for(i=0; i<N_FDC; i++){
+		if(FDCS->address != -1){
+			printk("dor %d = %x\n", i, fdc_state[i].dor );
+			outb_p(fdc_state[i].address+2, fdc_state[i].dor);
+			udelay(1000); /* maybe we'll catch an interrupt... */
+		}
+	}
+#endif
+	printk("status=%x\n", inb_p(FD_STATUS));
+	printk("fdc_busy=%d\n", fdc_busy);
+	if( DEVICE_INTR)
+		printk("DEVICE_INTR=%p\n", DEVICE_INTR);
+	if(floppy_tq.sync)
+		printk("floppy_tq.routine=%p\n", floppy_tq.routine);
+	if(fd_timer.prev)
+		printk("fd_timer.function=%p\n", fd_timer.function);
+	if(fd_timeout.prev){
+		printk("timer_table=%p\n",fd_timeout.function);
+		printk("expires=%ld\n",fd_timeout.expires-jiffies);
+		printk("now=%ld\n",jiffies);
+	}
+	printk("cont=%p\n", cont);
+	printk("CURRENT=%p\n", CURRENT);
+	printk("command_status=%d\n", command_status);
+	printk("\n");
+}
+
+static void floppy_shutdown(void)
+{
+	if (!initialising)
+		show_floppy();
+	CLEAR_INTR;
+	floppy_tq.routine = (void *)(void *) empty;
+	del_timer( &fd_timer);
+	sti();
+
+	floppy_enable_hlt();
+	disable_dma(FLOPPY_DMA);
+	/* avoid dma going to a random drive after shutdown */
+	
+	if(!initialising)
+		DPRINT("floppy timeout\n");
+	FDCS->reset = 1;
+	if (cont){
+		cont->done(0);
+		cont->redo(); /* this will recall reset when needed */
+	} else {
+		printk("no cont in shutdown!\n");
+		process_fd_request();
+	}
+	is_alive("floppy shutdown");
+}
+/*typedef void (*timeout_fn)(unsigned long);*/
+
+/* start motor, check media-changed condition and write protection */
+static int start_motor( void (*function)(void)  )
+{
+	int mask, data;
+
+	mask = 0xfc;
+	data = UNIT(current_drive);
+	if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)){
+		if(!(FDCS->dor & ( 0x10 << UNIT(current_drive) ) )){
+			set_debugt();
+			/* no read since this drive is running */
+			DRS->first_read_date = 0;
+			/* note motor start time if motor is not yet running */
+			DRS->spinup_date = jiffies;
+			data |= (0x10 << UNIT(current_drive));
+		}
+	} else
+		if (FDCS->dor & ( 0x10 << UNIT(current_drive) ) )
+			mask &= ~(0x10 << UNIT(current_drive));
+
+	/* starts motor and selects floppy */
+	del_timer(motor_off_timer + current_drive);
+	set_dor( fdc, mask, data);
+
+	/* wait_for_completion also schedules reset if needed. */
+	return(wait_for_completion(DRS->select_date+DP->select_delay,
+				   (timeout_fn) function));
+}
+
+static void floppy_ready(void)
+{
+	CHECK_RESET;
+	if(start_motor(floppy_ready)) return;
+	if(fdc_dtr()) return;
+
+#ifdef DCL_DEBUG
+	if (DP->flags & FD_DEBUG){
+		DPRINT("calling disk change from floppy_ready\n");
+	}
+#endif
+
+	if(!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
+	   disk_change(current_drive) &&
+	   !DP->select_delay)
+	   	twaddle(); /* this clears the dcl on certain drive/controller
+	   		    * combinations */
+
+	if ( raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
+		perpendicular_mode();
+		fdc_specify(); /* must be done here because of hut, hlt ... */
+		seek_floppy();
+	} else
+		setup_rw_floppy();
+}
+
+static void floppy_start(void)
+{
+	reschedule_timeout(CURRENTD, "floppy start", 0);
+
+	scandrives();
+#ifdef DCL_DEBUG
+	if (DP->flags & FD_DEBUG){
+		DPRINT("setting NEWCHANGE in floppy_start\n");
+	}
+#endif
+	SETF(FD_DISK_NEWCHANGE);
+	floppy_ready();
+}
+
+/*
+ * ========================================================================
+ * here ends the bottom half. Exported routines are:
+ * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
+ * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
+ * Initialisation also uses output_byte, result, set_dor, floppy_interrupt
+ * and set_dor.
+ * ========================================================================
+ */
+/*
+ * General purpose continuations.
+ * ==============================
+ */
+
+static void do_wakeup(void)
+{
+	reschedule_timeout(MAXTIMEOUT, "do wakeup", 0);
+	cont = 0;
+	command_status += 2;
+	wake_up(&command_done);
+}
+
+static struct cont_t wakeup_cont={
+	empty,
+	do_wakeup,
+	empty,
+	(done_f)empty
+};
+
+static int wait_til_done( void (*handler)(void ), int interruptible )
+{
+	int ret;
+
+	floppy_tq.routine = (void *)(void *) handler;
+	queue_task(&floppy_tq, &tq_timer);
+
+	cli();
+	while(command_status < 2 && NO_SIGNAL){
+		is_alive("wait_til_done");
+		if (interruptible)
+			interruptible_sleep_on(&command_done);
+		else
+			sleep_on(&command_done);
+	}
+	if(command_status < 2){
+		floppy_shutdown();
+		sti();
+		process_fd_request();
+		return -EINTR;
+	}
+	sti();
+
+	if ( FDCS->reset )
+		command_status = FD_COMMAND_ERROR;
+	if ( command_status == FD_COMMAND_OKAY )
+		ret=0;
+	else
+		ret=-EIO;
+	command_status = FD_COMMAND_NONE;
+	return ret;
+}
+
+static void generic_done(int result)
+{
+	command_status = result;
+	cont = &wakeup_cont;
+}
+
+static void generic_success(void)
+{
+	cont->done(1);
+}
+
+static void generic_failure(void)
+{
+	cont->done(0);
+}
+
+static void success_and_wakeup(void)
+{
+	generic_success();
+	cont->redo();
+}
+
+
+/*
+ * formatting and rw support.
+ * ==========================
+ */
+
+static int next_valid_format(void)
+{
+	int probed_format;
+
+	probed_format = DRS->probed_format;
+	while(1){
+		if ( probed_format >= 8 ||
+		    ! DP->autodetect[probed_format] ){
+			DRS->probed_format = 0;
+			return 1;
+		}
+		if ( floppy_type[DP->autodetect[probed_format]].sect ){
+			DRS->probed_format = probed_format;
+			return 0;
+		}
+		probed_format++;
+	}
+}
+
+static void bad_flp_intr(void)
+{
+	if ( probing ){
+		DRS->probed_format++;
+		if ( !next_valid_format())
+			return;
+	}
+	(*errors)++;
+	if (*errors > DRWE->badness)
+	        DRWE->badness = *errors;
+	if (*errors > DP->max_errors.abort)
+		cont->done(0);
+	if (*errors > DP->max_errors.reset)
+		FDCS->reset = 1;
+	else if (*errors > DP->max_errors.recal)
+		DRS->track = NEED_2_RECAL;
+}
+
+static void set_floppy(kdev_t device)
+{
+	if (TYPE(device))
+		floppy = TYPE(device) + floppy_type;
+	else
+		floppy = current_type[ DRIVE(device) ];
+}
+
+/*
+ * formatting and support.
+ * =======================
+ */
+static void format_interrupt(void)
+{
+	switch (interpret_errors()){
+		case 1:
+			cont->error();
+		case 2:
+			break;
+		case 0:
+			cont->done(1);
+	}
+	cont->redo();
+}
+
+#define CODE2SIZE (ssize = ( ( 1 << SIZECODE ) + 3 ) >> 2)
+#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80 ) >>1))
+#define CT(x) ( (x) | 0x40 )
+static void setup_format_params(int track)
+{
+	struct fparm {
+		unsigned char track,head,sect,size;
+	} *here = (struct fparm *)floppy_track_buffer;
+	int il,n;
+	int count,head_shift,track_shift;
+
+	raw_cmd = &default_raw_cmd;
+	raw_cmd->track = track;
+	
+	raw_cmd->flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
+		/*FD_RAW_NEED_DISK |*/ FD_RAW_NEED_SEEK;
+	raw_cmd->rate = floppy->rate & 0x3;
+	raw_cmd->cmd_count = NR_F;
+	COMMAND = FM_MODE(floppy,FD_FORMAT);
+	DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,format_req.head);
+	F_SIZECODE = FD_SIZECODE(floppy);
+	F_SECT_PER_TRACK = floppy->sect << 2 >> F_SIZECODE;
+	F_GAP = floppy->fmt_gap;
+	F_FILL = FD_FILL_BYTE;
+
+	raw_cmd->kernel_data = floppy_track_buffer;
+	raw_cmd->length = 4 * F_SECT_PER_TRACK;
+
+	/* allow for about 30ms for data transport per track */
+	head_shift  = (F_SECT_PER_TRACK + 5) / 6;
+
+	/* a ``cylinder'' is two tracks plus a little stepping time */
+	track_shift = 2 * head_shift + 3;
+
+	/* position of logical sector 1 on this track */
+	n = (track_shift * format_req.track + head_shift * format_req.head )
+		% F_SECT_PER_TRACK;
+
+	/* determine interleave */
+	il = 1;
+	if (floppy->sect > DP->interleave_sect && F_SIZECODE == 2)
+		il++;
+
+	/* initialize field */
+	for (count = 0; count < F_SECT_PER_TRACK; ++count) {
+		here[count].track = format_req.track;
+		here[count].head = format_req.head;
+		here[count].sect = 0;
+		here[count].size = F_SIZECODE;
+	}
+	/* place logical sectors */
+	for (count = 1; count <= F_SECT_PER_TRACK; ++count) {
+		here[n].sect = count;
+		n = (n+il) % F_SECT_PER_TRACK;
+		if (here[n].sect) { /* sector busy, find next free sector */
+			++n;
+			if (n>= F_SECT_PER_TRACK) {
+				n-=F_SECT_PER_TRACK;
+				while (here[n].sect) ++n;
+			}
+		}
+	}
+}
+
+static void redo_format(void)
+{
+	buffer_track = -1;
+	setup_format_params(format_req.track << STRETCH(floppy));
+	floppy_start();
+#ifdef DEBUGT
+	debugt("queue format request");
+#endif
+}
+
+static struct cont_t format_cont={
+	format_interrupt,
+	redo_format,
+	bad_flp_intr,
+	generic_done };
+
+static int do_format(kdev_t device, struct format_descr *tmp_format_req)
+{
+	int ret;
+	int drive=DRIVE(device);
+
+	LOCK_FDC(drive,1);
+	set_floppy(device);
+	if (!floppy ||
+	    floppy->track > DP->tracks ||
+	    tmp_format_req->track >= floppy->track ||
+	    tmp_format_req->head >= floppy->head ||
+	    (floppy->sect << 2) % (1 << FD_SIZECODE(floppy)) ||
+	    !floppy->fmt_gap) {
+		process_fd_request();
+		return -EINVAL;
+	}
+	format_req = *tmp_format_req;
+	format_errors = 0;
+	cont = &format_cont;
+	errors = &format_errors;
+	IWAIT(redo_format);
+	process_fd_request();
+	return ret;
+}
+
+/*
+ * Buffer read/write and support
+ * =============================
+ */
+
+/* new request_done. Can handle physical sectors which are smaller than a
+ * logical buffer */
+static void request_done(int uptodate)
+{
+	int block;
+
+	probing = 0;
+	reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
+
+	if (!CURRENT){
+		DPRINT("request list destroyed in floppy request done\n");
+		return;
+	}
+	if (uptodate){
+		/* maintain values for invalidation on geometry
+		 * change */
+		block = current_count_sectors + CURRENT->sector;
+		if (block > DRS->maxblock)
+			DRS->maxblock=block;
+		if ( block > floppy->sect)
+			DRS->maxtrack = 1;
+
+		/* unlock chained buffers */
+		while (current_count_sectors && CURRENT &&
+		       current_count_sectors >= CURRENT->current_nr_sectors ){
+			current_count_sectors -= CURRENT->current_nr_sectors;
+			CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
+			CURRENT->sector += CURRENT->current_nr_sectors;
+			end_request(1);
+		}
+		if ( current_count_sectors && CURRENT){
+			/* "unlock" last subsector */
+			CURRENT->buffer += current_count_sectors <<9;
+			CURRENT->current_nr_sectors -= current_count_sectors;
+			CURRENT->nr_sectors -= current_count_sectors;
+			CURRENT->sector += current_count_sectors;
+			return;
+		}
+
+		if ( current_count_sectors && ! CURRENT )
+			DPRINT("request list destroyed in floppy request done\n");
+
+	} else {
+		if(CURRENT->cmd == WRITE) {
+			/* record write error information */
+			DRWE->write_errors++;
+			if(DRWE->write_errors == 1) {
+				DRWE->first_error_sector = CURRENT->sector;
+				DRWE->first_error_generation = DRS->generation;
+			}
+			DRWE->last_error_sector = CURRENT->sector;
+			DRWE->last_error_generation = DRS->generation;
+		}
+		end_request(0);
+	}
+}
+
+/* Interrupt handler evaluating the result of the r/w operation */
+static void rw_interrupt(void)
+{
+	int nr_sectors, ssize;
+
+	if ( ! DRS->first_read_date )
+		DRS->first_read_date = jiffies;
+
+	nr_sectors = 0;
+	CODE2SIZE;
+	nr_sectors = ((R_TRACK-TRACK)*floppy->head+R_HEAD-HEAD) *
+		floppy->sect + ((R_SECTOR-SECTOR) <<  SIZECODE >> 2) -
+		(sector_t % floppy->sect) % ssize;
+
+#ifdef CONFIG_FLOPPY_SANITY
+	if ( nr_sectors > current_count_sectors + ssize -
+	     (current_count_sectors + sector_t) % ssize +
+	     sector_t % ssize){
+		DPRINT2("long rw: %x instead of %lx\n",
+			nr_sectors, current_count_sectors);
+		printk("rs=%d s=%d\n", R_SECTOR, SECTOR);
+		printk("rh=%d h=%d\n", R_HEAD, HEAD);
+		printk("rt=%d t=%d\n", R_TRACK, TRACK);
+		printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK,
+		       sector_t, ssize);
+	}
+#endif
+	if ( nr_sectors < 0 )
+		nr_sectors = 0;
+	if ( nr_sectors < current_count_sectors )
+		current_count_sectors = nr_sectors;
+
+	switch (interpret_errors()){
+		case 2:
+			cont->redo();
+			return;
+		case 1:
+			if (  !current_count_sectors){
+				cont->error();
+				cont->redo();
+				return;
+			}
+			break;
+		case 0:
+			if (  !current_count_sectors){
+				cont->redo();
+				return;
+			}
+			current_type[current_drive] = floppy;
+			floppy_sizes[TOMINOR(current_drive) ]= floppy->size>>1;
+			break;
+	}
+
+	if (probing) {
+		if (DP->flags & FTD_MSG)
+			DPRINT2("Auto-detected floppy type %s in fd%d\n",
+				floppy->name,current_drive);
+		current_type[current_drive] = floppy;
+		floppy_sizes[TOMINOR(current_drive)] = floppy->size >> 1;
+		probing = 0;
+	}
+
+	if ( CT(COMMAND) != FD_READ ||
+	     raw_cmd->kernel_data == CURRENT->buffer ){
+		/* transfer directly from buffer */
+		cont->done(1);
+	} else if ( CT(COMMAND) == FD_READ){
+		buffer_track = raw_cmd->track;
+		buffer_drive = current_drive;
+		if ( nr_sectors + sector_t > buffer_max )
+			buffer_max = nr_sectors + sector_t;
+	}
+	cont->redo();
+}
+
+/* Compute maximal contiguous buffer size. */
+static int buffer_chain_size(void)
+{
+	struct buffer_head *bh;
+	int size;
+	char *base;
+
+	base = CURRENT->buffer;
+	size = CURRENT->current_nr_sectors << 9;
+	bh = CURRENT->bh;
+
+	if(bh){
+		bh = bh->b_reqnext;
+		while ( bh && bh->b_data == base + size ){
+			size += bh->b_size;
+			bh = bh->b_reqnext;
+		}
+	}
+	return size >> 9;
+}
+
+/* Compute the maximal transfer size */
+static int transfer_size(int ssize, int max_sector, int max_size)
+{
+	if ( max_sector > sector_t + max_size)
+		max_sector = sector_t + max_size;
+
+	/* alignment */
+	max_sector -= (max_sector % floppy->sect ) % ssize;
+
+	/* transfer size, beginning not aligned */
+	current_count_sectors = max_sector - sector_t ;
+
+	return max_sector;
+}
+
+/*
+ * Move data from/to the track buffer to/from the buffer cache.
+ */
+static void copy_buffer(int ssize, int max_sector, int max_sector_2)
+{
+	int remaining; /* number of transferred 512-byte sectors */
+	struct buffer_head *bh;
+	char *buffer, *dma_buffer;
+	int size;
+
+	if ( max_sector > max_sector_2 )
+		max_sector = max_sector_2;
+
+	max_sector = transfer_size(ssize, max_sector, CURRENT->nr_sectors);
+
+	if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE &&
+	    buffer_max > sector_t + CURRENT->nr_sectors){
+		current_count_sectors = buffer_max - sector_t;
+		if ( current_count_sectors > CURRENT->nr_sectors )
+			current_count_sectors = CURRENT->nr_sectors;
+	}
+	remaining = current_count_sectors << 9;
+#ifdef CONFIG_FLOPPY_SANITY
+	if ((remaining >> 9) > CURRENT->nr_sectors  &&
+	    CT(COMMAND) == FD_WRITE ){
+		DPRINT("in copy buffer\n");
+		printk("current_count_sectors=%ld\n", current_count_sectors);
+		printk("remaining=%d\n", remaining >> 9);
+		printk("CURRENT->nr_sectors=%ld\n",CURRENT->nr_sectors);
+		printk("CURRENT->current_nr_sectors=%ld\n",
+		       CURRENT->current_nr_sectors);
+		printk("max_sector=%d\n", max_sector);
+		printk("ssize=%d\n", ssize);
+	}
+#endif
+
+	if ( max_sector > buffer_max )
+		buffer_max = max_sector;
+
+	dma_buffer = floppy_track_buffer + ((sector_t - buffer_min) << 9);
+
+	bh = CURRENT->bh;
+	size = CURRENT->current_nr_sectors << 9;
+	buffer = CURRENT->buffer;
+
+	while ( remaining > 0){
+		if ( size > remaining )
+			size = remaining;
+#ifdef CONFIG_FLOPPY_SANITY
+		if (dma_buffer + size >
+		    floppy_track_buffer + (max_buffer_sectors << 10) ||
+		    dma_buffer < floppy_track_buffer ){
+			DPRINT1("buffer overrun in copy buffer %d\n",
+				(int) ((floppy_track_buffer - dma_buffer) >>9));
+			printk("sector_t=%d buffer_min=%d\n",
+			       sector_t, buffer_min);
+			printk("current_count_sectors=%ld\n",
+			       current_count_sectors);
+			if ( CT(COMMAND) == FD_READ )
+				printk("read\n");
+			if ( CT(COMMAND) == FD_READ )
+				printk("write\n");
+			break;
+		}
+		if ( ((unsigned long)buffer) % 512 )
+			DPRINT1("%p buffer not aligned\n", buffer);
+#endif
+		if ( CT(COMMAND) == FD_READ )
+			memcpy( buffer, dma_buffer, size);
+		else
+			memcpy( dma_buffer, buffer, size);
+		remaining -= size;
+		if ( !remaining)
+			break;
+
+		dma_buffer += size;
+		bh = bh->b_reqnext;
+#ifdef CONFIG_FLOPPY_SANITY
+		if ( !bh){
+			DPRINT("bh=null in copy buffer after copy\n");
+			break;
+		}
+#endif
+		size = bh->b_size;
+		buffer = bh->b_data;
+	}
+#ifdef CONFIG_FLOPPY_SANITY
+	if ( remaining ){
+		if ( remaining > 0 )
+			max_sector -= remaining >> 9;
+		DPRINT1("weirdness: remaining %d\n", remaining>>9);
+	}
+#endif
+}
+
+/*
+ * Formulate a read/write request.
+ * this routine decides where to load the data (directly to buffer, or to
+ * tmp floppy area), how much data to load (the size of the buffer, the whole
+ * track, or a single sector)
+ * All floppy_track_buffer handling goes in here. If we ever add track buffer
+ * allocation on the fly, it should be done here. No other part should need
+ * modification.
+ */
+
+static int make_raw_rw_request(void)
+{
+	int aligned_sector_t;
+	int max_sector, max_size, tracksize, ssize;
+
+	set_fdc(DRIVE(CURRENT->rq_dev));
+
+	raw_cmd = &default_raw_cmd;
+	raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
+		FD_RAW_NEED_SEEK;
+	raw_cmd->cmd_count = NR_RW;
+	if (CURRENT->cmd == READ){
+		raw_cmd->flags |= FD_RAW_READ;
+		COMMAND = FM_MODE(floppy,FD_READ);
+	} else if (CURRENT->cmd == WRITE){
+		raw_cmd->flags |= FD_RAW_WRITE;
+		COMMAND = FM_MODE(floppy,FD_WRITE);
+	} else {
+		DPRINT("make_raw_rw_request: unknown command\n");
+		return 0;
+	}
+
+	max_sector = floppy->sect * floppy->head;
+	
+	TRACK = CURRENT->sector / max_sector;
+	sector_t = CURRENT->sector % max_sector;
+	if ( floppy->track && TRACK >= floppy->track )
+		return 0;
+	HEAD = sector_t / floppy->sect;
+
+	if (((floppy->stretch & FD_SWAPSIDES) || TESTF( FD_NEED_TWADDLE)) &&
+	    sector_t < floppy->sect )
+		max_sector = floppy->sect;
+
+	/* 2M disks have phantom sectors on the first track */
+	if ( (floppy->rate & FD_2M ) && (!TRACK) && (!HEAD)){
+		max_sector = 2 * floppy->sect / 3;
+		if (sector_t >= max_sector){
+			current_count_sectors =  (floppy->sect - sector_t);
+			if ( current_count_sectors > CURRENT->nr_sectors )
+				current_count_sectors = CURRENT->nr_sectors;
+			return 1;
+		}
+		SIZECODE = 2;
+	} else
+		SIZECODE = FD_SIZECODE(floppy);
+	raw_cmd->rate = floppy->rate & 3;
+	if ((floppy->rate & FD_2M) &&
+	    (TRACK || HEAD ) &&
+	    raw_cmd->rate == 2)
+		raw_cmd->rate = 1;
+
+	if ( SIZECODE )
+		SIZECODE2 = 0xff;
+	else
+		SIZECODE2 = 0x80;
+	raw_cmd->track = TRACK << STRETCH(floppy);
+	DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,HEAD);
+	GAP = floppy->gap;
+	CODE2SIZE;
+	SECT_PER_TRACK = floppy->sect << 2 >> SIZECODE;
+	SECTOR = ((sector_t % floppy->sect) << 2 >> SIZECODE) + 1;
+	tracksize = floppy->sect - floppy->sect % ssize;
+	if ( tracksize < floppy->sect ){
+		SECT_PER_TRACK ++;
+		if (  tracksize <= sector_t % floppy->sect)
+			SECTOR--;
+		while ( tracksize <= sector_t % floppy->sect){
+			while( tracksize + ssize > floppy->sect ){
+				SIZECODE--;
+				ssize >>= 1;
+			}
+			SECTOR++; SECT_PER_TRACK ++;
+			tracksize += ssize;
+		}
+		max_sector = HEAD * floppy->sect + tracksize;
+	} else if ( !TRACK && !HEAD && !( floppy->rate & FD_2M ) && probing)
+		max_sector = floppy->sect;
+
+	aligned_sector_t = sector_t - ( sector_t % floppy->sect ) % ssize;
+	max_size = CURRENT->nr_sectors;
+	if ((raw_cmd->track == buffer_track) && (current_drive == buffer_drive) &&
+	    (sector_t >= buffer_min) && (sector_t < buffer_max)) {
+		/* data already in track buffer */
+		if (CT(COMMAND) == FD_READ) {
+			copy_buffer(1, max_sector, buffer_max);
+			return 1;
+		}
+	} else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){
+		if (CT(COMMAND) == FD_WRITE){
+			if(sector_t + CURRENT->nr_sectors > ssize &&
+			   sector_t + CURRENT->nr_sectors < ssize + ssize)
+				max_size = ssize + ssize;
+			else
+				max_size = ssize;
+		}
+		raw_cmd->flags &= ~FD_RAW_WRITE;
+		raw_cmd->flags |= FD_RAW_READ;
+		COMMAND = FM_MODE(floppy,FD_READ);
+	} else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS ) {
+		int direct, indirect;
+
+		indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
+			sector_t;
+
+		max_size = buffer_chain_size();
+#if 0
+		if ( max_size > ( MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer))>>9)
+			max_size=(MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer))>>9;
+		/* 64 kb boundaries */
+		if (CROSS_64KB(CURRENT->buffer, max_size << 9))
+			max_size = ( K_64 - ((long) CURRENT->buffer) % K_64)>>9;
+#endif
+		direct = transfer_size(ssize,max_sector,max_size) - sector_t;
+		/*
+		 *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * 