linux release 0.01

		1. Short intro

This is a free minix-like kernel for i386(+) based AT-machines.  Full
source is included, and this source has been used to produce a running
kernel on two different machines.  Currently there are no kernel
binaries for public viewing, as they have to be recompiled for different
machines.  You need to compile it with gcc (I use 1.40, don't know if
1.37.1 will handle all __asm__-directives), after having changed the
relevant configuration file(s).

As the version number (0.01) suggests this is not a mature product.
Currently only a subset of AT-hardware is supported (hard-disk, screen,
keyboard and serial lines), and some of the system calls are not yet
fully implemented (notably mount/umount aren't even implemented).  See
comments or readme's in the code.

This version is also meant mostly for reading - ie if you are interested
in how the system looks like currently.  It will compile and produce a
working kernel, and though I will help in any way I can to get it
working on your machine (mail me), it isn't really supported.  Changes
are frequent, and the first "production" version will probably differ
wildly from this pre-alpha-release.

Hardware needed for running linux:
	- 386 AT
	- VGA/EGA screen
	- AT-type harddisk controller (IDE is fine)
	- Finnish keyboard (oh, you can use a US keyboard, but not
	  without some practise :-)

The Finnish keyboard is hard-wired, and as I don't have a US one I
cannot change it without major problems. See kernel/keyboard.s for
details. If anybody is willing to make an even partial port, I'd be
grateful. Shouldn't be too hard, as it's tabledriven (it's assembler
though, so ...)

Although linux is a complete kernel, and uses no code from minix or
other sources, almost none of the support routines have yet been coded.
Thus you currently need minix to bootstrap the system. It might be
possible to use the free minix demo-disk to make a filesystem and run
linux without having minix, but I don't know...

		2. Copyrights etc

This kernel is (C) 1991 Linus Torvalds, but all or part of it may be
redistributed provided you do the following:

	- Full source must be available (and free), if not with the
	  distribution then at least on asking for it.

	- Copyright notices must be intact. (In fact, if you distribute
	  only parts of it you may have to add copyrights, as there aren't
	  (C)'s in all files.) Small partial excerpts may be copied
	  without bothering with copyrights.

	- You may not distibute this for a fee, not even "handling"
	  costs.

Mail me at "torvalds@kruuna.helsinki.fi" if you have any questions.

Sadly, a kernel by itself gets you nowhere. To get a working system you
need a shell, compilers, a library etc. These are separate parts and may
be under a stricter (or even looser) copyright. Most of the tools used
with linux are GNU software and are under the GNU copyleft. These tools
aren't in the distribution - ask me (or GNU) for more info.

		3. Short technical overview of the kernel.

The linux kernel has been made under minix, and it was my original idea
to make it binary compatible with minix. That was dropped, as the
differences got bigger, but the system still resembles minix a great
deal. Some of the key points are:

	- Efficient use of the possibilities offered by the 386 chip.
	  Minix was written on a 8088, and later ported to other
	  machines - linux takes full advantage of the 386 (which is
	  nice if you /have/ a 386, but makes porting very difficult)

	- No message passing, this is a more traditional approach to
	  unix. System calls are just that - calls. This might or might
	  not be faster, but it does mean we can dispense with some of
	  the problems with messages (message queues etc). Of course, we
	  also miss the nice features :-p.

	- Multithreaded FS - a direct consequence of not using messages.
	  This makes the filesystem a bit (a lot) more complicated, but
	  much nicer. Coupled with a better scheduler, this means that
	  you can actually run several processes concurrently without
	  the performance hit induced by minix.

	- Minimal task switching. This too is a consequence of not using
	  messages. We task switch only when we really want to switch
	  tasks - unlike minix which task-switches whatever you do. This
	  means we can more easily implement 387 support (indeed this is
	  already mostly implemented)

	- Interrupts aren't hidden. Some people (among them Tanenbaum)
	  think interrupts are ugly and should be hidden. Not so IMHO.
	  Due to practical reasons interrupts must be mainly handled by
	  machine code, which is a pity, but they are a part of the code
	  like everything else. Especially device drivers are mostly
	  interrupt routines - see kernel/hd.c etc.

	- There is no distinction between kernel/fs/mm, and they are all
	  linked into the same heap of code. This has it's good sides as
	  well as bad. The code isn't as modular as the minix code, but
	  on the other hand some things are simpler. The different parts
	  of the kernel are under different sub-directories in the
	  source tree, but when running everything happens in the same
	  data/code space.

The guiding line when implementing linux was: get it working fast. I
wanted the kernel simple, yet powerful enough to run most unix software.
The file system I couldn't do much about - it needed to be minix
compatible for practical reasons, and the minix filesystem was simple
enough as it was. The kernel and mm could be simplified, though:

	- Just one data structure for tasks. "Real" unices have task
	  information in several places, I wanted everything in one
	  place.

	- A very simple memory management algorithm, using both the
	  paging and segmentation capabilities of the i386. Currently
	  MM is just two files - memory.c and page.s, just a couple of
	  hundreds of lines of code.

These decisions seem to have worked out well - bugs were easy to spot,
and things work.

		4. The "kernel proper"

All the routines handling tasks are in the subdirectory "kernel". These
include things like 'fork' and 'exit' as well as scheduling and minor
system calls like 'getpid' etc. Here are also the handlers for most
exceptions and traps (not page faults, they are in mm), and all
low-level device drivers (get_hd_block, tty_write etc). Currently all
faults lead to a exit with error code 11 (Segmentation fault), and the
system seems to be relatively stable ("crashme" hasn't - yet).

		5. Memory management

This is the simplest of all parts, and should need only little changes.
It contains entry-points for some things that the rest of the kernel
needs, but mostly copes on it's own, handling page faults as they
happen. Indeed, the rest of the kernel usually doesn't actively allocate
pages, and just writes into user space, letting mm handle any possible
'page-not-present' errors.

Memory is dealt with in two completely different ways - by paging and
segmentation.  First the 386 VM-space (4GB) is divided into a number of
segments (currently 64 segments of 64Mb each), the first of which is the
kernel memory segment, with the complete physical memory identity-mapped
into it.  All kernel functions live within this area.

Tasks are then given one segment each, to use as they wish. The paging
mechanism sees to filling the segment with the appropriate pages,
keeping track of any duplicate copies (created at a 'fork'), and making
copies on any write. The rest of the system doesn't need to know about
all this.

		6. The file system

As already mentioned, the linux FS is the same as in minix. This makes
crosscompiling from minix easy, and means you can mount a linux
partition from minix (or the other way around as soon as I implement
mount :-). This is only on the logical level though - the actual
routines are very different.

	NOTE! Minix-1.6.16 seems to have a new FS, with minor
	modifications to the 1.5.10 I've been using. Linux
	won't understand the new system.

The main difference is in the fact that minix has a single-threaded
file-system and linux hasn't. Implementing a single-threaded FS is much
easier as you don't need to worry about other processes allocating
buffer blocks etc while you do something else. It also means that you
lose some of the multiprocessing so important to unix.

There are a number of problems (deadlocks/raceconditions) that the linux
kernel needed to address due to multi-threading.  One way to inhibit
race-conditions is to lock everything you need, but as this can lead to
unnecessary blocking I decided never to lock any data structures (unless
actually reading or writing to a physical device).  This has the nice
property that dead-locks cannot happen.

Sadly it has the not so nice property that race-conditions can happen
almost everywhere.  These are handled by double-checking allocations etc
(see fs/buffer.c and fs/inode.c).  Not letting the kernel schedule a
task while it is in supervisor mode (standard unix practise), means that
all kernel/fs/mm actions are atomic (not counting interrupts, and we are
careful when writing those) if you don't call 'sleep', so that is one of
the things we can count on.

		7. Apologies :-)

This isn't yet the "mother of all operating systems", and anyone who
hoped for that will have to wait for the first real release (1.0), and
even then you might not want to change from minix.  This is a source
release for those that are interested in seeing what linux looks like,
and it's not really supported yet.  Anyone with questions or suggestions
(even bug-reports if you decide to get it working on your system) is
encouraged to mail me.

		8. Getting it working

Most hardware dependancies will have to be compiled into the system, and
there a number of defines in the file "include/linux/config.h" that you
have to change to get a personalized kernel.  Also you must uncomment
the right "equ" in the file boot/boot.s, telling the bootup-routine what
kind of device your A-floppy is.  After that a simple "make" should make
the file "Image", which you can copy to a floppy (cp Image /dev/PS0 is
what I use with a 1.44Mb floppy).  That's it.

Without any programs to run, though, the kernel cannot do anything. You
should find binaries for 'update' and 'bash' at the same place you found
this, which will have to be put into the '/bin' directory on the
specified root-device (specified in config.h). Bash must be found under
the name '/bin/sh', as that's what the kernel currently executes. Happy
hacking.

		Linus Torvalds		"torvalds@kruuna.helsinki.fi"
		Petersgatan 2 A 2
		00140 Helsingfors 14
		FINLAND

-----

From: torvalds@klaava.Helsinki.FI (Linus Benedict Torvalds)
Newsgroups: comp.os.minix
Subject: What would you like to see most in minix?
Summary: small poll for my new operating system
Keywords: 386, preferences
Message-ID: <1991Aug25.205708.9541@klaava.Helsinki.FI>
Date: 25 Aug 91 20:57:08 GMT
Organization: University of Helsinki

Hello everybody out there using minix -

I'm doing a (free) operating system (just a hobby, won't be big and
professional like gnu) for 386(486) AT clones.  This has been brewing
since april, and is starting to get ready.  I'd like any feedback on
things people like/dislike in minix, as my OS resembles it somewhat
(same physical layout of the file-system (due to practical reasons)
among other things).

I've currently ported bash(1.08) and gcc(1.40), and things seem to work.
This implies that I'll get something practical within a few months, and
I'd like to know what features most people would want.  Any suggestions
are welcome, but I won't promise I'll implement them :-)

		Linus (torvalds@kruuna.helsinki.fi)

PS.  Yes - it's free of any minix code, and it has a multi-threaded fs.
It is NOT protable (uses 386 task switching etc), and it probably never
will support anything other than AT-harddisks, as that's all I have :-(.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d9d9fab
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,96 @@
+#
+# Makefile for linux.
+# If you don't have '-mstring-insns' in your gcc (and nobody but me has :-)
+# remove them from the CFLAGS defines.
+#
+
+AS86	=as -0 -a
+CC86	=cc -0
+LD86	=ld -0
+
+AS	=gas
+LD	=gld
+LDFLAGS	=-s -x -M
+CC	=gcc
+CFLAGS	=-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs
+CPP	=gcc -E -nostdinc -Iinclude
+
+ARCHIVES=kernel/kernel.o mm/mm.o fs/fs.o
+LIBS	=lib/lib.a
+
+.c.s:
+	$(CC) $(CFLAGS) \
+	-nostdinc -Iinclude -S -o $*.s $<
+.s.o:
+	$(AS) -c -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) \
+	-nostdinc -Iinclude -c -o $*.o $<
+
+all:	Image
+
+Image: boot/boot tools/system tools/build
+	tools/build boot/boot tools/system > Image
+	sync
+
+tools/build: tools/build.c
+	$(CC) $(CFLAGS) \
+	-o tools/build tools/build.c
+	chmem +65000 tools/build
+
+boot/head.o: boot/head.s
+
+tools/system:	boot/head.o init/main.o \
+		$(ARCHIVES) $(LIBS)
+	$(LD) $(LDFLAGS) boot/head.o init/main.o \
+	$(ARCHIVES) \
+	$(LIBS) \
+	-o tools/system > System.map
+
+kernel/kernel.o:
+	(cd kernel; make)
+
+mm/mm.o:
+	(cd mm; make)
+
+fs/fs.o:
+	(cd fs; make)
+
+lib/lib.a:
+	(cd lib; make)
+
+boot/boot:	boot/boot.s tools/system
+	(echo -n "SYSSIZE = (";ls -l tools/system | grep system \
+		| cut -c25-31 | tr '\012' ' '; echo "+ 15 ) / 16") > tmp.s
+	cat boot/boot.s >> tmp.s
+	$(AS86) -o boot/boot.o tmp.s
+	rm -f tmp.s
+	$(LD86) -s -o boot/boot boot/boot.o
+
+clean:
+	rm -f Image System.map tmp_make boot/boot core
+	rm -f init/*.o boot/*.o tools/system tools/build
+	(cd mm;make clean)
+	(cd fs;make clean)
+	(cd kernel;make clean)
+	(cd lib;make clean)
+
+backup: clean
+	(cd .. ; tar cf - linux | compress16 - > backup.Z)
+	sync
+
+dep:
+	sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
+	(for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done) >> tmp_make
+	cp tmp_make Makefile
+	(cd fs; make dep)
+	(cd kernel; make dep)
+	(cd mm; make dep)
+
+### Dependencies:
+init/main.o : init/main.c include/unistd.h include/sys/stat.h \
+  include/sys/types.h include/sys/times.h include/sys/utsname.h \
+  include/utime.h include/time.h include/linux/tty.h include/termios.h \
+  include/linux/sched.h include/linux/head.h include/linux/fs.h \
+  include/linux/mm.h include/asm/system.h include/asm/io.h include/stddef.h \
+  include/stdarg.h include/fcntl.h 
diff --git a/boot/boot.s b/boot/boot.s
new file mode 100644
index 0000000..e19bbd2
--- /dev/null
+++ b/boot/boot.s
@@ -0,0 +1,329 @@
+|
+|	boot.s
+|
+| boot.s is loaded at 0x7c00 by the bios-startup routines, and moves itself
+| out of the way to address 0x90000, and jumps there.
+|
+| It then loads the system at 0x10000, using BIOS interrupts. Thereafter
+| it disables all interrupts, moves the system down to 0x0000, changes
+| to protected mode, and calls the start of system. System then must
+| RE-initialize the protected mode in it's own tables, and enable
+| interrupts as needed.
+|
+| NOTE! currently system is at most 8*65536 bytes long. This should be no
+| problem, even in the future. I want to keep it simple. This 512 kB
+| kernel size should be enough - in fact more would mean we'd have to move
+| not just these start-up routines, but also do something about the cache-
+| memory (block IO devices). The area left over in the lower 640 kB is meant
+| for these. No other memory is assumed to be "physical", ie all memory
+| over 1Mb is demand-paging. All addresses under 1Mb are guaranteed to match
+| their physical addresses.
+|
+| NOTE1 abouve is no longer valid in it's entirety. cache-memory is allocated
+| above the 1Mb mark as well as below. Otherwise it is mainly correct.
+|
+| NOTE 2! The boot disk type must be set at compile-time, by setting
+| the following equ. Having the boot-up procedure hunt for the right
+| disk type is severe brain-damage.
+| The loader has been made as simple as possible (had to, to get it
+| in 512 bytes with the code to move to protected mode), and continuos
+| read errors will result in a unbreakable loop. Reboot by hand. It
+| loads pretty fast by getting whole sectors at a time whenever possible.
+
+| 1.44Mb disks:
+sectors = 18
+| 1.2Mb disks:
+| sectors = 15
+| 720kB disks:
+| sectors = 9
+
+.globl begtext, begdata, begbss, endtext, enddata, endbss
+.text
+begtext:
+.data
+begdata:
+.bss
+begbss:
+.text
+
+BOOTSEG = 0x07c0
+INITSEG = 0x9000
+SYSSEG  = 0x1000			| system loaded at 0x10000 (65536).
+ENDSEG	= SYSSEG + SYSSIZE
+
+entry start
+start:
+	mov	ax,#BOOTSEG
+	mov	ds,ax
+	mov	ax,#INITSEG
+	mov	es,ax
+	mov	cx,#256
+	sub	si,si
+	sub	di,di
+	rep
+	movw
+	jmpi	go,INITSEG
+go:	mov	ax,cs
+	mov	ds,ax
+	mov	es,ax
+	mov	ss,ax
+	mov	sp,#0x400		| arbitrary value >>512
+
+	mov	ah,#0x03	| read cursor pos
+	xor	bh,bh
+	int	0x10
+	
+	mov	cx,#24
+	mov	bx,#0x0007	| page 0, attribute 7 (normal)
+	mov	bp,#msg1
+	mov	ax,#0x1301	| write string, move cursor
+	int	0x10
+
+| ok, we've written the message, now
+| we want to load the system (at 0x10000)
+
+	mov	ax,#SYSSEG
+	mov	es,ax		| segment of 0x010000
+	call	read_it
+	call	kill_motor
+
+| if the read went well we get current cursor position ans save it for
+| posterity.
+
+	mov	ah,#0x03	| read cursor pos
+	xor	bh,bh
+	int	0x10		| save it in known place, con_init fetches
+	mov	[510],dx	| it from 0x90510.
+		
+| now we want to move to protected mode ...
+
+	cli			| no interrupts allowed !
+
+| first we move the system to it's rightful place
+
+	mov	ax,#0x0000
+	cld			| 'direction'=0, movs moves forward
+do_move:
+	mov	es,ax		| destination segment
+	add	ax,#0x1000
+	cmp	ax,#0x9000
+	jz	end_move
+	mov	ds,ax		| source segment
+	sub	di,di
+	sub	si,si
+	mov 	cx,#0x8000
+	rep
+	movsw
+	j	do_move
+
+| then we load the segment descriptors
+
+end_move:
+
+	mov	ax,cs		| right, forgot this at first. didn't work :-)
+	mov	ds,ax
+	lidt	idt_48		| load idt with 0,0
+	lgdt	gdt_48		| load gdt with whatever appropriate
+
+| that was painless, now we enable A20
+
+	call	empty_8042
+	mov	al,#0xD1		| command write
+	out	#0x64,al
+	call	empty_8042
+	mov	al,#0xDF		| A20 on
+	out	#0x60,al
+	call	empty_8042
+
+| well, that went ok, I hope. Now we have to reprogram the interrupts :-(
+| we put them right after the intel-reserved hardware interrupts, at
+| int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
+| messed this up with the original PC, and they haven't been able to
+| rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
+| which is used for the internal hardware interrupts as well. We just
+| have to reprogram the 8259's, and it isn't fun.
+
+	mov	al,#0x11		| initialization sequence
+	out	#0x20,al		| send it to 8259A-1
+	.word	0x00eb,0x00eb		| jmp $+2, jmp $+2
+	out	#0xA0,al		| and to 8259A-2
+	.word	0x00eb,0x00eb
+	mov	al,#0x20		| start of hardware int's (0x20)
+	out	#0x21,al
+	.word	0x00eb,0x00eb
+	mov	al,#0x28		| start of hardware int's 2 (0x28)
+	out	#0xA1,al
+	.word	0x00eb,0x00eb
+	mov	al,#0x04		| 8259-1 is master
+	out	#0x21,al
+	.word	0x00eb,0x00eb
+	mov	al,#0x02		| 8259-2 is slave
+	out	#0xA1,al
+	.word	0x00eb,0x00eb
+	mov	al,#0x01		| 8086 mode for both
+	out	#0x21,al
+	.word	0x00eb,0x00eb
+	out	#0xA1,al
+	.word	0x00eb,0x00eb
+	mov	al,#0xFF		| mask off all interrupts for now
+	out	#0x21,al
+	.word	0x00eb,0x00eb
+	out	#0xA1,al
+
+| well, that certainly wasn't fun :-(. Hopefully it works, and we don't
+| need no steenking BIOS anyway (except for the initial loading :-).
+| The BIOS-routine wants lots of unnecessary data, and it's less
+| "interesting" anyway. This is how REAL programmers do it.
+|
+| Well, now's the time to actually move into protected mode. To make
+| things as simple as possible, we do no register set-up or anything,
+| we let the gnu-compiled 32-bit programs do that. We just jump to
+| absolute address 0x00000, in 32-bit protected mode.
+
+	mov	ax,#0x0001	| protected mode (PE) bit
+	lmsw	ax		| This is it!
+	jmpi	0,8		| jmp offset 0 of segment 8 (cs)
+
+| This routine checks that the keyboard command queue is empty
+| No timeout is used - if this hangs there is something wrong with
+| the machine, and we probably couldn't proceed anyway.
+empty_8042:
+	.word	0x00eb,0x00eb
+	in	al,#0x64	| 8042 status port
+	test	al,#2		| is input buffer full?
+	jnz	empty_8042	| yes - loop
+	ret
+
+| This routine loads the system at address 0x10000, making sure
+| no 64kB boundaries are crossed. We try to load it as fast as
+| possible, loading whole tracks whenever we can.
+|
+| in:	es - starting address segment (normally 0x1000)
+|
+| This routine has to be recompiled to fit another drive type,
+| just change the "sectors" variable at the start of the file
+| (originally 18, for a 1.44Mb drive)
+|
+sread:	.word 1			| sectors read of current track
+head:	.word 0			| current head
+track:	.word 0			| current track
+read_it:
+	mov ax,es
+	test ax,#0x0fff
+die:	jne die			| es must be at 64kB boundary
+	xor bx,bx		| bx is starting address within segment
+rp_read:
+	mov ax,es
+	cmp ax,#ENDSEG		| have we loaded all yet?
+	jb ok1_read
+	ret
+ok1_read:
+	mov ax,#sectors
+	sub ax,sread
+	mov cx,ax
+	shl cx,#9
+	add cx,bx
+	jnc ok2_read
+	je ok2_read
+	xor ax,ax
+	sub ax,bx
+	shr ax,#9
+ok2_read:
+	call read_track
+	mov cx,ax
+	add ax,sread
+	cmp ax,#sectors
+	jne ok3_read
+	mov ax,#1
+	sub ax,head
+	jne ok4_read
+	inc track
+ok4_read:
+	mov head,ax
+	xor ax,ax
+ok3_read:
+	mov sread,ax
+	shl cx,#9
+	add bx,cx
+	jnc rp_read
+	mov ax,es
+	add ax,#0x1000
+	mov es,ax
+	xor bx,bx
+	jmp rp_read
+
+read_track:
+	push ax
+	push bx
+	push cx
+	push dx
+	mov dx,track
+	mov cx,sread
+	inc cx
+	mov ch,dl
+	mov dx,head
+	mov dh,dl
+	mov dl,#0
+	and dx,#0x0100
+	mov ah,#2
+	int 0x13
+	jc bad_rt
+	pop dx
+	pop cx
+	pop bx
+	pop ax
+	ret
+bad_rt:	mov ax,#0
+	mov dx,#0
+	int 0x13
+	pop dx
+	pop cx
+	pop bx
+	pop ax
+	jmp read_track
+
+/*
+ * This procedure turns off the floppy drive motor, so
+ * that we enter the kernel in a known state, and
+ * don't have to worry about it later.
+ */
+kill_motor:
+	push dx
+	mov dx,#0x3f2
+	mov al,#0
+	outb
+	pop dx
+	ret
+
+gdt:
+	.word	0,0,0,0		| dummy
+
+	.word	0x07FF		| 8Mb - limit=2047 (2048*4096=8Mb)
+	.word	0x0000		| base address=0
+	.word	0x9A00		| code read/exec
+	.word	0x00C0		| granularity=4096, 386
+
+	.word	0x07FF		| 8Mb - limit=2047 (2048*4096=8Mb)
+	.word	0x0000		| base address=0
+	.word	0x9200		| data read/write
+	.word	0x00C0		| granularity=4096, 386
+
+idt_48:
+	.word	0			| idt limit=0
+	.word	0,0			| idt base=0L
+
+gdt_48:
+	.word	0x800		| gdt limit=2048, 256 GDT entries
+	.word	gdt,0x9		| gdt base = 0X9xxxx
+	
+msg1:
+	.byte 13,10
+	.ascii "Loading system ..."
+	.byte 13,10,13,10
+
+.text
+endtext:
+.data
+enddata:
+.bss
+endbss:
diff --git a/boot/head.s b/boot/head.s
new file mode 100644
index 0000000..c008ba8
--- /dev/null
+++ b/boot/head.s
@@ -0,0 +1,175 @@
+/*
+ *  head.s contains the 32-bit startup code.
+ *
+ * NOTE!!! Startup happens at absolute address 0x00000000, which is also where
+ * the page directory will exist. The startup code will be overwritten by
+ * the page directory.
+ */
+.text
+.globl _idt,_gdt,_pg_dir
+_pg_dir:
+startup_32:
+	movl $0x10,%eax
+	mov %ax,%ds
+	mov %ax,%es
+	mov %ax,%fs
+	mov %ax,%gs
+	lss _stack_start,%esp
+	call setup_idt
+	call setup_gdt
+	movl $0x10,%eax		# reload all the segment registers
+	mov %ax,%ds		# after changing gdt. CS was already
+	mov %ax,%es		# reloaded in 'setup_gdt'
+	mov %ax,%fs
+	mov %ax,%gs
+	lss _stack_start,%esp
+	xorl %eax,%eax
+1:	incl %eax		# check that A20 really IS enabled
+	movl %eax,0x000000
+	cmpl %eax,0x100000
+	je 1b
+	movl %cr0,%eax		# check math chip
+	andl $0x80000011,%eax	# Save PG,ET,PE
+	testl $0x10,%eax
+	jne 1f			# ET is set - 387 is present
+	orl $4,%eax		# else set emulate bit
+1:	movl %eax,%cr0
+	jmp after_page_tables
+
+/*
+ *  setup_idt
+ *
+ *  sets up a idt with 256 entries pointing to
+ *  ignore_int, interrupt gates. It then loads
+ *  idt. Everything that wants to install itself
+ *  in the idt-table may do so themselves. Interrupts
+ *  are enabled elsewhere, when we can be relatively
+ *  sure everything is ok. This routine will be over-
+ *  written by the page tables.
+ */
+setup_idt:
+	lea ignore_int,%edx
+	movl $0x00080000,%eax
+	movw %dx,%ax		/* selector = 0x0008 = cs */
+	movw $0x8E00,%dx	/* interrupt gate - dpl=0, present */
+
+	lea _idt,%edi
+	mov $256,%ecx
+rp_sidt:
+	movl %eax,(%edi)
+	movl %edx,4(%edi)
+	addl $8,%edi
+	dec %ecx
+	jne rp_sidt
+	lidt idt_descr
+	ret
+
+/*
+ *  setup_gdt
+ *
+ *  This routines sets up a new gdt and loads it.
+ *  Only two entries are currently built, the same
+ *  ones that were built in init.s. The routine
+ *  is VERY complicated at two whole lines, so this
+ *  rather long comment is certainly needed :-).
+ *  This routine will beoverwritten by the page tables.
+ */
+setup_gdt:
+	lgdt gdt_descr
+	ret
+
+.org 0x1000
+pg0:
+
+.org 0x2000
+pg1:
+
+.org 0x3000
+pg2:		# This is not used yet, but if you
+		# want to expand past 8 Mb, you'll have
+		# to use it.
+
+.org 0x4000
+after_page_tables:
+	pushl $0		# These are the parameters to main :-)
+	pushl $0
+	pushl $0
+	pushl $L6		# return address for main, if it decides to.
+	pushl $_main
+	jmp setup_paging
+L6:
+	jmp L6			# main should never return here, but
+				# just in case, we know what happens.
+
+/* This is the default interrupt "handler" :-) */
+.align 2
+ignore_int:
+	incb 0xb8000+160		# put something on the screen
+	movb $2,0xb8000+161		# so that we know something
+	iret				# happened
+
+
+/*
+ * Setup_paging
+ *
+ * This routine sets up paging by setting the page bit
+ * in cr0. The page tables are set up, identity-mapping
+ * the first 8MB. The pager assumes that no illegal
+ * addresses are produced (ie >4Mb on a 4Mb machine).
+ *
+ * NOTE! Although all physical memory should be identity
+ * mapped by this routine, only the kernel page functions
+ * use the >1Mb addresses directly. All "normal" functions
+ * use just the lower 1Mb, or the local data space, which
+ * will be mapped to some other place - mm keeps track of
+ * that.
+ *
+ * For those with more memory than 8 Mb - tough luck. I've
+ * not got it, why should you :-) The source is here. Change
+ * it. (Seriously - it shouldn't be too difficult. Mostly
+ * change some constants etc. I left it at 8Mb, as my machine
+ * even cannot be extended past that (ok, but it was cheap :-)
+ * I've tried to show which constants to change by having
+ * some kind of marker at them (search for "8Mb"), but I
+ * won't guarantee that's all :-( )
+ */
+.align 2
+setup_paging:
+	movl $1024*3,%ecx
+	xorl %eax,%eax
+	xorl %edi,%edi			/* pg_dir is at 0x000 */
+	cld;rep;stosl
+	movl $pg0+7,_pg_dir		/* set present bit/user r/w */
+	movl $pg1+7,_pg_dir+4		/*  --------- " " --------- */
+	movl $pg1+4092,%edi
+	movl $0x7ff007,%eax		/*  8Mb - 4096 + 7 (r/w user,p) */
+	std
+1:	stosl			/* fill pages backwards - more efficient :-) */
+	subl $0x1000,%eax
+	jge 1b
+	xorl %eax,%eax		/* pg_dir is at 0x0000 */
+	movl %eax,%cr3		/* cr3 - page directory start */
+	movl %cr0,%eax
+	orl $0x80000000,%eax
+	movl %eax,%cr0		/* set paging (PG) bit */
+	ret			/* this also flushes prefetch-queue */
+
+.align 2
+.word 0
+idt_descr:
+	.word 256*8-1		# idt contains 256 entries
+	.long _idt
+.align 2
+.word 0
+gdt_descr:
+	.word 256*8-1		# so does gdt (not that that's any
+	.long _gdt		# magic number, but it works for me :^)
+
+	.align 3
+_idt:	.fill 256,8,0		# idt is uninitialized
+
+_gdt:	.quad 0x0000000000000000	/* NULL descriptor */
+	.quad 0x00c09a00000007ff	/* 8Mb */
+	.quad 0x00c09200000007ff	/* 8Mb */
+	.quad 0x0000000000000000	/* TEMPORARY - don't use */
+	.fill 252,8,0			/* space for LDT's and TSS's etc */
diff --git a/fs/Makefile b/fs/Makefile
new file mode 100644
index 0000000..24e7e4c
--- /dev/null
+++ b/fs/Makefile
@@ -0,0 +1,95 @@
+AR	=gar
+AS	=gas
+CC	=gcc
+LD	=gld
+CFLAGS	=-Wall -O -fstrength-reduce -fcombine-regs -fomit-frame-pointer \
+	-mstring-insns -nostdinc -I../include
+CPP	=gcc -E -nostdinc -I../include
+
+.c.s:
+	$(CC) $(CFLAGS) \
+	-S -o $*.s $<
+.c.o:
+	$(CC) $(CFLAGS) \
+	-c -o $*.o $<
+.s.o:
+	$(AS) -o $*.o $<
+
+OBJS=	open.o read_write.o inode.o file_table.o buffer.o super.o \
+	block_dev.o char_dev.o file_dev.o stat.o exec.o pipe.o namei.o \
+	bitmap.o fcntl.o ioctl.o tty_ioctl.o truncate.o
+
+fs.o: $(OBJS)
+	$(LD) -r -o fs.o $(OBJS)
+
+clean:
+	rm -f core *.o *.a tmp_make
+	for i in *.c;do rm -f `basename $$i .c`.s;done
+
+dep:
+	sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
+	(for i in *.c;do $(CPP) -M $$i;done) >> tmp_make
+	cp tmp_make Makefile
+
+### Dependencies:
+bitmap.o : bitmap.c ../include/string.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+  ../include/linux/mm.h ../include/linux/kernel.h 
+block_dev.o : block_dev.c ../include/errno.h ../include/linux/fs.h \
+  ../include/sys/types.h ../include/linux/kernel.h ../include/asm/segment.h 
+buffer.o : buffer.c ../include/linux/config.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+  ../include/linux/mm.h ../include/linux/kernel.h ../include/asm/system.h 
+char_dev.o : char_dev.c ../include/errno.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+  ../include/linux/mm.h ../include/linux/kernel.h 
+exec.o : exec.c ../include/errno.h ../include/sys/stat.h \
+  ../include/sys/types.h ../include/a.out.h ../include/linux/fs.h \
+  ../include/linux/sched.h ../include/linux/head.h ../include/linux/mm.h \
+  ../include/linux/kernel.h ../include/asm/segment.h 
+fcntl.o : fcntl.c ../include/string.h ../include/errno.h \
+  ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
+  ../include/sys/types.h ../include/linux/mm.h ../include/linux/kernel.h \
+  ../include/asm/segment.h ../include/fcntl.h ../include/sys/stat.h 
+file_dev.o : file_dev.c ../include/errno.h ../include/fcntl.h \
+  ../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \
+  ../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \
+  ../include/asm/segment.h 
+file_table.o : file_table.c ../include/linux/fs.h ../include/sys/types.h 
+inode.o : inode.c ../include/string.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+  ../include/linux/mm.h ../include/linux/kernel.h ../include/asm/system.h 
+ioctl.o : ioctl.c ../include/string.h ../include/errno.h \
+  ../include/sys/stat.h ../include/sys/types.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h 
+namei.o : namei.c ../include/linux/sched.h ../include/linux/head.h \
+  ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
+  ../include/linux/kernel.h ../include/asm/segment.h ../include/string.h \
+  ../include/fcntl.h ../include/errno.h ../include/const.h \
+  ../include/sys/stat.h 
+open.o : open.c ../include/string.h ../include/errno.h ../include/fcntl.h \
+  ../include/sys/types.h ../include/utime.h ../include/sys/stat.h \
+  ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
+  ../include/linux/mm.h ../include/linux/tty.h ../include/termios.h \
+  ../include/linux/kernel.h ../include/asm/segment.h 
+pipe.o : pipe.c ../include/signal.h ../include/sys/types.h \
+  ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
+  ../include/linux/mm.h ../include/asm/segment.h 
+read_write.o : read_write.c ../include/sys/stat.h ../include/sys/types.h \
+  ../include/errno.h ../include/linux/kernel.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
+  ../include/asm/segment.h 
+stat.o : stat.c ../include/errno.h ../include/sys/stat.h \
+  ../include/sys/types.h ../include/linux/fs.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/mm.h ../include/linux/kernel.h \
+  ../include/asm/segment.h 
+super.o : super.c ../include/linux/config.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+  ../include/linux/mm.h ../include/linux/kernel.h 
+truncate.o : truncate.c ../include/linux/sched.h ../include/linux/head.h \
+  ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
+  ../include/sys/stat.h 
+tty_ioctl.o : tty_ioctl.c ../include/errno.h ../include/termios.h \
+  ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
+  ../include/sys/types.h ../include/linux/mm.h ../include/linux/kernel.h \
+  ../include/linux/tty.h ../include/asm/segment.h ../include/asm/system.h 
diff --git a/fs/bitmap.c b/fs/bitmap.c
new file mode 100644
index 0000000..49bb81b
--- /dev/null
+++ b/fs/bitmap.c
@@ -0,0 +1,158 @@
+/* bitmap.c contains the code that handles the inode and block bitmaps */
+#include <string.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+
+#define clear_block(addr) \
+__asm__("cld\n\t" \
+	"rep\n\t" \
+	"stosl" \
+	::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)):"cx","di")
+
+#define set_bit(nr,addr) ({\
+register int res __asm__("ax"); \
+__asm__("btsl %2,%3\n\tsetb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
+res;})
+
+#define clear_bit(nr,addr) ({\
+register int res __asm__("ax"); \
+__asm__("btrl %2,%3\n\tsetnb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
+res;})
+
+#define find_first_zero(addr) ({ \
+int __res; \
+__asm__("cld\n" \
+	"1:\tlodsl\n\t" \
+	"notl %%eax\n\t" \
+	"bsfl %%eax,%%edx\n\t" \
+	"je 2f\n\t" \
+	"addl %%edx,%%ecx\n\t" \
+	"jmp 3f\n" \
+	"2:\taddl $32,%%ecx\n\t" \
+	"cmpl $8192,%%ecx\n\t" \
+	"jl 1b\n" \
+	"3:" \
+	:"=c" (__res):"c" (0),"S" (addr):"ax","dx","si"); \
+__res;})
+
+void free_block(int dev, int block)
+{
+	struct super_block * sb;
+	struct buffer_head * bh;
+
+	if (!(sb = get_super(dev)))
+		panic("trying to free block on nonexistent device");
+	if (block < sb->s_firstdatazone || block >= sb->s_nzones)
+		panic("trying to free block not in datazone");
+	bh = get_hash_table(dev,block);
+	if (bh) {
+		if (bh->b_count != 1) {
+			printk("trying to free block (%04x:%d), count=%d\n",
+				dev,block,bh->b_count);
+			return;
+		}
+		bh->b_dirt=0;
+		bh->b_uptodate=0;
+		brelse(bh);
+	}
+	block -= sb->s_firstdatazone - 1 ;
+	if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) {
+		printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);
+		panic("free_block: bit already cleared");
+	}
+	sb->s_zmap[block/8192]->b_dirt = 1;
+}
+
+int new_block(int dev)
+{
+	struct buffer_head * bh;
+	struct super_block * sb;
+	int i,j;
+
+	if (!(sb = get_super(dev)))
+		panic("trying to get new block from nonexistant device");
+	j = 8192;
+	for (i=0 ; i<8 ; i++)
+		if (bh=sb->s_zmap[i])
+			if ((j=find_first_zero(bh->b_data))<8192)
+				break;
+	if (i>=8 || !bh || j>=8192)
+		return 0;
+	if (set_bit(j,bh->b_data))
+		panic("new_block: bit already set");
+	bh->b_dirt = 1;
+	j += i*8192 + sb->s_firstdatazone-1;
+	if (j >= sb->s_nzones)
+		return 0;
+	if (!(bh=getblk(dev,j)))
+		panic("new_block: cannot get block");
+	if (bh->b_count != 1)
+		panic("new block: count is != 1");
+	clear_block(bh->b_data);
+	bh->b_uptodate = 1;
+	bh->b_dirt = 1;
+	brelse(bh);
+	return j;
+}
+
+void free_inode(struct m_inode * inode)
+{
+	struct super_block * sb;
+	struct buffer_head * bh;
+
+	if (!inode)
+		return;
+	if (!inode->i_dev) {
+		memset(inode,0,sizeof(*inode));
+		return;
+	}
+	if (inode->i_count>1) {
+		printk("trying to free inode with count=%d\n",inode->i_count);
+		panic("free_inode");
+	}
+	if (inode->i_nlinks)
+		panic("trying to free inode with links");
+	if (!(sb = get_super(inode->i_dev)))
+		panic("trying to free inode on nonexistent device");
+	if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)
+		panic("trying to free inode 0 or nonexistant inode");
+	if (!(bh=sb->s_imap[inode->i_num>>13]))
+		panic("nonexistent imap in superblock");
+	if (clear_bit(inode->i_num&8191,bh->b_data))
+		panic("free_inode: bit already cleared");
+	bh->b_dirt = 1;
+	memset(inode,0,sizeof(*inode));
+}
+
+struct m_inode * new_inode(int dev)
+{
+	struct m_inode * inode;
+	struct super_block * sb;
+	struct buffer_head * bh;
+	int i,j;
+
+	if (!(inode=get_empty_inode()))
+		return NULL;
+	if (!(sb = get_super(dev)))
+		panic("new_inode with unknown device");
+	j = 8192;
+	for (i=0 ; i<8 ; i++)
+		if (bh=sb->s_imap[i])
+			if ((j=find_first_zero(bh->b_data))<8192)
+				break;
+	if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) {
+		iput(inode);
+		return NULL;
+	}
+	if (set_bit(j,bh->b_data))
+		panic("new_inode: bit already set");
+	bh->b_dirt = 1;
+	inode->i_count=1;
+	inode->i_nlinks=1;
+	inode->i_dev=dev;
+	inode->i_dirt=1;
+	inode->i_num = j + i*8192;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	return inode;
+}
diff --git a/fs/block_dev.c b/fs/block_dev.c
new file mode 100644
index 0000000..7bbae6a
--- /dev/null
+++ b/fs/block_dev.c
@@ -0,0 +1,86 @@
+#include <errno.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+#define NR_BLK_DEV ((sizeof (rd_blk))/(sizeof (rd_blk[0])))
+
+int block_write(int dev, long * pos, char * buf, int count)
+{
+	int block = *pos / BLOCK_SIZE;
+	int offset = *pos % BLOCK_SIZE;
+	int chars;
+	int written = 0;
+	struct buffer_head * bh;
+	register char * p;
+
+	while (count>0) {
+		bh = bread(dev,block);
+		if (!bh)
+			return written?written:-EIO;
+		chars = (count<BLOCK_SIZE) ? count : BLOCK_SIZE;
+		p = offset + bh->b_data;
+		offset = 0;
+		block++;
+		*pos += chars;
+		written += chars;
+		count -= chars;
+		while (chars-->0)
+			*(p++) = get_fs_byte(buf++);
+		bh->b_dirt = 1;
+		brelse(bh);
+	}
+	return written;
+}
+
+int block_read(int dev, unsigned long * pos, char * buf, int count)
+{
+	int block = *pos / BLOCK_SIZE;
+	int offset = *pos % BLOCK_SIZE;
+	int chars;
+	int read = 0;
+	struct buffer_head * bh;
+	register char * p;
+
+	while (count>0) {
+		bh = bread(dev,block);
+		if (!bh)
+			return read?read:-EIO;
+		chars = (count<BLOCK_SIZE) ? count : BLOCK_SIZE;
+		p = offset + bh->b_data;
+		offset = 0;
+		block++;
+		*pos += chars;
+		read += chars;
+		count -= chars;
+		while (chars-->0)
+			put_fs_byte(*(p++),buf++);
+		bh->b_dirt = 1;
+		brelse(bh);
+	}
+	return read;
+}
+
+extern void rw_hd(int rw, struct buffer_head * bh);
+
+typedef void (*blk_fn)(int rw, struct buffer_head * bh);
+
+static blk_fn rd_blk[]={
+	NULL,		/* nodev */
+	NULL,		/* dev mem */
+	NULL,		/* dev fd */
+	rw_hd,		/* dev hd */
+	NULL,		/* dev ttyx */
+	NULL,		/* dev tty */
+	NULL};		/* dev lp */
+
+void ll_rw_block(int rw, struct buffer_head * bh)
+{
+	blk_fn blk_addr;
+	unsigned int major;
+
+	if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV || !(blk_addr=rd_blk[major]))
+		panic("Trying to read nonexistent block-device");
+	blk_addr(rw, bh);
+}
diff --git a/fs/buffer.c b/fs/buffer.c
new file mode 100644
index 0000000..312ab17
--- /dev/null
+++ b/fs/buffer.c
@@ -0,0 +1,254 @@
+/*
+ *  'buffer.c' implements the buffer-cache functions. Race-conditions have
+ * been avoided by NEVER letting a interrupt change a buffer (except for the
+ * data, of course), but instead letting the caller do it. NOTE! As interrupts
+ * can wake up a caller, some cli-sti sequences are needed to check for
+ * sleep-on-calls. These should be extremely quick, though (I hope).
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+
+#if (BUFFER_END & 0xfff)
+#error "Bad BUFFER_END value"
+#endif
+
+#if (BUFFER_END > 0xA0000 && BUFFER_END <= 0x100000)
+#error "Bad BUFFER_END value"
+#endif
+
+extern int end;
+struct buffer_head * start_buffer = (struct buffer_head *) &end;
+struct buffer_head * hash_table[NR_HASH];
+static struct buffer_head * free_list;
+static struct task_struct * buffer_wait = NULL;
+int NR_BUFFERS = 0;
+
+static inline void wait_on_buffer(struct buffer_head * bh)
+{
+	cli();
+	while (bh->b_lock)
+		sleep_on(&bh->b_wait);
+	sti();
+}
+
+int sys_sync(void)
+{
+	int i;
+	struct buffer_head * bh;
+
+	sync_inodes();		/* write out inodes into buffers */
+	bh = start_buffer;
+	for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
+		wait_on_buffer(bh);
+		if (bh->b_dirt)
+			ll_rw_block(WRITE,bh);
+	}
+	return 0;
+}
+
+static int sync_dev(int dev)
+{
+	int i;
+	struct buffer_head * bh;
+
+	bh = start_buffer;
+	for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
+		if (bh->b_dev != dev)
+			continue;
+		wait_on_buffer(bh);
+		if (bh->b_dirt)
+			ll_rw_block(WRITE,bh);
+	}
+	return 0;
+}
+
+#define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
+#define hash(dev,block) hash_table[_hashfn(dev,block)]
+
+static inline void remove_from_queues(struct buffer_head * bh)
+{
+/* remove from hash-queue */
+	if (bh->b_next)
+		bh->b_next->b_prev = bh->b_prev;
+	if (bh->b_prev)
+		bh->b_prev->b_next = bh->b_next;
+	if (hash(bh->b_dev,bh->b_blocknr) == bh)
+		hash(bh->b_dev,bh->b_blocknr) = bh->b_next;
+/* remove from free list */
+	if (!(bh->b_prev_free) || !(bh->b_next_free))
+		panic("Free block list corrupted");
+	bh->b_prev_free->b_next_free = bh->b_next_free;
+	bh->b_next_free->b_prev_free = bh->b_prev_free;
+	if (free_list == bh)
+		free_list = bh->b_next_free;
+}
+
+static inline void insert_into_queues(struct buffer_head * bh)
+{
+/* put at end of free list */
+	bh->b_next_free = free_list;
+	bh->b_prev_free = free_list->b_prev_free;
+	free_list->b_prev_free->b_next_free = bh;
+	free_list->b_prev_free = bh;
+/* put the buffer in new hash-queue if it has a device */
+	bh->b_prev = NULL;
+	bh->b_next = NULL;
+	if (!bh->b_dev)
+		return;
+	bh->b_next = hash(bh->b_dev,bh->b_blocknr);
+	hash(bh->b_dev,bh->b_blocknr) = bh;
+	bh->b_next->b_prev = bh;
+}
+
+static struct buffer_head * find_buffer(int dev, int block)
+{		
+	struct buffer_head * tmp;
+
+	for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
+		if (tmp->b_dev==dev && tmp->b_blocknr==block)
+			return tmp;
+	return NULL;
+}
+
+/*
+ * Why like this, I hear you say... The reason is race-conditions.
+ * As we don't lock buffers (unless we are readint them, that is),
+ * something might happen to it while we sleep (ie a read-error
+ * will force it bad). This shouldn't really happen currently, but
+ * the code is ready.
+ */
+struct buffer_head * get_hash_table(int dev, int block)
+{
+	struct buffer_head * bh;
+
+repeat:
+	if (!(bh=find_buffer(dev,block)))
+		return NULL;
+	bh->b_count++;
+	wait_on_buffer(bh);
+	if (bh->b_dev != dev || bh->b_blocknr != block) {
+		brelse(bh);
+		goto repeat;
+	}
+	return bh;
+}
+
+/*
+ * Ok, this is getblk, and it isn't very clear, again to hinder
+ * race-conditions. Most of the code is seldom used, (ie repeating),
+ * so it should be much more efficient than it looks.
+ */
+struct buffer_head * getblk(int dev,int block)
+{
+	struct buffer_head * tmp;
+
+repeat:
+	if (tmp=get_hash_table(dev,block))
+		return tmp;
+	tmp = free_list;
+	do {
+		if (!tmp->b_count) {
+			wait_on_buffer(tmp);	/* we still have to wait */
+			if (!tmp->b_count)	/* on it, it might be dirty */
+				break;
+		}
+		tmp = tmp->b_next_free;
+	} while (tmp != free_list || (tmp=NULL));
+	/* Kids, don't try THIS at home ^^^^^. Magic */
+	if (!tmp) {
+		printk("Sleeping on free buffer ..");
+		sleep_on(&buffer_wait);
+		printk("ok\n");
+		goto repeat;
+	}
+	tmp->b_count++;
+	remove_from_queues(tmp);
+/*
+ * Now, when we know nobody can get to this node (as it's removed from the
+ * free list), we write it out. We can sleep here without fear of race-
+ * conditions.
+ */
+	if (tmp->b_dirt)
+		sync_dev(tmp->b_dev);
+/* update buffer contents */
+	tmp->b_dev=dev;
+	tmp->b_blocknr=block;
+	tmp->b_dirt=0;
+	tmp->b_uptodate=0;
+/* NOTE!! While we possibly slept in sync_dev(), somebody else might have
+ * added "this" block already, so check for that. Thank God for goto's.
+ */
+	if (find_buffer(dev,block)) {
+		tmp->b_dev=0;		/* ok, someone else has beaten us */
+		tmp->b_blocknr=0;	/* to it - free this block and */
+		tmp->b_count=0;		/* try again */
+		insert_into_queues(tmp);
+		goto repeat;
+	}
+/* and then insert into correct position */
+	insert_into_queues(tmp);
+	return tmp;
+}
+
+void brelse(struct buffer_head * buf)
+{
+	if (!buf)
+		return;
+	wait_on_buffer(buf);
+	if (!(buf->b_count--))
+		panic("Trying to free free buffer");
+	wake_up(&buffer_wait);
+}
+
+/*
+ * bread() reads a specified block and returns the buffer that contains
+ * it. It returns NULL if the block was unreadable.
+ */
+struct buffer_head * bread(int dev,int block)
+{
+	struct buffer_head * bh;
+
+	if (!(bh=getblk(dev,block)))
+		panic("bread: getblk returned NULL\n");
+	if (bh->b_uptodate)
+		return bh;
+	ll_rw_block(READ,bh);
+	if (bh->b_uptodate)
+		return bh;
+	brelse(bh);
+	return (NULL);
+}
+
+void buffer_init(void)
+{
+	struct buffer_head * h = start_buffer;
+	void * b = (void *) BUFFER_END;
+	int i;
+
+	while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) {
+		h->b_dev = 0;
+		h->b_dirt = 0;
+		h->b_count = 0;
+		h->b_lock = 0;
+		h->b_uptodate = 0;
+		h->b_wait = NULL;
+		h->b_next = NULL;
+		h->b_prev = NULL;
+		h->b_data = (char *) b;
+		h->b_prev_free = h-1;
+		h->b_next_free = h+1;
+		h++;
+		NR_BUFFERS++;
+		if (b == (void *) 0x100000)
+			b = (void *) 0xA0000;
+	}
+	h--;
+	free_list = start_buffer;
+	free_list->b_prev_free = h;
+	h->b_next_free = free_list;
+	for (i=0;i<NR_HASH;i++)
+		hash_table[i]=NULL;
+}	
diff --git a/fs/char_dev.c b/fs/char_dev.c
new file mode 100644
index 0000000..e974242
--- /dev/null
+++ b/fs/char_dev.c
@@ -0,0 +1,50 @@
+#include <errno.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+
+extern int tty_read(unsigned minor,char * buf,int count);
+extern int tty_write(unsigned minor,char * buf,int count);
+
+static int rw_ttyx(int rw,unsigned minor,char * buf,int count);
+static int rw_tty(int rw,unsigned minor,char * buf,int count);
+
+typedef (*crw_ptr)(int rw,unsigned minor,char * buf,int count);
+
+#define NRDEVS ((sizeof (crw_table))/(sizeof (crw_ptr)))
+
+static crw_ptr crw_table[]={
+	NULL,		/* nodev */
+	NULL,		/* /dev/mem */
+	NULL,		/* /dev/fd */
+	NULL,		/* /dev/hd */
+	rw_ttyx,	/* /dev/ttyx */
+	rw_tty,		/* /dev/tty */
+	NULL,		/* /dev/lp */
+	NULL};		/* unnamed pipes */
+
+static int rw_ttyx(int rw,unsigned minor,char * buf,int count)
+{
+	return ((rw==READ)?tty_read(minor,buf,count):
+		tty_write(minor,buf,count));
+}
+
+static int rw_tty(int rw,unsigned minor,char * buf,int count)
+{
+	if (current->tty<0)
+		return -EPERM;
+	return rw_ttyx(rw,current->tty,buf,count);
+}
+
+int rw_char(int rw,int dev, char * buf, int count)
+{
+	crw_ptr call_addr;
+
+	if (MAJOR(dev)>=NRDEVS)
+		panic("rw_char: dev>NRDEV");
+	if (!(call_addr=crw_table[MAJOR(dev)])) {
+		printk("dev: %04x\n",dev);
+		panic("Trying to r/w from/to nonexistent character device");
+	}
+	return call_addr(rw,MINOR(dev),buf,count);
+}
diff --git a/fs/exec.c b/fs/exec.c
new file mode 100644
index 0000000..e9d5058
--- /dev/null
+++ b/fs/exec.c
@@ -0,0 +1,306 @@
+#include <errno.h>
+#include <sys/stat.h>
+#include <a.out.h>
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+extern int sys_exit(int exit_code);
+extern int sys_close(int fd);
+
+/*
+ * MAX_ARG_PAGES defines the number of pages allocated for arguments
+ * and envelope for the new program. 32 should suffice, this gives
+ * a maximum env+arg of 128kB !
+ */
+#define MAX_ARG_PAGES 32
+
+#define cp_block(from,to) \
+__asm__("pushl $0x10\n\t" \
+	"pushl $0x17\n\t" \
+	"pop %%es\n\t" \
+	"cld\n\t" \
+	"rep\n\t" \
+	"movsl\n\t" \
+	"pop %%es" \
+	::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \
+	:"cx","di","si")
+
+/*
+ * read_head() reads blocks 1-6 (not 0). Block 0 has already been
+ * read for header information.
+ */
+int read_head(struct m_inode * inode,int blocks)
+{
+	struct buffer_head * bh;
+	int count;
+
+	if (blocks>6)
+		blocks=6;
+	for(count = 0 ; count<blocks ; count++) {
+		if (!inode->i_zone[count+1])
+			continue;
+		if (!(bh=bread(inode->i_dev,inode->i_zone[count+1])))
+			return -1;
+		cp_block(bh->b_data,count*BLOCK_SIZE);
+		brelse(bh);
+	}
+	return 0;
+}
+
+int read_ind(int dev,int ind,long size,unsigned long offset)
+{
+	struct buffer_head * ih, * bh;
+	unsigned short * table,block;
+
+	if (size<=0)
+		panic("size<=0 in read_ind");
+	if (size>512*BLOCK_SIZE)
+		size=512*BLOCK_SIZE;
+	if (!ind)
+		return 0;
+	if (!(ih=bread(dev,ind)))
+		return -1;
+	table = (unsigned short *) ih->b_data;
+	while (size>0) {
+		if (block=*(table++))
+			if (!(bh=bread(dev,block))) {
+				brelse(ih);
+				return -1;
+			} else {
+				cp_block(bh->b_data,offset);
+				brelse(bh);
+			}
+		size -= BLOCK_SIZE;
+		offset += BLOCK_SIZE;
+	}
+	brelse(ih);
+	return 0;
+}
+
+/*
+ * read_area() reads an area into %fs:mem.
+ */
+int read_area(struct m_inode * inode,long size)
+{
+	struct buffer_head * dind;
+	unsigned short * table;
+	int i,count;
+
+	if ((i=read_head(inode,(size+BLOCK_SIZE-1)/BLOCK_SIZE)) ||
+	    (size -= BLOCK_SIZE*6)<=0)
+		return i;
+	if ((i=read_ind(inode->i_dev,inode->i_zone[7],size,BLOCK_SIZE*6)) ||
+	    (size -= BLOCK_SIZE*512)<=0)
+		return i;
+	if (!(i=inode->i_zone[8]))
+		return 0;
+	if (!(dind = bread(inode->i_dev,i)))
+		return -1;
+	table = (unsigned short *) dind->b_data;
+	for(count=0 ; count<512 ; count++)
+		if ((i=read_ind(inode->i_dev,*(table++),size,
+		    BLOCK_SIZE*(518+count))) || (size -= BLOCK_SIZE*512)<=0)
+			return i;
+	panic("Impossibly long executable");
+}
+
+/*
+ * create_tables() parses the env- and arg-strings in new user
+ * memory and creates the pointer tables from them, and puts their
+ * addresses on the "stack", returning the new stack pointer value.
+ */
+static unsigned long * create_tables(char * p,int argc,int envc)
+{
+	unsigned long *argv,*envp;
+	unsigned long * sp;
+
+	sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
+	sp -= envc+1;
+	envp = sp;
+	sp -= argc+1;
+	argv = sp;
+	put_fs_long((unsigned long)envp,--sp);
+	put_fs_long((unsigned long)argv,--sp);
+	put_fs_long((unsigned long)argc,--sp);
+	while (argc-->0) {
+		put_fs_long((unsigned long) p,argv++);
+		while (get_fs_byte(p++)) /* nothing */ ;
+	}
+	put_fs_long(0,argv);
+	while (envc-->0) {
+		put_fs_long((unsigned long) p,envp++);
+		while (get_fs_byte(p++)) /* nothing */ ;
+	}
+	put_fs_long(0,envp);
+	return sp;
+}
+
+/*
+ * count() counts the number of arguments/envelopes
+ */
+static int count(char ** argv)
+{
+	int i=0;
+	char ** tmp;
+
+	if (tmp = argv)
+		while (get_fs_long((unsigned long *) (tmp++)))
+			i++;
+
+	return i;
+}
+
+/*
+ * 'copy_string()' copies argument/envelope strings from user
+ * memory to free pages in kernel mem. These are in a format ready
+ * to be put directly into the top of new user memory.
+ */
+static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
+		unsigned long p)
+{
+	int len,i;
+	char *tmp;
+
+	while (argc-- > 0) {
+		if (!(tmp = (char *)get_fs_long(((unsigned long *) argv)+argc)))
+			panic("argc is wrong");
+		len=0;		/* remember zero-padding */
+		do {
+			len++;
+		} while (get_fs_byte(tmp++));
+		if (p-len < 0)		/* this shouldn't happen - 128kB */
+			return 0;
+		i = ((unsigned) (p-len)) >> 12;
+		while (i<MAX_ARG_PAGES && !page[i]) {
+			if (!(page[i]=get_free_page()))
+				return 0;
+			i++;
+		}
+		do {
+			--p;
+			if (!page[p/PAGE_SIZE])
+				panic("nonexistent page in exec.c");
+			((char *) page[p/PAGE_SIZE])[p%PAGE_SIZE] =
+				get_fs_byte(--tmp);
+		} while (--len);
+	}
+	return p;
+}
+
+static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
+{
+	unsigned long code_limit,data_limit,code_base,data_base;
+	int i;
+
+	code_limit = text_size+PAGE_SIZE -1;
+	code_limit &= 0xFFFFF000;
+	data_limit = 0x4000000;
+	code_base = get_base(current->ldt[1]);
+	data_base = code_base;
+	set_base(current->ldt[1],code_base);
+	set_limit(current->ldt[1],code_limit);
+	set_base(current->ldt[2],data_base);
+	set_limit(current->ldt[2],data_limit);
+/* make sure fs points to the NEW data segment */
+	__asm__("pushl $0x17\n\tpop %%fs"::);
+	data_base += data_limit;
+	for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
+		data_base -= PAGE_SIZE;
+		if (page[i])
+			put_page(page[i],data_base);
+	}
+	return data_limit;
+}
+
+/*
+ * 'do_execve()' executes a new program.
+ */
+int do_execve(unsigned long * eip,long tmp,char * filename,
+	char ** argv, char ** envp)
+{
+	struct m_inode * inode;
+	struct buffer_head * bh;
+	struct exec ex;
+	unsigned long page[MAX_ARG_PAGES];
+	int i,argc,envc;
+	unsigned long p;
+
+	if ((0xffff & eip[1]) != 0x000f)
+		panic("execve called from supervisor mode");
+	for (i=0 ; i<MAX_ARG_PAGES ; i++)	/* clear page-table */
+		page[i]=0;
+	if (!(inode=namei(filename)))		/* get executables inode */
+		return -ENOENT;
+	if (!S_ISREG(inode->i_mode)) {	/* must be regular file */
+		iput(inode);
+		return -EACCES;
+	}
+	i = inode->i_mode;
+	if (current->uid && current->euid) {
+		if (current->euid == inode->i_uid)
+			i >>= 6;
+		else if (current->egid == inode->i_gid)
+			i >>= 3;
+	} else if (i & 0111)
+		i=1;
+	if (!(i & 1)) {
+		iput(inode);
+		return -ENOEXEC;
+	}
+	if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) {
+		iput(inode);
+		return -EACCES;
+	}
+	ex = *((struct exec *) bh->b_data);	/* read exec-header */
+	brelse(bh);
+	if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||
+		ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||
+		inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
+		iput(inode);
+		return -ENOEXEC;
+	}
+	if (N_TXTOFF(ex) != BLOCK_SIZE)
+		panic("N_TXTOFF != BLOCK_SIZE. See a.out.h.");
+	argc = count(argv);
+	envc = count(envp);
+	p = copy_strings(envc,envp,page,PAGE_SIZE*MAX_ARG_PAGES-4);
+	p = copy_strings(argc,argv,page,p);
+	if (!p) {
+		for (i=0 ; i<MAX_ARG_PAGES ; i++)
+			free_page(page[i]);
+		iput(inode);
+		return -1;
+	}
+/* OK, This is the point of no return */
+	for (i=0 ; i<32 ; i++)
+		current->sig_fn[i] = NULL;
+	for (i=0 ; i<NR_OPEN ; i++)
+		if ((current->close_on_exec>>i)&1)
+			sys_close(i);
+	current->close_on_exec = 0;
+	free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
+	free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
+	if (last_task_used_math == current)
+		last_task_used_math = NULL;
+	current->used_math = 0;
+	p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE;
+	p = (unsigned long) create_tables((char *)p,argc,envc);
+	current->brk = ex.a_bss +
+		(current->end_data = ex.a_data +
+		(current->end_code = ex.a_text));
+	current->start_stack = p & 0xfffff000;
+	i = read_area(inode,ex.a_text+ex.a_data);
+	iput(inode);
+	if (i<0)
+		sys_exit(-1);
+	i = ex.a_text+ex.a_data;
+	while (i&0xfff)
+		put_fs_byte(0,(char *) (i++));
+	eip[0] = ex.a_entry;		/* eip, magic happens :-) */
+	eip[3] = p;			/* stack pointer */
+	return 0;
+}
diff --git a/fs/fcntl.c b/fs/fcntl.c
new file mode 100644
index 0000000..169f376
--- /dev/null
+++ b/fs/fcntl.c
@@ -0,0 +1,69 @@
+#include <string.h>
+#include <errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+extern int sys_close(int fd);
+
+static int dupfd(unsigned int fd, unsigned int arg)
+{
+	if (fd >= NR_OPEN || !current->filp[fd])
+		return -EBADF;
+	if (arg >= NR_OPEN)
+		return -EINVAL;
+	while (arg < NR_OPEN)
+		if (current->filp[arg])
+			arg++;
+		else
+			break;
+	if (arg >= NR_OPEN)
+		return -EMFILE;
+	current->close_on_exec &= ~(1<<arg);
+	(current->filp[arg] = current->filp[fd])->f_count++;
+	return arg;
+}
+
+int sys_dup2(unsigned int oldfd, unsigned int newfd)
+{
+	sys_close(newfd);
+	return dupfd(oldfd,newfd);
+}
+
+int sys_dup(unsigned int fildes)
+{
+	return dupfd(fildes,0);
+}
+
+int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{	
+	struct file * filp;
+
+	if (fd >= NR_OPEN || !(filp = current->filp[fd]))
+		return -EBADF;
+	switch (cmd) {
+		case F_DUPFD:
+			return dupfd(fd,arg);
+		case F_GETFD:
+			return (current->close_on_exec>>fd)&1;
+		case F_SETFD:
+			if (arg&1)
+				current->close_on_exec |= (1<<fd);
+			else
+				current->close_on_exec &= ~(1<<fd);
+			return 0;
+		case F_GETFL:
+			return filp->f_flags;
+		case F_SETFL:
+			filp->f_flags &= ~(O_APPEND | O_NONBLOCK);
+			filp->f_flags |= arg & (O_APPEND | O_NONBLOCK);
+			return 0;
+		case F_GETLK:	case F_SETLK:	case F_SETLKW:
+			return -1;
+		default:
+			return -1;
+	}
+}
diff --git a/fs/file_dev.c b/fs/file_dev.c
new file mode 100644
index 0000000..da5d014
--- /dev/null
+++ b/fs/file_dev.c
@@ -0,0 +1,84 @@
+#include <errno.h>
+#include <fcntl.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+int file_read(struct m_inode * inode, struct file * filp, char * buf, int count)
+{
+	int left,chars,nr;
+	struct buffer_head * bh;
+
+	if ((left=count)<=0)
+		return 0;
+	while (left) {
+		if (nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE)) {
+			if (!(bh=bread(inode->i_dev,nr)))
+				break;
+		} else
+			bh = NULL;
+		nr = filp->f_pos % BLOCK_SIZE;
+		chars = MIN( BLOCK_SIZE-nr , left );
+		filp->f_pos += chars;
+		left -= chars;
+		if (bh) {
+			char * p = nr + bh->b_data;
+			while (chars-->0)
+				put_fs_byte(*(p++),buf++);
+			brelse(bh);
+		} else {
+			while (chars-->0)
+				put_fs_byte(0,buf++);
+		}
+	}
+	inode->i_atime = CURRENT_TIME;
+	return (count-left)?(count-left):-ERROR;
+}
+
+int file_write(struct m_inode * inode, struct file * filp, char * buf, int count)
+{
+	off_t pos;
+	int block,c;
+	struct buffer_head * bh;
+	char * p;
+	int i=0;
+
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+	if (filp->f_flags & O_APPEND)
+		pos = inode->i_size;
+	else
+		pos = filp->f_pos;
+	while (i<count) {
+		if (!(block = create_block(inode,pos/BLOCK_SIZE)))
+			break;
+		if (!(bh=bread(inode->i_dev,block)))
+			break;
+		c = pos % BLOCK_SIZE;
+		p = c + bh->b_data;
+		bh->b_dirt = 1;
+		c = BLOCK_SIZE-c;
+		if (c > count-i) c = count-i;
+		pos += c;
+		if (pos > inode->i_size) {
+			inode->i_size = pos;
+			inode->i_dirt = 1;
+		}
+		i += c;
+		while (c-->0)
+			*(p++) = get_fs_byte(buf++);
+		brelse(bh);
+	}
+	inode->i_mtime = CURRENT_TIME;
+	if (!(filp->f_flags & O_APPEND)) {
+		filp->f_pos = pos;
+		inode->i_ctime = CURRENT_TIME;
+	}
+	return (i?i:-1);
+}
diff --git a/fs/file_table.c b/fs/file_table.c
new file mode 100644
index 0000000..ab7cebf
--- /dev/null
+++ b/fs/file_table.c
@@ -0,0 +1,3 @@
+#include <linux/fs.h>
+
+struct file file_table[NR_FILE];
diff --git a/fs/inode.c b/fs/inode.c
new file mode 100644
index 0000000..d06ff90
--- /dev/null
+++ b/fs/inode.c
@@ -0,0 +1,288 @@
+#include <string.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <asm/system.h>
+
+struct m_inode inode_table[NR_INODE]={{0,},};
+
+static void read_inode(struct m_inode * inode);
+static void write_inode(struct m_inode * inode);
+
+static inline void wait_on_inode(struct m_inode * inode)
+{
+	cli();
+	while (inode->i_lock)
+		sleep_on(&inode->i_wait);
+	sti();
+}
+
+static inline void lock_inode(struct m_inode * inode)
+{
+	cli();
+	while (inode->i_lock)
+		sleep_on(&inode->i_wait);
+	inode->i_lock=1;
+	sti();
+}
+
+static inline void unlock_inode(struct m_inode * inode)
+{
+	inode->i_lock=0;
+	wake_up(&inode->i_wait);
+}
+
+void sync_inodes(void)
+{
+	int i;
+	struct m_inode * inode;
+
+	inode = 0+inode_table;
+	for(i=0 ; i<NR_INODE ; i++,inode++) {
+		wait_on_inode(inode);
+		if (inode->i_dirt && !inode->i_pipe)
+			write_inode(inode);
+	}
+}
+
+static int _bmap(struct m_inode * inode,int block,int create)
+{
+	struct buffer_head * bh;
+	int i;
+
+	if (block<0)
+		panic("_bmap: block<0");
+	if (block >= 7+512+512*512)
+		panic("_bmap: block>big");
+	if (block<7) {
+		if (create && !inode->i_zone[block])
+			if (inode->i_zone[block]=new_block(inode->i_dev)) {
+				inode->i_ctime=CURRENT_TIME;
+				inode->i_dirt=1;
+			}
+		return inode->i_zone[block];
+	}
+	block -= 7;
+	if (block<512) {
+		if (create && !inode->i_zone[7])
+			if (inode->i_zone[7]=new_block(inode->i_dev)) {
+				inode->i_dirt=1;
+				inode->i_ctime=CURRENT_TIME;
+			}
+		if (!inode->i_zone[7])
+			return 0;
+		if (!(bh = bread(inode->i_dev,inode->i_zone[7])))
+			return 0;
+		i = ((unsigned short *) (bh->b_data))[block];
+		if (create && !i)
+			if (i=new_block(inode->i_dev)) {
+				((unsigned short *) (bh->b_data))[block]=i;
+				bh->b_dirt=1;
+			}
+		brelse(bh);
+		return i;
+	}
+	block -= 512;
+	if (create && !inode->i_zone[8])
+		if (inode->i_zone[8]=new_block(inode->i_dev)) {
+			inode->i_dirt=1;
+			inode->i_ctime=CURRENT_TIME;
+		}
+	if (!inode->i_zone[8])
+		return 0;
+	if (!(bh=bread(inode->i_dev,inode->i_zone[8])))
+		return 0;
+	i = ((unsigned short *)bh->b_data)[block>>9];
+	if (create && !i)
+		if (i=new_block(inode->i_dev)) {
+			((unsigned short *) (bh->b_data))[block>>9]=i;
+			bh->b_dirt=1;
+		}
+	brelse(bh);
+	if (!i)
+		return 0;
+	if (!(bh=bread(inode->i_dev,i)))
+		return 0;
+	i = ((unsigned short *)bh->b_data)[block&511];
+	if (create && !i)
+		if (i=new_block(inode->i_dev)) {
+			((unsigned short *) (bh->b_data))[block&511]=i;
+			bh->b_dirt=1;
+		}
+	brelse(bh);
+	return i;
+}
+
+int bmap(struct m_inode * inode,int block)
+{
+	return _bmap(inode,block,0);
+}
+
+int create_block(struct m_inode * inode, int block)
+{
+	return _bmap(inode,block,1);
+}
+		
+void iput(struct m_inode * inode)
+{
+	if (!inode)
+		return;
+	wait_on_inode(inode);
+	if (!inode->i_count)
+		panic("iput: trying to free free inode");
+	if (inode->i_pipe) {
+		wake_up(&inode->i_wait);
+		if (--inode->i_count)
+			return;
+		free_page(inode->i_size);
+		inode->i_count=0;
+		inode->i_dirt=0;
+		inode->i_pipe=0;
+		return;
+	}
+	if (!inode->i_dev || inode->i_count>1) {
+		inode->i_count--;
+		return;
+	}
+repeat:
+	if (!inode->i_nlinks) {
+		truncate(inode);
+		free_inode(inode);
+		return;
+	}
+	if (inode->i_dirt) {
+		write_inode(inode);	/* we can sleep - so do again */
+		wait_on_inode(inode);
+		goto repeat;
+	}
+	inode->i_count--;
+	return;
+}
+
+static volatile int last_allocated_inode = 0;
+
+struct m_inode * get_empty_inode(void)
+{
+	struct m_inode * inode;
+	int inr;
+
+	while (1) {
+		inode = NULL;
+		inr = last_allocated_inode;
+		do {
+			if (!inode_table[inr].i_count) {
+				inode = inr + inode_table;
+				break;
+			}
+			inr++;
+			if (inr>=NR_INODE)
+				inr=0;
+		} while (inr != last_allocated_inode);
+		if (!inode) {
+			for (inr=0 ; inr<NR_INODE ; inr++)
+				printk("%04x: %6d\t",inode_table[inr].i_dev,
+					inode_table[inr].i_num);
+			panic("No free inodes in mem");
+		}
+		last_allocated_inode = inr;
+		wait_on_inode(inode);
+		while (inode->i_dirt) {
+			write_inode(inode);
+			wait_on_inode(inode);
+		}
+		if (!inode->i_count)
+			break;
+	}
+	memset(inode,0,sizeof(*inode));
+	inode->i_count = 1;
+	return inode;
+}
+
+struct m_inode * get_pipe_inode(void)
+{
+	struct m_inode * inode;
+
+	if (!(inode = get_empty_inode()))
+		return NULL;
+	if (!(inode->i_size=get_free_page())) {
+		inode->i_count = 0;
+		return NULL;
+	}
+	inode->i_count = 2;	/* sum of readers/writers */
+	PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;
+	inode->i_pipe = 1;
+	return inode;
+}
+
+struct m_inode * iget(int dev,int nr)
+{
+	struct m_inode * inode, * empty;
+
+	if (!dev)
+		panic("iget with dev==0");
+	empty = get_empty_inode();
+	inode = inode_table;
+	while (inode < NR_INODE+inode_table) {
+		if (inode->i_dev != dev || inode->i_num != nr) {
+			inode++;
+			continue;
+		}
+		wait_on_inode(inode);
+		if (inode->i_dev != dev || inode->i_num != nr) {
+			inode = inode_table;
+			continue;
+		}
+		inode->i_count++;
+		if (empty)
+			iput(empty);
+		return inode;
+	}
+	if (!empty)
+		return (NULL);
+	inode=empty;
+	inode->i_dev = dev;
+	inode->i_num = nr;
+	read_inode(inode);
+	return inode;
+}
+
+static void read_inode(struct m_inode * inode)
+{
+	struct super_block * sb;
+	struct buffer_head * bh;
+	int block;
+
+	lock_inode(inode);
+	sb=get_super(inode->i_dev);
+	block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
+		(inode->i_num-1)/INODES_PER_BLOCK;
+	if (!(bh=bread(inode->i_dev,block)))
+		panic("unable to read i-node block");
+	*(struct d_inode *)inode =
+		((struct d_inode *)bh->b_data)
+			[(inode->i_num-1)%INODES_PER_BLOCK];
+	brelse(bh);
+	unlock_inode(inode);
+}
+
+static void write_inode(struct m_inode * inode)
+{
+	struct super_block * sb;
+	struct buffer_head * bh;
+	int block;
+
+	lock_inode(inode);
+	sb=get_super(inode->i_dev);
+	block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
+		(inode->i_num-1)/INODES_PER_BLOCK;
+	if (!(bh=bread(inode->i_dev,block)))
+		panic("unable to read i-node block");
+	((struct d_inode *)bh->b_data)
+		[(inode->i_num-1)%INODES_PER_BLOCK] =
+			*(struct d_inode *)inode;
+	bh->b_dirt=1;
+	inode->i_dirt=0;
+	brelse(bh);
+	unlock_inode(inode);
+}
diff --git a/fs/ioctl.c b/fs/ioctl.c
new file mode 100644
index 0000000..131561b
--- /dev/null
+++ b/fs/ioctl.c
@@ -0,0 +1,40 @@
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <linux/sched.h>
+
+extern int tty_ioctl(int dev, int cmd, int arg);
+
+typedef int (*ioctl_ptr)(int dev,int cmd,int arg);
+
+#define NRDEVS ((sizeof (ioctl_table))/(sizeof (ioctl_ptr)))
+
+static ioctl_ptr ioctl_table[]={
+	NULL,		/* nodev */
+	NULL,		/* /dev/mem */
+	NULL,		/* /dev/fd */
+	NULL,		/* /dev/hd */
+	tty_ioctl,	/* /dev/ttyx */
+	tty_ioctl,	/* /dev/tty */
+	NULL,		/* /dev/lp */
+	NULL};		/* named pipes */
+	
+
+int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{	
+	struct file * filp;
+	int dev,mode;
+
+	if (fd >= NR_OPEN || !(filp = current->filp[fd]))
+		return -EBADF;
+	mode=filp->f_inode->i_mode;
+	if (!S_ISCHR(mode) && !S_ISBLK(mode))
+		return -EINVAL;
+	dev = filp->f_inode->i_zone[0];
+	if (MAJOR(dev) >= NRDEVS)
+		panic("unknown device for ioctl");
+	if (!ioctl_table[MAJOR(dev)])
+		return -ENOTTY;
+	return ioctl_table[MAJOR(dev)](dev,cmd,arg);
+}
diff --git a/fs/namei.c b/fs/namei.c
new file mode 100644
index 0000000..600737b
--- /dev/null
+++ b/fs/namei.c
@@ -0,0 +1,678 @@
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <const.h>
+#include <sys/stat.h>
+
+#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE])
+
+/*
+ * comment out this line if you want names > NAME_LEN chars to be
+ * truncated. Else they will be disallowed.
+ */
+/* #define NO_TRUNCATE */
+
+#define MAY_EXEC 1
+#define MAY_WRITE 2
+#define MAY_READ 4
+
+/*
+ *	permission()
+ *
+ * is used to check for read/write/execute permissions on a file.
+ * I don't know if we should look at just the euid or both euid and
+ * uid, but that should be easily changed.
+ */
+static int permission(struct m_inode * inode,int mask)
+{
+	int mode = inode->i_mode;
+
+/* special case: not even root can read/write a deleted file */
+	if (inode->i_dev && !inode->i_nlinks)
+		return 0;
+	if (!(current->uid && current->euid))
+		mode=0777;
+	else if (current->uid==inode->i_uid || current->euid==inode->i_uid)
+		mode >>= 6;
+	else if (current->gid==inode->i_gid || current->egid==inode->i_gid)
+		mode >>= 3;
+	return mode & mask & 0007;
+}
+
+/*
+ * ok, we cannot use strncmp, as the name is not in our data space.
+ * Thus we'll have to use match. No big problem. Match also makes
+ * some sanity tests.
+ *
+ * NOTE! unlike strncmp, match returns 1 for success, 0 for failure.
+ */
+static int match(int len,const char * name,struct dir_entry * de)
+{
+	register int same __asm__("ax");
+
+	if (!de || !de->inode || len > NAME_LEN)
+		return 0;
+	if (len < NAME_LEN && de->name[len])
+		return 0;
+	__asm__("cld\n\t"
+		"fs ; repe ; cmpsb\n\t"
+		"setz %%al"
+		:"=a" (same)
+		:"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
+		:"cx","di","si");
+	return same;
+}
+
+/*
+ *	find_entry()
+ *
+ * finds and entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as a parameter - res_dir). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ */
+static struct buffer_head * find_entry(struct m_inode * dir,
+	const char * name, int namelen, struct dir_entry ** res_dir)
+{
+	int entries;
+	int block,i;
+	struct buffer_head * bh;
+	struct dir_entry * de;
+
+#ifdef NO_TRUNCATE
+	if (namelen > NAME_LEN)
+		return NULL;
+#else
+	if (namelen > NAME_LEN)
+		namelen = NAME_LEN;
+#endif
+	entries = dir->i_size / (sizeof (struct dir_entry));
+	*res_dir = NULL;
+	if (!namelen)
+		return NULL;
+	if (!(block = dir->i_zone[0]))
+		return NULL;
+	if (!(bh = bread(dir->i_dev,block)))
+		return NULL;
+	i = 0;
+	de = (struct dir_entry *) bh->b_data;
+	while (i < entries) {
+		if ((char *)de >= BLOCK_SIZE+bh->b_data) {
+			brelse(bh);
+			bh = NULL;
+			if (!(block = bmap(dir,i/DIR_ENTRIES_PER_BLOCK)) ||
+			    !(bh = bread(dir->i_dev,block))) {
+				i += DIR_ENTRIES_PER_BLOCK;
+				continue;
+			}
+			de = (struct dir_entry *) bh->b_data;
+		}
+		if (match(namelen,name,de)) {
+			*res_dir = de;
+			return bh;
+		}
+		de++;
+		i++;
+	}
+	brelse(bh);
+	return NULL;
+}
+
+/*
+ *	add_entry()
+ *
+ * adds a file entry to the specified directory, using the same
+ * semantics as find_entry(). It returns NULL if it failed.
+ *
+ * NOTE!! The inode part of 'de' is left at 0 - which means you
+ * may not sleep between calling this and putting something into
+ * the entry, as someone else might have used it while you slept.
+ */
+static struct buffer_head * add_entry(struct m_inode * dir,
+	const char * name, int namelen, struct dir_entry ** res_dir)
+{
+	int block,i;
+	struct buffer_head * bh;
+	struct dir_entry * de;
+
+	*res_dir = NULL;
+#ifdef NO_TRUNCATE
+	if (namelen > NAME_LEN)
+		return NULL;
+#else
+	if (namelen > NAME_LEN)
+		namelen = NAME_LEN;
+#endif
+	if (!namelen)
+		return NULL;
+	if (!(block = dir->i_zone[0]))
+		return NULL;
+	if (!(bh = bread(dir->i_dev,block)))
+		return NULL;
+	i = 0;
+	de = (struct dir_entry *) bh->b_data;
+	while (1) {
+		if ((char *)de >= BLOCK_SIZE+bh->b_data) {
+			brelse(bh);
+			bh = NULL;
+			block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);
+			if (!block)
+				return NULL;
+			if (!(bh = bread(dir->i_dev,block))) {
+				i += DIR_ENTRIES_PER_BLOCK;
+				continue;
+			}
+			de = (struct dir_entry *) bh->b_data;
+		}
+		if (i*sizeof(struct dir_entry) >= dir->i_size) {
+			de->inode=0;
+			dir->i_size = (i+1)*sizeof(struct dir_entry);
+			dir->i_dirt = 1;
+			dir->i_ctime = CURRENT_TIME;
+		}
+		if (!de->inode) {
+			dir->i_mtime = CURRENT_TIME;
+			for (i=0; i < NAME_LEN ; i++)
+				de->name[i]=(i<namelen)?get_fs_byte(name+i):0;
+			bh->b_dirt = 1;
+			*res_dir = de;
+			return bh;
+		}
+		de++;
+		i++;
+	}
+	brelse(bh);
+	return NULL;
+}
+
+/*
+ *	get_dir()
+ *
+ * Getdir traverses the pathname until it hits the topmost directory.
+ * It returns NULL on failure.
+ */
+static struct m_inode * get_dir(const char * pathname)
+{
+	char c;
+	const char * thisname;
+	struct m_inode * inode;
+	struct buffer_head * bh;
+	int namelen,inr,idev;
+	struct dir_entry * de;
+
+	if (!current->root || !current->root->i_count)
+		panic("No root inode");
+	if (!current->pwd || !current->pwd->i_count)
+		panic("No cwd inode");
+	if ((c=get_fs_byte(pathname))=='/') {
+		inode = current->root;
+		pathname++;
+	} else if (c)
+		inode = current->pwd;
+	else
+		return NULL;	/* empty name is bad */
+	inode->i_count++;
+	while (1) {
+		thisname = pathname;
+		if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {
+			iput(inode);
+			return NULL;
+		}
+		for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)
+			/* nothing */ ;
+		if (!c)
+			return inode;
+		if (!(bh = find_entry(inode,thisname,namelen,&de))) {
+			iput(inode);
+			return NULL;
+		}
+		inr = de->inode;
+		idev = inode->i_dev;
+		brelse(bh);
+		iput(inode);
+		if (!(inode = iget(idev,inr)))
+			return NULL;
+	}
+}
+
+/*
+ *	dir_namei()
+ *
+ * dir_namei() returns the inode of the directory of the
+ * specified name, and the name within that directory.
+ */
+static struct m_inode * dir_namei(const char * pathname,
+	int * namelen, const char ** name)
+{
+	char c;
+	const char * basename;
+	struct m_inode * dir;
+
+	if (!(dir = get_dir(pathname)))
+		return NULL;
+	basename = pathname;
+	while (c=get_fs_byte(pathname++))
+		if (c=='/')
+			basename=pathname;
+	*namelen = pathname-basename-1;
+	*name = basename;
+	return dir;
+}
+
+/*
+ *	namei()
+ *
+ * is used by most simple commands to get the inode of a specified name.
+ * Open, link etc use their own routines, but this is enough for things
+ * like 'chmod' etc.
+ */
+struct m_inode * namei(const char * pathname)
+{
+	const char * basename;
+	int inr,dev,namelen;
+	struct m_inode * dir;
+	struct buffer_head * bh;
+	struct dir_entry * de;
+
+	if (!(dir = dir_namei(pathname,&namelen,&basename)))
+		return NULL;
+	if (!namelen)			/* special case: '/usr/' etc */
+		return dir;
+	bh = find_entry(dir,basename,namelen,&de);
+	if (!bh) {
+		iput(dir);
+		return NULL;
+	}
+	inr = de->inode;
+	dev = dir->i_dev;
+	brelse(bh);
+	iput(dir);
+	dir=iget(dev,inr);
+	if (dir) {
+		dir->i_atime=CURRENT_TIME;
+		dir->i_dirt=1;
+	}
+	return dir;
+}
+
+/*
+ *	open_namei()
+ *
+ * namei for open - this is in fact almost the whole open-routine.
+ */
+int open_namei(const char * pathname, int flag, int mode,
+	struct m_inode ** res_inode)
+{
+	const char * basename;
+	int inr,dev,namelen;
+	struct m_inode * dir, *inode;
+	struct buffer_head * bh;
+	struct dir_entry * de;
+
+	if ((flag & O_TRUNC) && !(flag & O_ACCMODE))
+		flag |= O_WRONLY;
+	mode &= 0777 & ~current->umask;
+	mode |= I_REGULAR;
+	if (!(dir = dir_namei(pathname,&namelen,&basename)))
+		return -ENOENT;
+	if (!namelen) {			/* special case: '/usr/' etc */
+		if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {
+			*res_inode=dir;
+			return 0;
+		}
+		iput(dir);
+		return -EISDIR;
+	}
+	bh = find_entry(dir,basename,namelen,&de);
+	if (!bh) {
+		if (!(flag & O_CREAT)) {
+			iput(dir);
+			return -ENOENT;
+		}
+		if (!permission(dir,MAY_WRITE)) {
+			iput(dir);
+			return -EACCES;
+		}
+		inode = new_inode(dir->i_dev);
+		if (!inode) {
+			iput(dir);
+			return -ENOSPC;
+		}
+		inode->i_mode = mode;
+		inode->i_dirt = 1;
+		bh = add_entry(dir,basename,namelen,&de);
+		if (!bh) {
+			inode->i_nlinks--;
+			iput(inode);
+			iput(dir);
+			return -ENOSPC;
+		}
+		de->inode = inode->i_num;
+		bh->b_dirt = 1;
+		brelse(bh);
+		iput(dir);
+		*res_inode = inode;
+		return 0;
+	}
+	inr = de->inode;
+	dev = dir->i_dev;
+	brelse(bh);
+	iput(dir);
+	if (flag & O_EXCL)
+		return -EEXIST;
+	if (!(inode=iget(dev,inr)))
+		return -EACCES;
+	if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||
+	    permission(inode,ACC_MODE(flag))!=ACC_MODE(flag)) {
+		iput(inode);
+		return -EPERM;
+	}
+	inode->i_atime = CURRENT_TIME;
+	if (flag & O_TRUNC)
+		truncate(inode);
+	*res_inode = inode;
+	return 0;
+}
+
+int sys_mkdir(const char * pathname, int mode)
+{
+	const char * basename;
+	int namelen;
+	struct m_inode * dir, * inode;
+	struct buffer_head * bh, *dir_block;
+	struct dir_entry * de;
+
+	if (current->euid && current->uid)
+		return -EPERM;
+	if (!(dir = dir_namei(pathname,&namelen,&basename)))
+		return -ENOENT;
+	if (!namelen) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!permission(dir,MAY_WRITE)) {
+		iput(dir);
+		return -EPERM;
+	}
+	bh = find_entry(dir,basename,namelen,&de);
+	if (bh) {
+		brelse(bh);
+		iput(dir);
+		return -EEXIST;
+	}
+	inode = new_inode(dir->i_dev);
+	if (!inode) {
+		iput(dir);
+		return -ENOSPC;
+	}
+	inode->i_size = 32;
+	inode->i_dirt = 1;
+	inode->i_mtime = inode->i_atime = CURRENT_TIME;
+	if (!(inode->i_zone[0]=new_block(inode->i_dev))) {
+		iput(dir);
+		inode->i_nlinks--;
+		iput(inode);
+		return -ENOSPC;
+	}
+	inode->i_dirt = 1;
+	if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) {
+		iput(dir);
+		free_block(inode->i_dev,inode->i_zone[0]);
+		inode->i_nlinks--;
+		iput(inode);
+		return -ERROR;
+	}
+	de = (struct dir_entry *) dir_block->b_data;
+	de->inode=inode->i_num;
+	strcpy(de->name,".");
+	de++;
+	de->inode = dir->i_num;
+	strcpy(de->name,"..");
+	inode->i_nlinks = 2;
+	dir_block->b_dirt = 1;
+	brelse(dir_block);
+	inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);
+	inode->i_dirt = 1;
+	bh = add_entry(dir,basename,namelen,&de);
+	if (!bh) {
+		iput(dir);
+		free_block(inode->i_dev,inode->i_zone[0]);
+		inode->i_nlinks=0;
+		iput(inode);
+		return -ENOSPC;
+	}
+	de->inode = inode->i_num;
+	bh->b_dirt = 1;
+	dir->i_nlinks++;
+	dir->i_dirt = 1;
+	iput(dir);
+	iput(inode);
+	brelse(bh);
+	return 0;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+static int empty_dir(struct m_inode * inode)
+{
+	int nr,block;
+	int len;
+	struct buffer_head * bh;
+	struct dir_entry * de;
+
+	len = inode->i_size / sizeof (struct dir_entry);
+	if (len<2 || !inode->i_zone[0] ||
+	    !(bh=bread(inode->i_dev,inode->i_zone[0]))) {
+	    	printk("warning - bad directory on dev %04x\n",inode->i_dev);
+		return 0;
+	}
+	de = (struct dir_entry *) bh->b_data;
+	if (de[0].inode != inode->i_num || !de[1].inode || 
+	    strcmp(".",de[0].name) || strcmp("..",de[1].name)) {
+	    	printk("warning - bad directory on dev %04x\n",inode->i_dev);
+		return 0;
+	}
+	nr = 2;
+	de += 2;
+	while (nr<len) {
+		if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) {
+			brelse(bh);
+			block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK);
+			if (!block) {
+				nr += DIR_ENTRIES_PER_BLOCK;
+				continue;
+			}
+			if (!(bh=bread(inode->i_dev,block)))
+				return 0;
+			de = (struct dir_entry *) bh->b_data;
+		}
+		if (de->inode) {
+			brelse(bh);
+			return 0;
+		}
+		de++;
+		nr++;
+	}
+	brelse(bh);
+	return 1;
+}
+
+int sys_rmdir(const char * name)
+{
+	const char * basename;
+	int namelen;
+	struct m_inode * dir, * inode;
+	struct buffer_head * bh;
+	struct dir_entry * de;
+
+	if (current->euid && current->uid)
+		return -EPERM;
+	if (!(dir = dir_namei(name,&namelen,&basename)))
+		return -ENOENT;
+	if (!namelen) {
+		iput(dir);
+		return -ENOENT;
+	}
+	bh = find_entry(dir,basename,namelen,&de);
+	if (!bh) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!permission(dir,MAY_WRITE)) {
+		iput(dir);
+		brelse(bh);
+		return -EPERM;
+	}
+	if (!(inode = iget(dir->i_dev, de->inode))) {
+		iput(dir);
+		brelse(bh);
+		return -EPERM;
+	}
+	if (inode == dir) {	/* we may not delete ".", but "../dir" is ok */
+		iput(inode);
+		iput(dir);
+		brelse(bh);
+		return -EPERM;
+	}
+	if (!S_ISDIR(inode->i_mode)) {
+		iput(inode);
+		iput(dir);
+		brelse(bh);
+		return -ENOTDIR;
+	}
+	if (!empty_dir(inode)) {
+		iput(inode);
+		iput(dir);
+		brelse(bh);
+		return -ENOTEMPTY;
+	}
+	if (inode->i_nlinks != 2)
+		printk("empty directory has nlink!=2 (%d)",inode->i_nlinks);
+	de->inode = 0;
+	bh->b_dirt = 1;
+	brelse(bh);
+	inode->i_nlinks=0;
+	inode->i_dirt=1;
+	dir->i_nlinks--;
+	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->i_dirt=1;
+	iput(dir);
+	iput(inode);
+	return 0;
+}
+
+int sys_unlink(const char * name)
+{
+	const char * basename;
+	int namelen;
+	struct m_inode * dir, * inode;
+	struct buffer_head * bh;
+	struct dir_entry * de;
+
+	if (!(dir = dir_namei(name,&namelen,&basename)))
+		return -ENOENT;
+	if (!namelen) {
+		iput(dir);
+		return -ENOENT;
+	}
+	if (!permission(dir,MAY_WRITE)) {
+		iput(dir);
+		return -EPERM;
+	}
+	bh = find_entry(dir,basename,namelen,&de);
+	if (!bh) {
+		iput(dir);
+		return -ENOENT;
+	}
+	inode = iget(dir->i_dev, de->inode);
+	if (!inode) {
+		printk("iget failed in delete (%04x:%d)",dir->i_dev,de->inode);
+		iput(dir);
+		brelse(bh);
+		return -ENOENT;
+	}
+	if (!S_ISREG(inode->i_mode)) {
+		iput(inode);
+		iput(dir);
+		brelse(bh);
+		return -EPERM;
+	}
+	if (!inode->i_nlinks) {
+		printk("Deleting nonexistent file (%04x:%d), %d\n",
+			inode->i_dev,inode->i_num,inode->i_nlinks);
+		inode->i_nlinks=1;
+	}
+	de->inode = 0;
+	bh->b_dirt = 1;
+	brelse(bh);
+	inode->i_nlinks--;
+	inode->i_dirt = 1;
+	inode->i_ctime = CURRENT_TIME;
+	iput(inode);
+	iput(dir);
+	return 0;
+}
+
+int sys_link(const char * oldname, const char * newname)
+{
+	struct dir_entry * de;
+	struct m_inode * oldinode, * dir;
+	struct buffer_head * bh;
+	const char * basename;
+	int namelen;
+
+	oldinode=namei(oldname);
+	if (!oldinode)
+		return -ENOENT;
+	if (!S_ISREG(oldinode->i_mode)) {
+		iput(oldinode);
+		return -EPERM;
+	}
+	dir = dir_namei(newname,&namelen,&basename);
+	if (!dir) {
+		iput(oldinode);
+		return -EACCES;
+	}
+	if (!namelen) {
+		iput(oldinode);
+		iput(dir);
+		return -EPERM;
+	}
+	if (dir->i_dev != oldinode->i_dev) {
+		iput(dir);
+		iput(oldinode);
+		return -EXDEV;
+	}
+	if (!permission(dir,MAY_WRITE)) {
+		iput(dir);
+		iput(oldinode);
+		return -EACCES;
+	}
+	bh = find_entry(dir,basename,namelen,&de);
+	if (bh) {
+		brelse(bh);
+		iput(dir);
+		iput(oldinode);
+		return -EEXIST;
+	}
+	bh = add_entry(dir,basename,namelen,&de);
+	if (!bh) {
+		iput(dir);
+		iput(oldinode);
+		return -ENOSPC;
+	}
+	de->inode = oldinode->i_num;
+	bh->b_dirt = 1;
+	brelse(bh);
+	iput(dir);
+	oldinode->i_nlinks++;
+	oldinode->i_ctime = CURRENT_TIME;
+	oldinode->i_dirt = 1;
+	iput(oldinode);
+	return 0;
+}
diff --git a/fs/open.c b/fs/open.c
new file mode 100644
index 0000000..6918690
--- /dev/null
+++ b/fs/open.c
@@ -0,0 +1,188 @@
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <utime.h>
+#include <sys/stat.h>
+
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+int sys_utime(char * filename, struct utimbuf * times)
+{
+	struct m_inode * inode;
+	long actime,modtime;
+
+	if (!(inode=namei(filename)))
+		return -ENOENT;
+	if (times) {
+		actime = get_fs_long((unsigned long *) &times->actime);
+		modtime = get_fs_long((unsigned long *) &times->modtime);
+	} else
+		actime = modtime = CURRENT_TIME;
+	inode->i_atime = actime;
+	inode->i_mtime = modtime;
+	inode->i_dirt = 1;
+	iput(inode);
+	return 0;
+}
+
+int sys_access(const char * filename,int mode)
+{
+	struct m_inode * inode;
+	int res;
+
+	mode &= 0007;
+	if (!(inode=namei(filename)))
+		return -EACCES;
+	res = inode->i_mode & 0777;
+	iput(inode);
+	if (!(current->euid && current->uid))
+		if (res & 0111)
+			res = 0777;
+		else
+			res = 0666;
+	if (current->euid == inode->i_uid)
+		res >>= 6;
+	else if (current->egid == inode->i_gid)
+		res >>= 6;
+	if ((res & 0007 & mode) == mode)
+		return 0;
+	return -EACCES;
+}
+
+int sys_chdir(const char * filename)
+{
+	struct m_inode * inode;
+
+	if (!(inode = namei(filename)))
+		return -ENOENT;
+	if (!S_ISDIR(inode->i_mode)) {
+		iput(inode);
+		return -ENOTDIR;
+	}
+	iput(current->pwd);
+	current->pwd = inode;
+	return (0);
+}
+
+int sys_chroot(const char * filename)
+{
+	struct m_inode * inode;
+
+	if (!(inode=namei(filename)))
+		return -ENOENT;
+	if (!S_ISDIR(inode->i_mode)) {
+		iput(inode);
+		return -ENOTDIR;
+	}
+	iput(current->root);
+	current->root = inode;
+	return (0);
+}
+
+int sys_chmod(const char * filename,int mode)
+{
+	struct m_inode * inode;
+
+	if (!(inode=namei(filename)))
+		return -ENOENT;
+	if (current->uid && current->euid)
+		if (current->uid!=inode->i_uid && current->euid!=inode->i_uid) {
+			iput(inode);
+			return -EACCES;
+		} else 
+			mode = (mode & 0777) | (inode->i_mode & 07000);
+	inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
+	inode->i_dirt = 1;
+	iput(inode);
+	return 0;
+}
+
+int sys_chown(const char * filename,int uid,int gid)
+{
+	struct m_inode * inode;
+
+	if (!(inode=namei(filename)))
+		return -ENOENT;
+	if (current->uid && current->euid) {
+		iput(inode);
+		return -EACCES;
+	}
+	inode->i_uid=uid;
+	inode->i_gid=gid;
+	inode->i_dirt=1;
+	iput(inode);
+	return 0;
+}
+
+int sys_open(const char * filename,int flag,int mode)
+{
+	struct m_inode * inode;
+	struct file * f;
+	int i,fd;
+
+	mode &= 0777 & ~current->umask;
+	for(fd=0 ; fd<NR_OPEN ; fd++)
+		if (!current->filp[fd])
+			break;
+	if (fd>=NR_OPEN)
+		return -EINVAL;
+	current->close_on_exec &= ~(1<<fd);
+	f=0+file_table;
+	for (i=0 ; i<NR_FILE ; i++,f++)
+		if (!f->f_count) break;
+	if (i>=NR_FILE)
+		return -EINVAL;
+	(current->filp[fd]=f)->f_count++;
+	if ((i=open_namei(filename,flag,mode,&inode))<0) {
+		current->filp[fd]=NULL;
+		f->f_count=0;
+		return i;
+	}
+/* ttys are somewhat special (ttyxx major==4, tty major==5) */
+	if (S_ISCHR(inode->i_mode))
+		if (MAJOR(inode->i_zone[0])==4) {
+			if (current->leader && current->tty<0) {
+				current->tty = MINOR(inode->i_zone[0]);
+				tty_table[current->tty].pgrp = current->pgrp;
+			}
+		} else if (MAJOR(inode->i_zone[0])==5)
+			if (current->tty<0) {
+				iput(inode);
+				current->filp[fd]=NULL;
+				f->f_count=0;
+				return -EPERM;
+			}
+	f->f_mode = inode->i_mode;
+	f->f_flags = flag;
+	f->f_count = 1;
+	f->f_inode = inode;
+	f->f_pos = 0;
+	return (fd);
+}
+
+int sys_creat(const char * pathname, int mode)
+{
+	return sys_open(pathname, O_CREAT | O_TRUNC, mode);
+}
+
+int sys_close(unsigned int fd)
+{	
+	struct file * filp;
+
+	if (fd >= NR_OPEN)
+		return -EINVAL;
+	current->close_on_exec &= ~(1<<fd);
+	if (!(filp = current->filp[fd]))
+		return -EINVAL;
+	current->filp[fd] = NULL;
+	if (filp->f_count == 0)
+		panic("Close: file count is 0");
+	if (--filp->f_count)
+		return (0);
+	iput(filp->f_inode);
+	return (0);
+}
diff --git a/fs/pipe.c b/fs/pipe.c
new file mode 100644
index 0000000..7e03e13
--- /dev/null
+++ b/fs/pipe.c
@@ -0,0 +1,92 @@
+#include <signal.h>
+
+#include <linux/sched.h>
+#include <linux/mm.h>	/* for get_free_page */
+#include <asm/segment.h>
+
+int read_pipe(struct m_inode * inode, char * buf, int count)
+{
+	char * b=buf;
+
+	while (PIPE_EMPTY(*inode)) {
+		wake_up(&inode->i_wait);
+		if (inode->i_count != 2) /* are there any writers left? */
+			return 0;
+		sleep_on(&inode->i_wait);
+	}
+	while (count>0 && !(PIPE_EMPTY(*inode))) {
+		count --;
+		put_fs_byte(((char *)inode->i_size)[PIPE_TAIL(*inode)],b++);
+		INC_PIPE( PIPE_TAIL(*inode) );
+	}
+	wake_up(&inode->i_wait);
+	return b-buf;
+}
+	
+int write_pipe(struct m_inode * inode, char * buf, int count)
+{
+	char * b=buf;
+
+	wake_up(&inode->i_wait);
+	if (inode->i_count != 2) { /* no readers */
+		current->signal |= (1<<(SIGPIPE-1));
+		return -1;
+	}
+	while (count-->0) {
+		while (PIPE_FULL(*inode)) {
+			wake_up(&inode->i_wait);
+			if (inode->i_count != 2) {
+				current->signal |= (1<<(SIGPIPE-1));
+				return b-buf;
+			}
+			sleep_on(&inode->i_wait);
+		}
+		((char *)inode->i_size)[PIPE_HEAD(*inode)] = get_fs_byte(b++);
+		INC_PIPE( PIPE_HEAD(*inode) );
+		wake_up(&inode->i_wait);
+	}
+	wake_up(&inode->i_wait);
+	return b-buf;
+}
+
+int sys_pipe(unsigned long * fildes)
+{
+	struct m_inode * inode;
+	struct file * f[2];
+	int fd[2];
+	int i,j;
+
+	j=0;
+	for(i=0;j<2 && i<NR_FILE;i++)
+		if (!file_table[i].f_count)
+			(f[j++]=i+file_table)->f_count++;
+	if (j==1)
+		f[0]->f_count=0;
+	if (j<2)
+		return -1;
+	j=0;
+	for(i=0;j<2 && i<NR_OPEN;i++)
+		if (!current->filp[i]) {
+			current->filp[ fd[j]=i ] = f[j];
+			j++;
+		}
+	if (j==1)
+		current->filp[fd[0]]=NULL;
+	if (j<2) {
+		f[0]->f_count=f[1]->f_count=0;
+		return -1;
+	}
+	if (!(inode=get_pipe_inode())) {
+		current->filp[fd[0]] =
+			current->filp[fd[1]] = NULL;
+		f[0]->f_count = f[1]->f_count = 0;
+		return -1;
+	}
+	f[0]->f_inode = f[1]->f_inode = inode;
+	f[0]->f_pos = f[1]->f_pos = 0;
+	f[0]->f_mode = 1;		/* read */
+	f[1]->f_mode = 2;		/* write */
+	put_fs_long(fd[0],0+fildes);
+	put_fs_long(fd[1],1+fildes);
+	return 0;
+}
diff --git a/fs/read_write.c b/fs/read_write.c
new file mode 100644
index 0000000..93faac2
--- /dev/null
+++ b/fs/read_write.c
@@ -0,0 +1,97 @@
+#include <sys/stat.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+
+extern int rw_char(int rw,int dev, char * buf, int count);
+extern int read_pipe(struct m_inode * inode, char * buf, int count);
+extern int write_pipe(struct m_inode * inode, char * buf, int count);
+extern int block_read(int dev, off_t * pos, char * buf, int count);
+extern int block_write(int dev, off_t * pos, char * buf, int count);
+extern int file_read(struct m_inode * inode, struct file * filp,
+		char * buf, int count);
+extern int file_write(struct m_inode * inode, struct file * filp,
+		char * buf, int count);
+
+int sys_lseek(unsigned int fd,off_t offset, int origin)
+{
+	struct file * file;
+	int tmp;
+
+	if (fd >= NR_OPEN || !(file=current->filp[fd]) || !(file->f_inode)
+	   || !IS_BLOCKDEV(MAJOR(file->f_inode->i_dev)))
+		return -EBADF;
+	if (file->f_inode->i_pipe)
+		return -ESPIPE;
+	switch (origin) {
+		case 0:
+			if (offset<0) return -EINVAL;
+			file->f_pos=offset;
+			break;
+		case 1:
+			if (file->f_pos+offset<0) return -EINVAL;
+			file->f_pos += offset;
+			break;
+		case 2:
+			if ((tmp=file->f_inode->i_size+offset) < 0)
+				return -EINVAL;
+			file->f_pos = tmp;
+			break;
+		default:
+			return -EINVAL;
+	}
+	return file->f_pos;
+}
+
+int sys_read(unsigned int fd,char * buf,int count)
+{
+	struct file * file;
+	struct m_inode * inode;
+
+	if (fd>=NR_OPEN || count<0 || !(file=current->filp[fd]))
+		return -EINVAL;
+	if (!count)
+		return 0;
+	verify_area(buf,count);
+	inode = file->f_inode;
+	if (inode->i_pipe)
+		return (file->f_mode&1)?read_pipe(inode,buf,count):-1;
+	if (S_ISCHR(inode->i_mode))
+		return rw_char(READ,inode->i_zone[0],buf,count);
+	if (S_ISBLK(inode->i_mode))
+		return block_read(inode->i_zone[0],&file->f_pos,buf,count);
+	if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
+		if (count+file->f_pos > inode->i_size)
+			count = inode->i_size - file->f_pos;
+		if (count<=0)
+			return 0;
+		return file_read(inode,file,buf,count);
+	}
+	printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode);
+	return -EINVAL;
+}
+
+int sys_write(unsigned int fd,char * buf,int count)
+{
+	struct file * file;
+	struct m_inode * inode;
+	
+	if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd]))
+		return -EINVAL;
+	if (!count)
+		return 0;
+	inode=file->f_inode;
+	if (inode->i_pipe)
+		return (file->f_mode&2)?write_pipe(inode,buf,count):-1;
+	if (S_ISCHR(inode->i_mode))
+		return rw_char(WRITE,inode->i_zone[0],buf,count);
+	if (S_ISBLK(inode->i_mode))
+		return block_write(inode->i_zone[0],&file->f_pos,buf,count);
+	if (S_ISREG(inode->i_mode))
+		return file_write(inode,file,buf,count);
+	printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode);
+	return -EINVAL;
+}
diff --git a/fs/stat.c b/fs/stat.c
new file mode 100644
index 0000000..4bec71d
--- /dev/null
+++ b/fs/stat.c
@@ -0,0 +1,51 @@
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+static int cp_stat(struct m_inode * inode, struct stat * statbuf)
+{
+	struct stat tmp;
+	int i;
+
+	verify_area(statbuf,sizeof (* statbuf));
+	tmp.st_dev = inode->i_dev;
+	tmp.st_ino = inode->i_num;
+	tmp.st_mode = inode->i_mode;
+	tmp.st_nlink = inode->i_nlinks;
+	tmp.st_uid = inode->i_uid;
+	tmp.st_gid = inode->i_gid;
+	tmp.st_rdev = inode->i_zone[0];
+	tmp.st_size = inode->i_size;
+	tmp.st_atime = inode->i_atime;
+	tmp.st_mtime = inode->i_mtime;
+	tmp.st_ctime = inode->i_ctime;
+	for (i=0 ; i<sizeof (tmp) ; i++)
+		put_fs_byte(((char *) &tmp)[i],&((char *) statbuf)[i]);
+	return (0);
+}
+
+int sys_stat(char * filename, struct stat * statbuf)
+{
+	int i;
+	struct m_inode * inode;
+
+	if (!(inode=namei(filename)))
+		return -ENOENT;
+	i=cp_stat(inode,statbuf);
+	iput(inode);
+	return i;
+}
+
+int sys_fstat(unsigned int fd, struct stat * statbuf)
+{
+	struct file * f;
+	struct m_inode * inode;
+
+	if (fd >= NR_OPEN || !(f=current->filp[fd]) || !(inode=f->f_inode))
+		return -ENOENT;
+	return cp_stat(inode,statbuf);
+}
diff --git a/fs/super.c b/fs/super.c
new file mode 100644
index 0000000..d832289
--- /dev/null
+++ b/fs/super.c
@@ -0,0 +1,102 @@
+/*
+ * super.c contains code to handle the super-block tables.
+ */
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+
+/* set_bit uses setb, as gas doesn't recognize setc */
+#define set_bit(bitnr,addr) ({ \
+register int __res __asm__("ax"); \
+__asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \
+__res; })
+
+struct super_block super_block[NR_SUPER];
+
+struct super_block * do_mount(int dev)
+{
+	struct super_block * p;
+	struct buffer_head * bh;
+	int i,block;
+
+	for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++ )
+		if (!(p->s_dev))
+			break;
+	p->s_dev = -1;		/* mark it in use */
+	if (p >= &super_block[NR_SUPER])
+		return NULL;
+	if (!(bh = bread(dev,1)))
+		return NULL;
+	*p = *((struct super_block *) bh->b_data);
+	brelse(bh);
+	if (p->s_magic != SUPER_MAGIC) {
+		p->s_dev = 0;
+		return NULL;
+	}
+	for (i=0;i<I_MAP_SLOTS;i++)
+		p->s_imap[i] = NULL;
+	for (i=0;i<Z_MAP_SLOTS;i++)
+		p->s_zmap[i] = NULL;
+	block=2;
+	for (i=0 ; i < p->s_imap_blocks ; i++)
+		if (p->s_imap[i]=bread(dev,block))
+			block++;
+		else
+			break;
+	for (i=0 ; i < p->s_zmap_blocks ; i++)
+		if (p->s_zmap[i]=bread(dev,block))
+			block++;
+		else
+			break;
+	if (block != 2+p->s_imap_blocks+p->s_zmap_blocks) {
+		for(i=0;i<I_MAP_SLOTS;i++)
+			brelse(p->s_imap[i]);
+		for(i=0;i<Z_MAP_SLOTS;i++)
+			brelse(p->s_zmap[i]);
+		p->s_dev=0;
+		return NULL;
+	}
+	p->s_imap[0]->b_data[0] |= 1;
+	p->s_zmap[0]->b_data[0] |= 1;
+	p->s_dev = dev;
+	p->s_isup = NULL;
+	p->s_imount = NULL;
+	p->s_time = 0;
+	p->s_rd_only = 0;
+	p->s_dirt = 0;
+	return p;
+}
+
+void mount_root(void)
+{
+	int i,free;
+	struct super_block * p;
+	struct m_inode * mi;
+
+	if (32 != sizeof (struct d_inode))
+		panic("bad i-node size");
+	for(i=0;i<NR_FILE;i++)
+		file_table[i].f_count=0;
+	for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++)
+		p->s_dev = 0;
+	if (!(p=do_mount(ROOT_DEV)))
+		panic("Unable to mount root");
+	if (!(mi=iget(ROOT_DEV,1)))
+		panic("Unable to read root i-node");
+	mi->i_count += 3 ;	/* NOTE! it is logically used 4 times, not 1 */
+	p->s_isup = p->s_imount = mi;
+	current->pwd = mi;
+	current->root = mi;
+	free=0;
+	i=p->s_nzones;
+	while (-- i >= 0)
+		if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data))
+			free++;
+	printk("%d/%d free blocks\n\r",free,p->s_nzones);
+	free=0;
+	i=p->s_ninodes+1;
+	while (-- i >= 0)
+		if (!set_bit(i&8191,p->s_imap[i>>13]->b_data))
+			free++;
+	printk("%d/%d free inodes\n\r",free,p->s_ninodes);
+}
diff --git a/fs/truncate.c b/fs/truncate.c
new file mode 100644
index 0000000..fd82db8
--- /dev/null
+++ b/fs/truncate.c
@@ -0,0 +1,59 @@
+#include <linux/sched.h>
+
+#include <sys/stat.h>
+
+static void free_ind(int dev,int block)
+{
+	struct buffer_head * bh;
+	unsigned short * p;
+	int i;
+
+	if (!block)
+		return;
+	if (bh=bread(dev,block)) {
+		p = (unsigned short *) bh->b_data;
+		for (i=0;i<512;i++,p++)
+			if (*p)
+				free_block(dev,*p);
+		brelse(bh);
+	}
+	free_block(dev,block);
+}
+
+static void free_dind(int dev,int block)
+{
+	struct buffer_head * bh;
+	unsigned short * p;
+	int i;
+
+	if (!block)
+		return;
+	if (bh=bread(dev,block)) {
+		p = (unsigned short *) bh->b_data;
+		for (i=0;i<512;i++,p++)
+			if (*p)
+				free_ind(dev,*p);
+		brelse(bh);
+	}
+	free_block(dev,block);
+}
+
+void truncate(struct m_inode * inode)
+{
+	int i;
+
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
+		return;
+	for (i=0;i<7;i++)
+		if (inode->i_zone[i]) {
+			free_block(inode->i_dev,inode->i_zone[i]);
+			inode->i_zone[i]=0;
+		}
+	free_ind(inode->i_dev,inode->i_zone[7]);
+	free_dind(inode->i_dev,inode->i_zone[8]);
+	inode->i_zone[7] = inode->i_zone[8] = 0;
+	inode->i_size = 0;
+	inode->i_dirt = 1;
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+}
+
diff --git a/fs/tty_ioctl.c b/fs/tty_ioctl.c
new file mode 100644
index 0000000..b4d9bf7
--- /dev/null
+++ b/fs/tty_ioctl.c
@@ -0,0 +1,166 @@
+#include <errno.h>
+#include <termios.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+static void flush(struct tty_queue * queue)
+{
+	cli();
+	queue->head = queue->tail;
+	sti();
+}
+
+static void wait_until_sent(struct tty_struct * tty)
+{
+	/* do nothing - not implemented */
+}
+
+static void send_break(struct tty_struct * tty)
+{
+	/* do nothing - not implemented */
+}
+
+static int get_termios(struct tty_struct * tty, struct termios * termios)
+{
+	int i;
+
+	verify_area(termios, sizeof (*termios));
+	for (i=0 ; i< (sizeof (*termios)) ; i++)
+		put_fs_byte( ((char *)&tty->termios)[i] , i+(char *)termios );
+	return 0;
+}
+
+static int set_termios(struct tty_struct * tty, struct termios * termios)
+{
+	int i;
+
+	for (i=0 ; i< (sizeof (*termios)) ; i++)
+		((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios);
+	return 0;
+}
+
+static int get_termio(struct tty_struct * tty, struct termio * termio)
+{
+	int i;
+	struct termio tmp_termio;
+
+	verify_area(termio, sizeof (*termio));
+	tmp_termio.c_iflag = tty->termios.c_iflag;
+	tmp_termio.c_oflag = tty->termios.c_oflag;
+	tmp_termio.c_cflag = tty->termios.c_cflag;
+	tmp_termio.c_lflag = tty->termios.c_lflag;
+	tmp_termio.c_line = tty->termios.c_line;
+	for(i=0 ; i < NCC ; i++)
+		tmp_termio.c_cc[i] = tty->termios.c_cc[i];
+	for (i=0 ; i< (sizeof (*termio)) ; i++)
+		put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio );
+	return 0;
+}
+
+static int set_termio(struct tty_struct * tty, struct termio * termio)
+{
+	int i;
+	struct termio tmp_termio;
+
+	for (i=0 ; i< (sizeof (*termio)) ; i++)
+		((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio);
+	*(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag;
+	*(unsigned short *)&tty->termios.c_oflag = tmp_termio.c_oflag;
+	*(unsigned short *)&tty->termios.c_cflag = tmp_termio.c_cflag;
+	*(unsigned short *)&tty->termios.c_lflag = tmp_termio.c_lflag;
+	tty->termios.c_line = tmp_termio.c_line;
+	for(i=0 ; i < NCC ; i++)
+		tty->termios.c_cc[i] = tmp_termio.c_cc[i];
+	return 0;
+}
+
+int tty_ioctl(int dev, int cmd, int arg)
+{
+	struct tty_struct * tty;
+	if (MAJOR(dev) == 5) {
+		dev=current->tty;
+		if (dev<0)
+			panic("tty_ioctl: dev<0");
+	} else
+		dev=MINOR(dev);
+	tty = dev + tty_table;
+	switch (cmd) {
+		case TCGETS:
+			return get_termios(tty,(struct termios *) arg);
+		case TCSETSF:
+			flush(&tty->read_q); /* fallthrough */
+		case TCSETSW:
+			wait_until_sent(tty); /* fallthrough */
+		case TCSETS:
+			return set_termios(tty,(struct termios *) arg);
+		case TCGETA:
+			return get_termio(tty,(struct termio *) arg);
+		case TCSETAF:
+			flush(&tty->read_q); /* fallthrough */
+		case TCSETAW:
+			wait_until_sent(tty); /* fallthrough */
+		case TCSETA:
+			return set_termio(tty,(struct termio *) arg);
+		case TCSBRK:
+			if (!arg) {
+				wait_until_sent(tty);
+				send_break(tty);
+			}
+			return 0;
+		case TCXONC:
+			return -EINVAL; /* not implemented */
+		case TCFLSH:
+			if (arg==0)
+				flush(&tty->read_q);
+			else if (arg==1)
+				flush(&tty->write_q);
+			else if (arg==2) {
+				flush(&tty->read_q);
+				flush(&tty->write_q);
+			} else
+				return -EINVAL;
+			return 0;
+		case TIOCEXCL:
+			return -EINVAL; /* not implemented */
+		case TIOCNXCL:
+			return -EINVAL; /* not implemented */
+		case TIOCSCTTY:
+			return -EINVAL; /* set controlling term NI */
+		case TIOCGPGRP:
+			verify_area((void *) arg,4);
+			put_fs_long(tty->pgrp,(unsigned long *) arg);
+			return 0;
+		case TIOCSPGRP:
+			tty->pgrp=get_fs_long((unsigned long *) arg);
+			return 0;
+		case TIOCOUTQ:
+			verify_area((void *) arg,4);
+			put_fs_long(CHARS(tty->write_q),(unsigned long *) arg);
+			return 0;
+		case TIOCSTI:
+			return -EINVAL; /* not implemented */
+		case TIOCGWINSZ:
+			return -EINVAL; /* not implemented */
+		case TIOCSWINSZ:
+			return -EINVAL; /* not implemented */
+		case TIOCMGET:
+			return -EINVAL; /* not implemented */
+		case TIOCMBIS:
+			return -EINVAL; /* not implemented */
+		case TIOCMBIC:
+			return -EINVAL; /* not implemented */
+		case TIOCMSET:
+			return -EINVAL; /* not implemented */
+		case TIOCGSOFTCAR:
+			return -EINVAL; /* not implemented */
+		case TIOCSSOFTCAR:
+			return -EINVAL; /* not implemented */
+		default:
+			return -EINVAL;
+	}
+}
diff --git a/include/a.out.h b/include/a.out.h
new file mode 100644
index 0000000..3e67974
--- /dev/null
+++ b/include/a.out.h
@@ -0,0 +1,220 @@
+#ifndef _A_OUT_H
+#define _A_OUT_H
+
+#define __GNU_EXEC_MACROS__
+
+struct exec {
+  unsigned long a_magic;	/* Use macros N_MAGIC, etc for access */
+  unsigned a_text;		/* length of text, in bytes */
+  unsigned a_data;		/* length of data, in bytes */
+  unsigned a_bss;		/* length of uninitialized data area for file, in bytes */
+  unsigned a_syms;		/* length of symbol table data in file, in bytes */
+  unsigned a_entry;		/* start address */
+  unsigned a_trsize;		/* length of relocation info for text, in bytes */
+  unsigned a_drsize;		/* length of relocation info for data, in bytes */
+};
+
+#ifndef N_MAGIC
+#define N_MAGIC(exec) ((exec).a_magic)
+#endif
+
+#ifndef OMAGIC
+/* Code indicating object file or impure executable.  */
+#define OMAGIC 0407
+/* Code indicating pure executable.  */
+#define NMAGIC 0410
+/* Code indicating demand-paged executable.  */
+#define ZMAGIC 0413
+#endif /* not OMAGIC */
+
+#ifndef N_BADMAG
+#define N_BADMAG(x)					\
+ (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC		\
+  && N_MAGIC(x) != ZMAGIC)
+#endif
+
+#define _N_BADMAG(x)					\
+ (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC		\
+  && N_MAGIC(x) != ZMAGIC)
+
+#define _N_HDROFF(x) (SEGMENT_SIZE - sizeof (struct exec))
+
+#ifndef N_TXTOFF
+#define N_TXTOFF(x) \
+ (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : sizeof (struct exec))
+#endif
+
+#ifndef N_DATOFF
+#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text)
+#endif
+
+#ifndef N_TRELOFF
+#define N_TRELOFF(x) (N_DATOFF(x) + (x).a_data)
+#endif
+
+#ifndef N_DRELOFF
+#define N_DRELOFF(x) (N_TRELOFF(x) + (x).a_trsize)
+#endif
+
+#ifndef N_SYMOFF
+#define N_SYMOFF(x) (N_DRELOFF(x) + (x).a_drsize)
+#endif
+
+#ifndef N_STROFF
+#define N_STROFF(x) (N_SYMOFF(x) + (x).a_syms)
+#endif
+
+/* Address of text segment in memory after it is loaded.  */
+#ifndef N_TXTADDR
+#define N_TXTADDR(x) 0
+#endif
+
+/* Address of data segment in memory after it is loaded.
+   Note that it is up to you to define SEGMENT_SIZE
+   on machines not listed here.  */
+#if defined(vax) || defined(hp300) || defined(pyr)
+#define SEGMENT_SIZE PAGE_SIZE
+#endif
+#ifdef	hp300
+#define	PAGE_SIZE	4096
+#endif
+#ifdef	sony
+#define	SEGMENT_SIZE	0x2000
+#endif	/* Sony.  */
+#ifdef is68k
+#define SEGMENT_SIZE 0x20000
+#endif
+#if defined(m68k) && defined(PORTAR)
+#define PAGE_SIZE 0x400
+#define SEGMENT_SIZE PAGE_SIZE
+#endif
+
+#define PAGE_SIZE 4096
+#define SEGMENT_SIZE 1024
+
+#define _N_SEGMENT_ROUND(x) (((x) + SEGMENT_SIZE - 1) & ~(SEGMENT_SIZE - 1))
+
+#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text)
+
+#ifndef N_DATADDR
+#define N_DATADDR(x) \
+    (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \
+     : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x))))
+#endif
+
+/* Address of bss segment in memory after it is loaded.  */
+#ifndef N_BSSADDR
+#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data)
+#endif
+
+#ifndef N_NLIST_DECLARED
+struct nlist {
+  union {
+    char *n_name;
+    struct nlist *n_next;
+    long n_strx;
+  } n_un;
+  unsigned char n_type;
+  char n_other;
+  short n_desc;
+  unsigned long n_value;
+};
+#endif
+
+#ifndef N_UNDF
+#define N_UNDF 0
+#endif
+#ifndef N_ABS
+#define N_ABS 2
+#endif
+#ifndef N_TEXT
+#define N_TEXT 4
+#endif
+#ifndef N_DATA
+#define N_DATA 6
+#endif
+#ifndef N_BSS
+#define N_BSS 8
+#endif
+#ifndef N_COMM
+#define N_COMM 18
+#endif
+#ifndef N_FN
+#define N_FN 15
+#endif
+
+#ifndef N_EXT
+#define N_EXT 1
+#endif
+#ifndef N_TYPE
+#define N_TYPE 036
+#endif
+#ifndef N_STAB
+#define N_STAB 0340
+#endif
+
+/* The following type indicates the definition of a symbol as being
+   an indirect reference to another symbol.  The other symbol
+   appears as an undefined reference, immediately following this symbol.
+
+   Indirection is asymmetrical.  The other symbol's value will be used
+   to satisfy requests for the indirect symbol, but not vice versa.
+   If the other symbol does not have a definition, libraries will
+   be searched to find a definition.  */
+#define N_INDR 0xa
+
+/* The following symbols refer to set elements.
+   All the N_SET[ATDB] symbols with the same name form one set.
+   Space is allocated for the set in the text section, and each set
+   element's value is stored into one word of the space.
+   The first word of the space is the length of the set (number of elements).
+
+   The address of the set is made into an N_SETV symbol
+   whose name is the same as the name of the set.
+   This symbol acts like a N_DATA global symbol
+   in that it can satisfy undefined external references.  */
+
+/* These appear as input to LD, in a .o file.  */
+#define	N_SETA	0x14		/* Absolute set element symbol */
+#define	N_SETT	0x16		/* Text set element symbol */
+#define	N_SETD	0x18		/* Data set element symbol */
+#define	N_SETB	0x1A		/* Bss set element symbol */
+
+/* This is output from LD.  */
+#define N_SETV	0x1C		/* Pointer to set vector in data area.  */
+
+#ifndef N_RELOCATION_INFO_DECLARED
+
+/* This structure describes a single relocation to be performed.
+   The text-relocation section of the file is a vector of these structures,
+   all of which apply to the text section.
+   Likewise, the data-relocation section applies to the data section.  */
+
+struct relocation_info
+{
+  /* Address (within segment) to be relocated.  */
+  int r_address;
+  /* The meaning of r_symbolnum depends on r_extern.  */
+  unsigned int r_symbolnum:24;
+  /* Nonzero means value is a pc-relative offset
+     and it should be relocated for changes in its own address
+     as well as for changes in the symbol or section specified.  */
+  unsigned int r_pcrel:1;
+  /* Length (as exponent of 2) of the field to be relocated.
+     Thus, a value of 2 indicates 1<<2 bytes.  */
+  unsigned int r_length:2;
+  /* 1 => relocate with value of symbol.
+          r_symbolnum is the index of the symbol
+	  in file's the symbol table.
+     0 => relocate with the address of a segment.
+          r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS
+	  (the N_EXT bit may be set also, but signifies nothing).  */
+  unsigned int r_extern:1;
+  /* Four bits that aren't used, but when writing an object file
+     it is desirable to clear them.  */
+  unsigned int r_pad:4;
+};
+#endif /* no N_RELOCATION_INFO_DECLARED.  */
+
+
+#endif /* __A_OUT_GNU_H__ */
diff --git a/include/asm/io.h b/include/asm/io.h
new file mode 100644
index 0000000..d5cc42a
--- /dev/null
+++ b/include/asm/io.h
@@ -0,0 +1,24 @@
+#define outb(value,port) \
+__asm__ ("outb %%al,%%dx"::"a" (value),"d" (port))
+
+
+#define inb(port) ({ \
+unsigned char _v; \
+__asm__ volatile ("inb %%dx,%%al":"=a" (_v):"d" (port)); \
+_v; \
+})
+
+#define outb_p(value,port) \
+__asm__ ("outb %%al,%%dx\n" \
+		"\tjmp 1f\n" \
+		"1:\tjmp 1f\n" \
+		"1:"::"a" (value),"d" (port))
+
+#define inb_p(port) ({ \
+unsigned char _v; \
+__asm__ volatile ("inb %%dx,%%al\n" \
+	"\tjmp 1f\n" \
+	"1:\tjmp 1f\n" \
+	"1:":"=a" (_v):"d" (port)); \
+_v; \
+})
diff --git a/include/asm/memory.h b/include/asm/memory.h
new file mode 100644
index 0000000..4b0a98e
--- /dev/null
+++ b/include/asm/memory.h
@@ -0,0 +1,14 @@
+/*
+ *  NOTE!!! memcpy(dest,src,n) assumes ds=es=normal data segment. This
+ *  goes for all kernel functions (ds=es=kernel space, fs=local data,
+ *  gs=null), as well as for all well-behaving user programs (ds=es=
+ *  user data space). This is NOT a bug, as any user program that changes
+ *  es deserves to die if it isn't careful.
+ */
+#define memcpy(dest,src,n) ({ \
+void * _res = dest; \
+__asm__ ("cld;rep;movsb" \
+	::"D" ((long)(_res)),"S" ((long)(src)),"c" ((long) (n)) \
+	:"di","si","cx"); \
+_res; \
+})
diff --git a/include/asm/segment.h b/include/asm/segment.h
new file mode 100644
index 0000000..1a53b08
--- /dev/null
+++ b/include/asm/segment.h
@@ -0,0 +1,38 @@
+extern inline unsigned char get_fs_byte(const char * addr)
+{
+	unsigned register char _v;
+
+	__asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr));
+	return _v;
+}
+
+extern inline unsigned short get_fs_word(const unsigned short *addr)
+{
+	unsigned short _v;
+
+	__asm__ ("movw %%fs:%1,%0":"=r" (_v):"m" (*addr));
+	return _v;
+}
+
+extern inline unsigned long get_fs_long(const unsigned long *addr)
+{
+	unsigned long _v;
+
+	__asm__ ("movl %%fs:%1,%0":"=r" (_v):"m" (*addr)); \
+	return _v;
+}
+
+extern inline void put_fs_byte(char val,char *addr)
+{
+__asm__ ("movb %0,%%fs:%1"::"r" (val),"m" (*addr));
+}
+
+extern inline void put_fs_word(short val,short * addr)
+{
+__asm__ ("movw %0,%%fs:%1"::"r" (val),"m" (*addr));
+}
+
+extern inline void put_fs_long(unsigned long val,unsigned long * addr)
+{
+__asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr));
+}
diff --git a/include/asm/system.h b/include/asm/system.h
new file mode 100644
index 0000000..0b5a21d
--- /dev/null
+++ b/include/asm/system.h
@@ -0,0 +1,66 @@
+#define move_to_user_mode() \
+__asm__ ("movl %%esp,%%eax\n\t" \
+	"pushl $0x17\n\t" \
+	"pushl %%eax\n\t" \
+	"pushfl\n\t" \
+	"pushl $0x0f\n\t" \
+	"pushl $1f\n\t" \
+	"iret\n" \
+	"1:\tmovl $0x17,%%eax\n\t" \
+	"movw %%ax,%%ds\n\t" \
+	"movw %%ax,%%es\n\t" \
+	"movw %%ax,%%fs\n\t" \
+	"movw %%ax,%%gs" \
+	:::"ax")
+
+#define sti() __asm__ ("sti"::)
+#define cli() __asm__ ("cli"::)
+#define nop() __asm__ ("nop"::)
+
+#define iret() __asm__ ("iret"::)
+
+#define _set_gate(gate_addr,type,dpl,addr) \
+__asm__ ("movw %%dx,%%ax\n\t" \
+	"movw %0,%%dx\n\t" \
+	"movl %%eax,%1\n\t" \
+	"movl %%edx,%2" \
+	: \
+	: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
+	"o" (*((char *) (gate_addr))), \
+	"o" (*(4+(char *) (gate_addr))), \
+	"d" ((char *) (addr)),"a" (0x00080000))
+
+#define set_intr_gate(n,addr) \
+	_set_gate(&idt[n],14,0,addr)
+
+#define set_trap_gate(n,addr) \
+	_set_gate(&idt[n],15,0,addr)
+
+#define set_system_gate(n,addr) \
+	_set_gate(&idt[n],15,3,addr)
+
+#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\
+	*(gate_addr) = ((base) & 0xff000000) | \
+		(((base) & 0x00ff0000)>>16) | \
+		((limit) & 0xf0000) | \
+		((dpl)<<13) | \
+		(0x00408000) | \
+		((type)<<8); \
+	*((gate_addr)+1) = (((base) & 0x0000ffff)<<16) | \
+		((limit) & 0x0ffff); }
+
+#define _set_tssldt_desc(n,addr,type) \
+__asm__ ("movw $104,%1\n\t" \
+	"movw %%ax,%2\n\t" \
+	"rorl $16,%%eax\n\t" \
+	"movb %%al,%3\n\t" \
+	"movb $" type ",%4\n\t" \
+	"movb $0x00,%5\n\t" \
+	"movb %%ah,%6\n\t" \
+	"rorl $16,%%eax" \
+	::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \
+	 "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \
+	)
+
+#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89")
+#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x82")
diff --git a/include/const.h b/include/const.h
new file mode 100644
index 0000000..7828e61
--- /dev/null
+++ b/include/const.h
@@ -0,0 +1,15 @@
+#ifndef _CONST_H
+#define _CONST_H
+
+#define BUFFER_END 0x200000
+
+#define I_TYPE          0170000
+#define I_DIRECTORY	0040000
+#define I_REGULAR       0100000
+#define I_BLOCK_SPECIAL 0060000
+#define I_CHAR_SPECIAL  0020000
+#define I_NAMED_PIPE	0010000
+#define I_SET_UID_BIT   0004000
+#define I_SET_GID_BIT   0002000
+
+#endif
diff --git a/include/ctype.h b/include/ctype.h
new file mode 100644
index 0000000..4043d6e
--- /dev/null
+++ b/include/ctype.h
@@ -0,0 +1,34 @@
+#ifndef _CTYPE_H
+#define _CTYPE_H
+
+#define _U	0x01	/* upper */
+#define _L	0x02	/* lower */
+#define _D	0x04	/* digit */
+#define _C	0x08	/* cntrl */
+#define _P	0x10	/* punct */
+#define _S	0x20	/* white space (space/lf/tab) */
+#define _X	0x40	/* hex digit */
+#define _SP	0x80	/* hard space (0x20) */
+
+extern unsigned char _ctype[];
+extern char _ctmp;
+
+#define isalnum(c) ((_ctype+1)[c]&(_U|_L|_D))
+#define isalpha(c) ((_ctype+1)[c]&(_U|_L))
+#define iscntrl(c) ((_ctype+1)[c]&(_C))
+#define isdigit(c) ((_ctype+1)[c]&(_D))
+#define isgraph(c) ((_ctype+1)[c]&(_P|_U|_L|_D))
+#define islower(c) ((_ctype+1)[c]&(_L))
+#define isprint(c) ((_ctype+1)[c]&(_P|_U|_L|_D|_SP))
+#define ispunct(c) ((_ctype+1)[c]&(_P))
+#define isspace(c) ((_ctype+1)[c]&(_S))
+#define isupper(c) ((_ctype+1)[c]&(_U))
+#define isxdigit(c) ((_ctype+1)[c]&(_D|_X))
+
+#define isascii(c) (((unsigned) c)<=0x7f)
+#define toascii(c) (((unsigned) c)&0x7f)
+
+#define tolower(c) (_ctmp=c,isupper(_ctmp)?_ctmp+('a'+'A'):_ctmp)
+#define toupper(c) (_ctmp=c,islower(_ctmp)?_ctmp+('A'-'a'):_ctmp)
+
+#endif
diff --git a/include/errno.h b/include/errno.h
new file mode 100644
index 0000000..c282f69
--- /dev/null
+++ b/include/errno.h
@@ -0,0 +1,60 @@
+#ifndef _ERRNO_H
+#define _ERRNO_H
+
+/*
+ * ok, as I hadn't got any other source of information about
+ * possible error numbers, I was forced to use the same numbers
+ * as minix.
+ * Hopefully these are posix or something. I wouldn't know (and posix
+ * isn't telling me - they want $$$ for their f***ing standard).
+ *
+ * We don't use the _SIGN cludge of minix, so kernel returns must
+ * see to the sign by themselves.
+ *
+ * NOTE! Remember to change strerror() if you change this file!
+ */
+
+extern int errno;
+
+#define ERROR		99
+#define EPERM		 1
+#define ENOENT		 2
+#define ESRCH		 3
+#define EINTR		 4
+#define EIO		 5
+#define ENXIO		 6
+#define E2BIG		 7
+#define ENOEXEC		 8
+#define EBADF		 9
+#define ECHILD		10
+#define EAGAIN		11
+#define ENOMEM		12
+#define EACCES		13
+#define EFAULT		14
+#define ENOTBLK		15
+#define EBUSY		16
+#define EEXIST		17
+#define EXDEV		18
+#define ENODEV		19
+#define ENOTDIR		20
+#define EISDIR		21
+#define EINVAL		22
+#define ENFILE		23
+#define EMFILE		24
+#define ENOTTY		25
+#define ETXTBSY		26
+#define EFBIG		27
+#define ENOSPC		28
+#define ESPIPE		29
+#define EROFS		30
+#define EMLINK		31
+#define EPIPE		32
+#define EDOM		33
+#define ERANGE		34
+#define EDEADLK		35
+#define ENAMETOOLONG	36
+#define ENOLCK		37
+#define ENOSYS		38
+#define ENOTEMPTY	39
+
+#endif
diff --git a/include/fcntl.h b/include/fcntl.h
new file mode 100644
index 0000000..a5bf9af
--- /dev/null
+++ b/include/fcntl.h
@@ -0,0 +1,55 @@
+#ifndef _FCNTL_H
+#define _FCNTL_H
+
+#include <sys/types.h>
+
+/* open/fcntl - NOCTTY, NDELAY isn't implemented yet */
+#define O_ACCMODE	00003
+#define O_RDONLY	   00
+#define O_WRONLY	   01
+#define O_RDWR		   02
+#define O_CREAT		00100	/* not fcntl */
+#define O_EXCL		00200	/* not fcntl */
+#define O_NOCTTY	00400	/* not fcntl */
+#define O_TRUNC		01000	/* not fcntl */
+#define O_APPEND	02000
+#define O_NONBLOCK	04000	/* not fcntl */
+#define O_NDELAY	O_NONBLOCK
+
+/* Defines for fcntl-commands. Note that currently
+ * locking isn't supported, and other things aren't really
+ * tested.
+ */
+#define F_DUPFD		0	/* dup */
+#define F_GETFD		1	/* get f_flags */
+#define F_SETFD		2	/* set f_flags */
+#define F_GETFL		3	/* more flags (cloexec) */
+#define F_SETFL		4
+#define F_GETLK		5	/* not implemented */
+#define F_SETLK		6
+#define F_SETLKW	7
+
+/* for F_[GET|SET]FL */
+#define FD_CLOEXEC	1	/* actually anything with low bit set goes */
+
+/* Ok, these are locking features, and aren't implemented at any
+ * level. POSIX wants them.
+ */
+#define F_RDLCK		0
+#define F_WRLCK		1
+#define F_UNLCK		2
+
+/* Once again - not implemented, but ... */
+struct flock {
+	short l_type;
+	short l_whence;
+	off_t l_start;
+	off_t l_len;
+	pid_t l_pid;
+};
+
+extern int creat(const char * filename,mode_t mode);
+extern int fcntl(int fildes,int cmd, ...);
+extern int open(const char * filename, int flags, ...);
+
+#endif
diff --git a/include/linux/config.h b/include/linux/config.h
new file mode 100644
index 0000000..c84cacc
--- /dev/null
+++ b/include/linux/config.h
@@ -0,0 +1,53 @@
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+/* #define LASU_HD */
+#define LINUS_HD
+
+/*
+ * Amount of ram memory (in bytes, 640k-1M not discounted). Currently 8Mb.
+ * Don't make this bigger without making sure that there are enough page
+ * directory entries (boot/head.s)
+ */
+#if	defined(LINUS_HD)
+#define HIGH_MEMORY (0x800000)
+#elif	defined(LASU_HD)
+#define HIGH_MEMORY (0x400000)
+#else
+#error "must define hd"
+#endif
+
+/* End of buffer memory. Must be 0xA0000, or > 0x100000, 4096-byte aligned */
+#if (HIGH_MEMORY>=0x600000)
+#define BUFFER_END 0x200000
+#else
+#define BUFFER_END 0xA0000
+#endif
+
+/* Root device at bootup. */
+#if	defined(LINUS_HD)
+#define ROOT_DEV 0x306
+#elif	defined(LASU_HD)
+#define ROOT_DEV 0x302
+#else
+#error "must define HD"
+#endif
+
+/*
+ * HD type. If 2, put 2 structures with a comma. If just 1, put
+ * only 1 struct. The structs are { HEAD, SECTOR, TRACKS, WPCOM, LZONE, CTL }
+ *
+ * NOTE. CTL is supposed to be 0 for drives with less than 8 heads, and
+ * 8 if heads >= 8. Don't know why, and I haven't tested it on a drive with
+ * more than 8 heads, but that is what the bios-listings seem to imply. I
+ * just love not having a manual.
+ */
+#if	defined(LASU_HD)
+#define HD_TYPE { 7,35,915,65536,920,0 }
+#elif	defined(LINUS_HD)
+#define HD_TYPE { 5,17,980,300,980,0 },{ 5,17,980,300,980,0 }
+#else
+#error "must define a hard-disk type"
+#endif
+
+#endif
diff --git a/include/linux/fs.h b/include/linux/fs.h
new file mode 100644
index 0000000..e5db892
--- /dev/null
+++ b/include/linux/fs.h
@@ -0,0 +1,185 @@
+/*
+ * This file has definitions for some important file table
+ * structures etc.
+ */
+
+#ifndef _FS_H
+#define _FS_H
+
+#include <sys/types.h>
+
+/* devices are as follows: (same as minix, so we can use the minix
+ * file system. These are major numbers.)
+ *
+ * 0 - unused (nodev)
+ * 1 - /dev/mem
+ * 2 - /dev/fd
+ * 3 - /dev/hd
+ * 4 - /dev/ttyx
+ * 5 - /dev/tty
+ * 6 - /dev/lp
+ * 7 - unnamed pipes
+ */
+
+#define IS_BLOCKDEV(x) ((x)==2 || (x)==3)
+
+#define READ 0
+#define WRITE 1
+
+void buffer_init(void);
+
+#define MAJOR(a) (((unsigned)(a))>>8)
+#define MINOR(a) ((a)&0xff)
+
+#define NAME_LEN 14
+
+#define I_MAP_SLOTS 8
+#define Z_MAP_SLOTS 8
+#define SUPER_MAGIC 0x137F
+
+#define NR_OPEN 20
+#define NR_INODE 32
+#define NR_FILE 64
+#define NR_SUPER 8
+#define NR_HASH 307
+#define NR_BUFFERS nr_buffers
+#define BLOCK_SIZE 1024
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode)))
+#define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct dir_entry)))
+
+typedef char buffer_block[BLOCK_SIZE];
+
+struct buffer_head {
+	char * b_data;			/* pointer to data block (1024 bytes) */
+	unsigned short b_dev;		/* device (0 = free) */
+	unsigned short b_blocknr;	/* block number */
+	unsigned char b_uptodate;
+	unsigned char b_dirt;		/* 0-clean,1-dirty */
+	unsigned char b_count;		/* users using this block */
+	unsigned char b_lock;		/* 0 - ok, 1 -locked */
+	struct task_struct * b_wait;
+	struct buffer_head * b_prev;
+	struct buffer_head * b_next;
+	struct buffer_head * b_prev_free;
+	struct buffer_head * b_next_free;
+};
+
+struct d_inode {
+	unsigned short i_mode;
+	unsigned short i_uid;
+	unsigned long i_size;
+	unsigned long i_time;
+	unsigned char i_gid;
+	unsigned char i_nlinks;
+	unsigned short i_zone[9];
+};
+
+struct m_inode {
+	unsigned short i_mode;
+	unsigned short i_uid;
+	unsigned long i_size;
+	unsigned long i_mtime;
+	unsigned char i_gid;
+	unsigned char i_nlinks;
+	unsigned short i_zone[9];
+/* these are in memory also */
+	struct task_struct * i_wait;
+	unsigned long i_atime;
+	unsigned long i_ctime;
+	unsigned short i_dev;
+	unsigned short i_num;
+	unsigned short i_count;
+	unsigned char i_lock;
+	unsigned char i_dirt;
+	unsigned char i_pipe;
+	unsigned char i_mount;
+	unsigned char i_seek;
+	unsigned char i_update;
+};
+
+#define PIPE_HEAD(inode) (((long *)((inode).i_zone))[0])
+#define PIPE_TAIL(inode) (((long *)((inode).i_zone))[1])
+#define PIPE_SIZE(inode) ((PIPE_HEAD(inode)-PIPE_TAIL(inode))&(PAGE_SIZE-1))
+#define PIPE_EMPTY(inode) (PIPE_HEAD(inode)==PIPE_TAIL(inode))
+#define PIPE_FULL(inode) (PIPE_SIZE(inode)==(PAGE_SIZE-1))
+#define INC_PIPE(head) \
+__asm__("incl %0\n\tandl $4095,%0"::"m" (head))
+
+struct file {
+	unsigned short f_mode;
+	unsigned short f_flags;
+	unsigned short f_count;
+	struct m_inode * f_inode;
+	off_t f_pos;
+};
+
+struct super_block {
+	unsigned short s_ninodes;
+	unsigned short s_nzones;
+	unsigned short s_imap_blocks;
+	unsigned short s_zmap_blocks;
+	unsigned short s_firstdatazone;
+	unsigned short s_log_zone_size;
+	unsigned long s_max_size;
+	unsigned short s_magic;
+/* These are only in memory */
+	struct buffer_head * s_imap[8];
+	struct buffer_head * s_zmap[8];
+	unsigned short s_dev;
+	struct m_inode * s_isup;
+	struct m_inode * s_imount;
+	unsigned long s_time;
+	unsigned char s_rd_only;
+	unsigned char s_dirt;
+};
+
+struct dir_entry {
+	unsigned short inode;
+	char name[NAME_LEN];
+};
+
+extern struct m_inode inode_table[NR_INODE];
+extern struct file file_table[NR_FILE];
+extern struct super_block super_block[NR_SUPER];
+extern struct buffer_head * start_buffer;
+extern int nr_buffers;
+
+extern void truncate(struct m_inode * inode);
+extern void sync_inodes(void);
+extern void wait_on(struct m_inode * inode);
+extern int bmap(struct m_inode * inode,int block);
+extern int create_block(struct m_inode * inode,int block);
+extern struct m_inode * namei(const char * pathname);
+extern int open_namei(const char * pathname, int flag, int mode,
+	struct m_inode ** res_inode);
+extern void iput(struct m_inode * inode);
+extern struct m_inode * iget(int dev,int nr);
+extern struct m_inode * get_empty_inode(void);
+extern struct m_inode * get_pipe_inode(void);
+extern struct buffer_head * get_hash_table(int dev, int block);
+extern struct buffer_head * getblk(int dev, int block);
+extern void ll_rw_block(int rw, struct buffer_head * bh);
+extern void brelse(struct buffer_head * buf);
+extern struct buffer_head * bread(int dev,int block);
+extern int new_block(int dev);
+extern void free_block(int dev, int block);
+extern struct m_inode * new_inode(int dev);
+extern void free_inode(struct m_inode * inode);
+
+extern void mount_root(void);
+
+extern inline struct super_block * get_super(int dev)
+{
+	struct super_block * s;
+
+	for(s = 0+super_block;s < NR_SUPER+super_block; s++)
+		if (s->s_dev == dev)
+			return s;
+	return NULL;
+}
+
+#endif
diff --git a/include/linux/hdreg.h b/include/linux/hdreg.h
new file mode 100644
index 0000000..04ebf87
--- /dev/null
+++ b/include/linux/hdreg.h
@@ -0,0 +1,99 @@
+/*
+ * This file contains some defines for the AT-hd-controller.
+ * Various sources. Check out some definitions (see comments with
+ * a ques).
+ */
+#ifndef _HDREG_H
+#define _HDREG_H
+
+/* currently supports only 1 hd, put type here */
+#define HARD_DISK_TYPE 17
+
+/*
+ * Ok, hard-disk-type is currently hardcoded. Not beatiful,
+ * but easier. We don't use BIOS for anything else, why should
+ * we get HD-type from it? Get these values from Reference Guide.
+ */
+
+#if HARD_DISK_TYPE == 17
+#define _CYL	977
+#define _HEAD	5
+#define __WPCOM	300
+#define _LZONE	977
+#define _SECT	17
+#define _CTL	0
+#elif HARD_DISK_TYPE == 18
+#define _CYL	977
+#define _HEAD	7
+#define __WPCOM	(-1)
+#define _LZONE	977
+#define _SECT	17
+#define _CTL	0
+#else
+#error Define HARD_DISK_TYPE and parameters, add your own entries as well
+#endif
+
+/* Controller wants just wp-com/4 */
+#if __WPCOM >= 0
+#define _WPCOM ((__WPCOM)>>2)
+#else
+#define _WPCOM __WPCOM
+#endif
+
+/* Hd controller regs. Ref: IBM AT Bios-listing */
+#define HD_DATA		0x1f0	/* _CTL when writing */
+#define HD_ERROR	0x1f1	/* see err-bits */
+#define HD_NSECTOR	0x1f2	/* nr of sectors to read/write */
+#define HD_SECTOR	0x1f3	/* starting sector */
+#define HD_LCYL		0x1f4	/* starting cylinder */
+#define HD_HCYL		0x1f5	/* high byte of starting cyl */
+#define HD_CURRENT	0x1f6	/* 101dhhhh , d=drive, hhhh=head */
+#define HD_STATUS	0x1f7	/* see status-bits */
+#define HD_PRECOMP HD_ERROR	/* same io address, read=error, write=precomp */
+#define HD_COMMAND HD_STATUS	/* same io address, read=status, write=cmd */
+
+#define HD_CMD		0x3f6
+
+/* Bits of HD_STATUS */
+#define ERR_STAT	0x01
+#define INDEX_STAT	0x02
+#define ECC_STAT	0x04	/* Corrected error */
+#define DRQ_STAT	0x08
+#define SEEK_STAT	0x10
+#define WRERR_STAT	0x20
+#define READY_STAT	0x40
+#define BUSY_STAT	0x80
+
+/* Values for HD_COMMAND */
+#define WIN_RESTORE		0x10
+#define WIN_READ		0x20
+#define WIN_WRITE		0x30
+#define WIN_VERIFY		0x40
+#define WIN_FORMAT		0x50
+#define WIN_INIT		0x60
+#define WIN_SEEK 		0x70
+#define WIN_DIAGNOSE		0x90
+#define WIN_SPECIFY		0x91
+
+/* Bits for HD_ERROR */
+#define MARK_ERR	0x01	/* Bad address mark ? */
+#define TRK0_ERR	0x02	/* couldn't find track 0 */
+#define ABRT_ERR	0x04	/* ? */
+#define ID_ERR		0x10	/* ? */
+#define ECC_ERR		0x40	/* ? */
+#define	BBD_ERR		0x80	/* ? */
+
+struct partition {
+	unsigned char boot_ind;		/* 0x80 - active (unused) */
+	unsigned char head;		/* ? */
+	unsigned char sector;		/* ? */
+	unsigned char cyl;		/* ? */
+	unsigned char sys_ind;		/* ? */
+	unsigned char end_head;		/* ? */
+	unsigned char end_sector;	/* ? */
+	unsigned char end_cyl;		/* ? */
+	unsigned int start_sect;	/* starting sector counting from 0 */
+	unsigned int nr_sects;		/* nr of sectors in partition */
+};
+
+#endif
diff --git a/include/linux/head.h b/include/linux/head.h
new file mode 100644
index 0000000..db3dda2
--- /dev/null
+++ b/include/linux/head.h
@@ -0,0 +1,20 @@
+#ifndef _HEAD_H
+#define _HEAD_H
+
+typedef struct desc_struct {
+	unsigned long a,b;
+} desc_table[256];
+
+extern unsigned long pg_dir[1024];
+extern desc_table idt,gdt;
+
+#define GDT_NUL 0
+#define GDT_CODE 1
+#define GDT_DATA 2
+#define GDT_TMP 3
+
+#define LDT_NUL 0
+#define LDT_CODE 1
+#define LDT_DATA 2
+
+#endif
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
new file mode 100644
index 0000000..9e533a7
--- /dev/null
+++ b/include/linux/kernel.h
@@ -0,0 +1,8 @@
+/*
+ * 'kernel.h' contains some often-used function prototypes etc
+ */
+void verify_area(void * addr,int count);
+volatile void panic(const char * str);
+int printf(const char * fmt, ...);
+int printk(const char * fmt, ...);
+int tty_write(unsigned ch,char * buf,int count);
diff --git a/include/linux/mm.h b/include/linux/mm.h
new file mode 100644
index 0000000..5a160f3
--- /dev/null
+++ b/include/linux/mm.h
@@ -0,0 +1,10 @@
+#ifndef _MM_H
+#define _MM_H
+
+#define PAGE_SIZE 4096
+
+extern unsigned long get_free_page(void);
+extern unsigned long put_page(unsigned long page,unsigned long address);
+extern void free_page(unsigned long addr);
+
+#endif
diff --git a/include/linux/sched.h b/include/linux/sched.h
new file mode 100644
index 0000000..bf6b639
--- /dev/null
+++ b/include/linux/sched.h
@@ -0,0 +1,230 @@
+#ifndef _SCHED_H
+#define _SCHED_H
+
+#define NR_TASKS 64
+#define HZ 100
+
+#define FIRST_TASK task[0]
+#define LAST_TASK task[NR_TASKS-1]
+
+#include <linux/head.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+
+#if (NR_OPEN > 32)
+#error "Currently the close-on-exec-flags are in one word, max 32 files/proc"
+#endif
+
+#define TASK_RUNNING		0
+#define TASK_INTERRUPTIBLE	1
+#define TASK_UNINTERRUPTIBLE	2
+#define TASK_ZOMBIE		3
+#define TASK_STOPPED		4
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+extern int copy_page_tables(unsigned long from, unsigned long to, long size);
+extern int free_page_tables(unsigned long from, long size);
+
+extern void sched_init(void);
+extern void schedule(void);
+extern void trap_init(void);
+extern void panic(const char * str);
+extern int tty_write(unsigned minor,char * buf,int count);
+
+typedef int (*fn_ptr)();
+
+struct i387_struct {
+	long	cwd;
+	long	swd;
+	long	twd;
+	long	fip;
+	long	fcs;
+	long	foo;
+	long	fos;
+	long	st_space[20];	/* 8*10 bytes for each FP-reg = 80 bytes */
+};
+
+struct tss_struct {
+	long	back_link;	/* 16 high bits zero */
+	long	esp0;
+	long	ss0;		/* 16 high bits zero */
+	long	esp1;
+	long	ss1;		/* 16 high bits zero */
+	long	esp2;
+	long	ss2;		/* 16 high bits zero */
+	long	cr3;
+	long	eip;
+	long	eflags;
+	long	eax,ecx,edx,ebx;
+	long	esp;
+	long	ebp;
+	long	esi;
+	long	edi;
+	long	es;		/* 16 high bits zero */
+	long	cs;		/* 16 high bits zero */
+	long	ss;		/* 16 high bits zero */
+	long	ds;		/* 16 high bits zero */
+	long	fs;		/* 16 high bits zero */
+	long	gs;		/* 16 high bits zero */
+	long	ldt;		/* 16 high bits zero */
+	long	trace_bitmap;	/* bits: trace 0, bitmap 16-31 */
+	struct i387_struct i387;
+};
+
+struct task_struct {
+/* these are hardcoded - don't touch */
+	long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
+	long counter;
+	long priority;
+	long signal;
+	fn_ptr sig_restorer;
+	fn_ptr sig_fn[32];
+/* various fields */
+	int exit_code;
+	unsigned long end_code,end_data,brk,start_stack;
+	long pid,father,pgrp,session,leader;
+	unsigned short uid,euid,suid;
+	unsigned short gid,egid,sgid;
+	long alarm;
+	long utime,stime,cutime,cstime,start_time;
+	unsigned short used_math;
+/* file system info */
+	int tty;		/* -1 if no tty, so it must be signed */
+	unsigned short umask;
+	struct m_inode * pwd;
+	struct m_inode * root;
+	unsigned long close_on_exec;
+	struct file * filp[NR_OPEN];
+/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
+	struct desc_struct ldt[3];
+/* tss for this task */
+	struct tss_struct tss;
+};
+
+/*
+ *  INIT_TASK is used to set up the first task table, touch at
+ * your own risk!. Base=0, limit=0x9ffff (=640kB)
+ */
+#define INIT_TASK \
+/* state etc */	{ 0,15,15, \
+/* signals */	0,NULL,{(fn_ptr) 0,}, \
+/* ec,brk... */	0,0,0,0,0, \
+/* pid etc.. */	0,-1,0,0,0, \
+/* uid etc */	0,0,0,0,0,0, \
+/* alarm */	0,0,0,0,0,0, \
+/* math */	0, \
+/* fs info */	-1,0133,NULL,NULL,0, \
+/* filp */	{NULL,}, \
+	{ \
+		{0,0}, \
+/* ldt */	{0x9f,0xc0fa00}, \
+		{0x9f,0xc0f200}, \
+	}, \
+/*tss*/	{0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\
+	 0,0,0,0,0,0,0,0, \
+	 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \
+	 _LDT(0),0x80000000, \
+		{} \
+	}, \
+}
+
+extern struct task_struct *task[NR_TASKS];
+extern struct task_struct *last_task_used_math;
+extern struct task_struct *current;
+extern long volatile jiffies;
+extern long startup_time;
+
+#define CURRENT_TIME (startup_time+jiffies/HZ)
+
+extern void sleep_on(struct task_struct ** p);
+extern void interruptible_sleep_on(struct task_struct ** p);
+extern void wake_up(struct task_struct ** p);
+
+/*
+ * Entry into gdt where to find first TSS. 0-nul, 1-cs, 2-ds, 3-syscall
+ * 4-TSS0, 5-LDT0, 6-TSS1 etc ...
+ */
+#define FIRST_TSS_ENTRY 4
+#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
+#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
+#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
+#define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n)))
+#define lldt(n) __asm__("lldt %%ax"::"a" (_LDT(n)))
+#define str(n) \
+__asm__("str %%ax\n\t" \
+	"subl %2,%%eax\n\t" \
+	"shrl $4,%%eax" \
+	:"=a" (n) \
+	:"a" (0),"i" (FIRST_TSS_ENTRY<<3))
+/*
+ *	switch_to(n) should switch tasks to task nr n, first
+ * checking that n isn't the current task, in which case it does nothing.
+ * This also clears the TS-flag if the task we switched to has used
+ * tha math co-processor latest.
+ */
+#define switch_to(n) {\
+struct {long a,b;} __tmp; \
+__asm__("cmpl %%ecx,_current\n\t" \
+	"je 1f\n\t" \
+	"xchgl %%ecx,_current\n\t" \
+	"movw %%dx,%1\n\t" \
+	"ljmp %0\n\t" \
+	"cmpl %%ecx,%2\n\t" \
+	"jne 1f\n\t" \
+	"clts\n" \
+	"1:" \
+	::"m" (*&__tmp.a),"m" (*&__tmp.b), \
+	"m" (last_task_used_math),"d" _TSS(n),"c" ((long) task[n])); \
+}
+
+#define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000)
+
+#define _set_base(addr,base) \
+__asm__("movw %%dx,%0\n\t" \
+	"rorl $16,%%edx\n\t" \
+	"movb %%dl,%1\n\t" \
+	"movb %%dh,%2" \
+	::"m" (*((addr)+2)), \
+	  "m" (*((addr)+4)), \
+	  "m" (*((addr)+7)), \
+	  "d" (base) \
+	:"dx")
+
+#define _set_limit(addr,limit) \
+__asm__("movw %%dx,%0\n\t" \
+	"rorl $16,%%edx\n\t" \
+	"movb %1,%%dh\n\t" \
+	"andb $0xf0,%%dh\n\t" \
+	"orb %%dh,%%dl\n\t" \
+	"movb %%dl,%1" \
+	::"m" (*(addr)), \
+	  "m" (*((addr)+6)), \
+	  "d" (limit) \
+	:"dx")
+
+#define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base )
+#define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 )
+
+#define _get_base(addr) ({\
+unsigned long __base; \
+__asm__("movb %3,%%dh\n\t" \
+	"movb %2,%%dl\n\t" \
+	"shll $16,%%edx\n\t" \
+	"movw %1,%%dx" \
+	:"=d" (__base) \
+	:"m" (*((addr)+2)), \
+	 "m" (*((addr)+4)), \
+	 "m" (*((addr)+7))); \
+__base;})
+
+#define get_base(ldt) _get_base( ((char *)&(ldt)) )
+
+#define get_limit(segment) ({ \
+unsigned long __limit; \
+__asm__("lsll %1,%0\n\tincl %0":"=r" (__limit):"r" (segment)); \
+__limit;})
+
+#endif
diff --git a/include/linux/sys.h b/include/linux/sys.h
new file mode 100644
index 0000000..acdcc95
--- /dev/null
+++ b/include/linux/sys.h
@@ -0,0 +1,80 @@
+extern int sys_setup();
+extern int sys_exit();
+extern int sys_fork();
+extern int sys_read();
+extern int sys_write();
+extern int sys_open();
+extern int sys_close();
+extern int sys_waitpid();
+extern int sys_creat();
+extern int sys_link();
+extern int sys_unlink();
+extern int sys_execve();
+extern int sys_chdir();
+extern int sys_time();
+extern int sys_mknod();
+extern int sys_chmod();
+extern int sys_chown();
+extern int sys_break();
+extern int sys_stat();
+extern int sys_lseek();
+extern int sys_getpid();
+extern int sys_mount();
+extern int sys_umount();
+extern int sys_setuid();
+extern int sys_getuid();
+extern int sys_stime();
+extern int sys_ptrace();
+extern int sys_alarm();
+extern int sys_fstat();
+extern int sys_pause();
+extern int sys_utime();
+extern int sys_stty();
+extern int sys_gtty();
+extern int sys_access();
+extern int sys_nice();
+extern int sys_ftime();
+extern int sys_sync();
+extern int sys_kill();
+extern int sys_rename();
+extern int sys_mkdir();
+extern int sys_rmdir();
+extern int sys_dup();
+extern int sys_pipe();
+extern int sys_times();
+extern int sys_prof();
+extern int sys_brk();
+extern int sys_setgid();
+extern int sys_getgid();
+extern int sys_signal();
+extern int sys_geteuid();
+extern int sys_getegid();
+extern int sys_acct();
+extern int sys_phys();
+extern int sys_lock();
+extern int sys_ioctl();
+extern int sys_fcntl();
+extern int sys_mpx();
+extern int sys_setpgid();
+extern int sys_ulimit();
+extern int sys_uname();
+extern int sys_umask();
+extern int sys_chroot();
+extern int sys_ustat();
+extern int sys_dup2();
+extern int sys_getppid();
+extern int sys_getpgrp();
+extern int sys_setsid();
+
+fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
+sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
+sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
+sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
+sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
+sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
+sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
+sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
+sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
+sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
+sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
+sys_getpgrp,sys_setsid};
diff --git a/include/linux/tty.h b/include/linux/tty.h
new file mode 100644
index 0000000..1d103e0
--- /dev/null
+++ b/include/linux/tty.h
@@ -0,0 +1,74 @@
+/*
+ * 'tty.h' defines some structures used by tty_io.c and some defines.
+ *
+ * NOTE! Don't touch this without checking that nothing in rs_io.s or
+ * con_io.s breaks. Some constants are hardwired into the system (mainly
+ * offsets into 'tty_queue'
+ */
+
+#ifndef _TTY_H
+#define _TTY_H
+
+#include <termios.h>
+
+#define TTY_BUF_SIZE 1024
+
+struct tty_queue {
+	unsigned long data;
+	unsigned long head;
+	unsigned long tail;
+	struct task_struct * proc_list;
+	char buf[TTY_BUF_SIZE];
+};
+
+#define INC(a) ((a) = ((a)+1) & (TTY_BUF_SIZE-1))
+#define DEC(a) ((a) = ((a)-1) & (TTY_BUF_SIZE-1))
+#define EMPTY(a) ((a).head == (a).tail)
+#define LEFT(a) (((a).tail-(a).head-1)&(TTY_BUF_SIZE-1))
+#define LAST(a) ((a).buf[(TTY_BUF_SIZE-1)&((a).head-1)])
+#define FULL(a) (!LEFT(a))
+#define CHARS(a) (((a).head-(a).tail)&(TTY_BUF_SIZE-1))
+#define GETCH(queue,c) \
+(void)({c=(queue).buf[(queue).tail];INC((queue).tail);})
+#define PUTCH(c,queue) \
+(void)({(queue).buf[(queue).head]=(c);INC((queue).head);})
+
+#define EOF_CHAR(tty) ((tty)->termios.c_cc[VEOF])
+#define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR])
+#define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP])
+#define START_CHAR(tty) ((tty)->termios.c_cc[VSTART])
+#define ERASE_CHAR(tty) ((tty)->termios.c_cc[VERASE])
+
+struct tty_struct {
+	struct termios termios;
+	int pgrp;
+	int stopped;
+	void (*write)(struct tty_struct * tty);
+	struct tty_queue read_q;
+	struct tty_queue write_q;
+	struct tty_queue secondary;
+	};
+
+extern struct tty_struct tty_table[];
+
+/*	intr=^C		quit=^|		erase=del	kill=^U
+	eof=^D		vtime=\0	vmin=\1		sxtc=\0
+	start=^Q	stop=^S		susp=^Y		eol=\0
+	reprint=^R	discard=^U	werase=^W	lnext=^V
+	eol2=\0
+*/
+#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\031\0\022\017\027\026\0"
+
+void rs_init(void);
+void con_init(void);
+void tty_init(void);
+
+int tty_read(unsigned c, char * buf, int n);
+int tty_write(unsigned c, char * buf, int n);
+
+void rs_write(struct tty_struct * tty);
+void con_write(struct tty_struct * tty);
+
+void copy_to_cooked(struct tty_struct * tty);
+
+#endif
diff --git a/include/signal.h b/include/signal.h
new file mode 100644
index 0000000..b895813
--- /dev/null
+++ b/include/signal.h
@@ -0,0 +1,65 @@
+#ifndef _SIGNAL_H
+#define _SIGNAL_H
+
+#include <sys/types.h>
+
+typedef int sig_atomic_t;
+typedef unsigned int sigset_t;		/* 32 bits */
+
+#define _NSIG             32
+#define NSIG		_NSIG
+
+#define SIGHUP		 1
+#define SIGINT		 2
+#define SIGQUIT		 3
+#define SIGILL		 4
+#define SIGTRAP		 5
+#define SIGABRT		 6
+#define SIGIOT		 6
+#define SIGUNUSED	 7
+#define SIGFPE		 8
+#define SIGKILL		 9
+#define SIGUSR1		10
+#define SIGSEGV		11
+#define SIGUSR2		12
+#define SIGPIPE		13
+#define SIGALRM		14
+#define SIGTERM		15
+#define SIGSTKFLT	16
+#define SIGCHLD		17
+#define SIGCONT		18
+#define SIGSTOP		19
+#define SIGTSTP		20
+#define SIGTTIN		21
+#define SIGTTOU		22
+
+/* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */
+#define SA_NOCLDSTOP	1
+
+#define SIG_BLOCK          0	/* for blocking signals */
+#define SIG_UNBLOCK        1	/* for unblocking signals */
+#define SIG_SETMASK        2	/* for setting the signal mask */
+
+#define SIG_DFL		((void (*)(int))0)	/* default signal handling */
+#define SIG_IGN		((void (*)(int))1)	/* ignore signal */
+
+struct sigaction {
+	void (*sa_handler)(int);
+	sigset_t sa_mask;
+	int sa_flags;
+};
+
+void (*signal(int _sig, void (*_func)(int)))(int);
+int raise(int sig);
+int kill(pid_t pid, int sig);
+int sigaddset(sigset_t *mask, int signo);
+int sigdelset(sigset_t *mask, int signo);
+int sigemptyset(sigset_t *mask);
+int sigfillset(sigset_t *mask);
+int sigismember(sigset_t *mask, int signo); /* 1 - is, 0 - not, -1 error */
+int sigpending(sigset_t *set);
+int sigprocmask(int how, sigset_t *set, sigset_t *oldset);
+int sigsuspend(sigset_t *sigmask);
+int sigaction(int sig, struct sigaction *act, struct sigaction *oldact);
+
+#endif /* _SIGNAL_H */
diff --git a/include/stdarg.h b/include/stdarg.h
new file mode 100755
index 0000000..fd79ec0
--- /dev/null
+++ b/include/stdarg.h
@@ -0,0 +1,28 @@
+#ifndef _STDARG_H
+#define _STDARG_H
+
+typedef char *va_list;
+
+/* Amount of space required in an argument list for an arg of type TYPE.
+   TYPE may alternatively be an expression whose type is used.  */
+
+#define __va_rounded_size(TYPE)  \
+  (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
+
+#ifndef __sparc__
+#define va_start(AP, LASTARG) 						\
+ (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
+#else
+#define va_start(AP, LASTARG) 						\
+ (__builtin_saveregs (),						\
+  AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
+#endif
+
+void va_end (va_list);		/* Defined in gnulib */
+#define va_end(AP)
+
+#define va_arg(AP, TYPE)						\
+ (AP += __va_rounded_size (TYPE),					\
+  *((TYPE *) (AP - __va_rounded_size (TYPE))))
+
+#endif /* _STDARG_H */
diff --git a/include/stddef.h b/include/stddef.h
new file mode 100644
index 0000000..97f72ff
--- /dev/null
+++ b/include/stddef.h
@@ -0,0 +1,19 @@
+#ifndef _STDDEF_H
+#define _STDDEF_H
+
+#ifndef _PTRDIFF_T
+#define _PTRDIFF_T
+typedef long ptrdiff_t;
+#endif
+
+#ifndef _SIZE_T
+#define _SIZE_T
+typedef unsigned long size_t;
+#endif
+
+#undef NULL
+#define NULL ((void *)0)
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#endif
diff --git a/include/string.h b/include/string.h
new file mode 100644
index 0000000..05a6d9c
--- /dev/null
+++ b/include/string.h
@@ -0,0 +1,405 @@
+#ifndef _STRING_H_
+#define _STRING_H_
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#ifndef _SIZE_T
+#define _SIZE_T
+typedef unsigned int size_t;
+#endif
+
+extern char * strerror(int errno);
+
+/*
+ * This string-include defines all string functions as inline
+ * functions. Use gcc. It also assumes ds=es=data space, this should be
+ * normal. Most of the string-functions are rather heavily hand-optimized,
+ * see especially strtok,strstr,str[c]spn. They should work, but are not
+ * very easy to understand. Everything is done entirely within the register
+ * set, making the functions fast and clean. String instructions have been
+ * used through-out, making for "slightly" unclear code :-)
+ *
+ *		(C) 1991 Linus Torvalds
+ */
+ 
+extern inline char * strcpy(char * dest,const char *src)
+{
+__asm__("cld\n"
+	"1:\tlodsb\n\t"
+	"stosb\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b"
+	::"S" (src),"D" (dest):"si","di","ax");
+return dest;
+}
+
+extern inline char * strncpy(char * dest,const char *src,int count)
+{
+__asm__("cld\n"
+	"1:\tdecl %2\n\t"
+	"js 2f\n\t"
+	"lodsb\n\t"
+	"stosb\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b\n\t"
+	"rep\n\t"
+	"stosb\n"
+	"2:"
+	::"S" (src),"D" (dest),"c" (count):"si","di","ax","cx");
+return dest;
+}
+
+extern inline char * strcat(char * dest,const char * src)
+{
+__asm__("cld\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"decl %1\n"
+	"1:\tlodsb\n\t"
+	"stosb\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b"
+	::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff):"si","di","ax","cx");
+return dest;
+}
+
+extern inline char * strncat(char * dest,const char * src,int count)
+{
+__asm__("cld\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"decl %1\n\t"
+	"movl %4,%3\n"
+	"1:\tdecl %3\n\t"
+	"js 2f\n\t"
+	"lodsb\n\t"
+	"stosb\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b\n"
+	"2:\txorl %2,%2\n\t"
+	"stosb"
+	::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count)
+	:"si","di","ax","cx");
+return dest;
+}
+
+extern inline int strcmp(const char * cs,const char * ct)
+{
+register int __res __asm__("ax");
+__asm__("cld\n"
+	"1:\tlodsb\n\t"
+	"scasb\n\t"
+	"jne 2f\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b\n\t"
+	"xorl %%eax,%%eax\n\t"
+	"jmp 3f\n"
+	"2:\tmovl $1,%%eax\n\t"
+	"jl 3f\n\t"
+	"negl %%eax\n"
+	"3:"
+	:"=a" (__res):"D" (cs),"S" (ct):"si","di");
+return __res;
+}
+
+extern inline int strncmp(const char * cs,const char * ct,int count)
+{
+register int __res __asm__("ax");
+__asm__("cld\n"
+	"1:\tdecl %3\n\t"
+	"js 2f\n\t"
+	"lodsb\n\t"
+	"scasb\n\t"
+	"jne 3f\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b\n"
+	"2:\txorl %%eax,%%eax\n\t"
+	"jmp 4f\n"
+	"3:\tmovl $1,%%eax\n\t"
+	"jl 4f\n\t"
+	"negl %%eax\n"
+	"4:"
+	:"=a" (__res):"D" (cs),"S" (ct),"c" (count):"si","di","cx");
+return __res;
+}
+
+extern inline char * strchr(const char * s,char c)
+{
+register char * __res __asm__("ax");
+__asm__("cld\n\t"
+	"movb %%al,%%ah\n"
+	"1:\tlodsb\n\t"
+	"cmpb %%ah,%%al\n\t"
+	"je 2f\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b\n\t"
+	"movl $1,%1\n"
+	"2:\tmovl %1,%0\n\t"
+	"decl %0"
+	:"=a" (__res):"S" (s),"0" (c):"si");
+return __res;
+}
+
+extern inline char * strrchr(const char * s,char c)
+{
+register char * __res __asm__("dx");
+__asm__("cld\n\t"
+	"movb %%al,%%ah\n"
+	"1:\tlodsb\n\t"
+	"cmpb %%ah,%%al\n\t"
+	"jne 2f\n\t"
+	"movl %%esi,%0\n\t"
+	"decl %0\n"
+	"2:\ttestb %%al,%%al\n\t"
+	"jne 1b"
+	:"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si");
+return __res;
+}
+
+extern inline int strspn(const char * cs, const char * ct)
+{
+register char * __res __asm__("si");
+__asm__("cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"movl %%ecx,%%edx\n"
+	"1:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 2f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"je 1b\n"
+	"2:\tdecl %0"
+	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+	:"ax","cx","dx","di");
+return __res-cs;
+}
+
+extern inline int strcspn(const char * cs, const char * ct)
+{
+register char * __res __asm__("si");
+__asm__("cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"movl %%ecx,%%edx\n"
+	"1:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 2f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"jne 1b\n"
+	"2:\tdecl %0"
+	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+	:"ax","cx","dx","di");
+return __res-cs;
+}
+
+extern inline char * strpbrk(const char * cs,const char * ct)
+{
+register char * __res __asm__("si");
+__asm__("cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"movl %%ecx,%%edx\n"
+	"1:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 2f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"jne 1b\n\t"
+	"decl %0\n\t"
+	"jmp 3f\n"
+	"2:\txorl %0,%0\n"
+	"3:"
+	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+	:"ax","cx","dx","di");
+return __res;
+}
+
+extern inline char * strstr(const char * cs,const char * ct)
+{
+register char * __res __asm__("ax");
+__asm__("cld\n\t" \
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"	/* NOTE! This also sets Z if searchstring='' */
+	"movl %%ecx,%%edx\n"
+	"1:\tmovl %4,%%edi\n\t"
+	"movl %%esi,%%eax\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repe\n\t"
+	"cmpsb\n\t"
+	"je 2f\n\t"		/* also works for empty string, see above */
+	"xchgl %%eax,%%esi\n\t"
+	"incl %%esi\n\t"
+	"cmpb $0,-1(%%eax)\n\t"
+	"jne 1b\n\t"
+	"xorl %%eax,%%eax\n\t"
+	"2:"
+	:"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct)
+	:"cx","dx","di","si");
+return __res;
+}
+
+extern inline int strlen(const char * s)
+{
+register int __res __asm__("cx");
+__asm__("cld\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %0\n\t"
+	"decl %0"
+	:"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff):"di");
+return __res;
+}
+
+extern char * ___strtok;
+
+extern inline char * strtok(char * s,const char * ct)
+{
+register char * __res __asm__("si");
+__asm__("testl %1,%1\n\t"
+	"jne 1f\n\t"
+	"testl %0,%0\n\t"
+	"je 8f\n\t"
+	"movl %0,%1\n"
+	"1:\txorl %0,%0\n\t"
+	"movl $-1,%%ecx\n\t"
+	"xorl %%eax,%%eax\n\t"
+	"cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"je 7f\n\t"			/* empty delimeter-string */
+	"movl %%ecx,%%edx\n"
+	"2:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 7f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"je 2b\n\t"
+	"decl %1\n\t"
+	"cmpb $0,(%1)\n\t"
+	"je 7f\n\t"
+	"movl %1,%0\n"
+	"3:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 5f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"jne 3b\n\t"
+	"decl %1\n\t"
+	"cmpb $0,(%1)\n\t"
+	"je 5f\n\t"
+	"movb $0,(%1)\n\t"
+	"incl %1\n\t"
+	"jmp 6f\n"
+	"5:\txorl %1,%1\n"
+	"6:\tcmpb $0,(%0)\n\t"
+	"jne 7f\n\t"
+	"xorl %0,%0\n"
+	"7:\ttestl %0,%0\n\t"
+	"jne 8f\n\t"
+	"movl %0,%1\n"
+	"8:"
+	:"=b" (__res),"=S" (___strtok)
+	:"0" (___strtok),"1" (s),"g" (ct)
+	:"ax","cx","dx","di");
+return __res;
+}
+
+extern inline void * memcpy(void * dest,const void * src, int n)
+{
+__asm__("cld\n\t"
+	"rep\n\t"
+	"movsb"
+	::"c" (n),"S" (src),"D" (dest)
+	:"cx","si","di");
+return dest;
+}
+
+extern inline void * memmove(void * dest,const void * src, int n)
+{
+if (dest<src)
+__asm__("cld\n\t"
+	"rep\n\t"
+	"movsb"
+	::"c" (n),"S" (src),"D" (dest)
+	:"cx","si","di");
+else
+__asm__("std\n\t"
+	"rep\n\t"
+	"movsb"
+	::"c" (n),"S" (src+n-1),"D" (dest+n-1)
+	:"cx","si","di");
+return dest;
+}
+
+extern inline int memcmp(const void * cs,const void * ct,int count)
+{
+register int __res __asm__("ax");
+__asm__("cld\n\t"
+	"repe\n\t"
+	"cmpsb\n\t"
+	"je 1f\n\t"
+	"movl $1,%%eax\n\t"
+	"jl 1f\n\t"
+	"negl %%eax\n"
+	"1:"
+	:"=a" (__res):"0" (0),"D" (cs),"S" (ct),"c" (count)
+	:"si","di","cx");
+return __res;
+}
+
+extern inline void * memchr(const void * cs,char c,int count)
+{
+register void * __res __asm__("di");
+if (!count)
+	return NULL;
+__asm__("cld\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"je 1f\n\t"
+	"movl $1,%0\n"
+	"1:\tdecl %0"
+	:"=D" (__res):"a" (c),"D" (cs),"c" (count)
+	:"cx");
+return __res;
+}
+
+extern inline void * memset(void * s,char c,int count)
+{
+__asm__("cld\n\t"
+	"rep\n\t"
+	"stosb"
+	::"a" (c),"D" (s),"c" (count)
+	:"cx","di");
+return s;
+}
+
+#endif
diff --git a/include/sys/stat.h b/include/sys/stat.h
new file mode 100644
index 0000000..41c3840
--- /dev/null
+++ b/include/sys/stat.h
@@ -0,0 +1,58 @@
+#ifndef _SYS_STAT_H
+#define _SYS_STAT_H
+
+#include <sys/types.h>
+
+struct stat {
+	dev_t	st_dev;
+	ino_t	st_ino;
+	umode_t	st_mode;
+	nlink_t	st_nlink;
+	uid_t	st_uid;
+	gid_t	st_gid;
+	dev_t	st_rdev;
+	off_t	st_size;
+	time_t	st_atime;
+	time_t	st_mtime;
+	time_t	st_ctime;
+};
+
+#define S_IFMT  00170000
+#define S_IFREG  0100000
+#define S_IFBLK  0060000
+#define S_IFDIR  0040000
+#define S_IFCHR  0020000
+#define S_IFIFO  0010000
+#define S_ISUID  0004000
+#define S_ISGID  0002000
+#define S_ISVTX  0001000
+
+#define S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)
+#define S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
+#define S_ISCHR(m)	(((m) & S_IFMT) == S_IFCHR)
+#define S_ISBLK(m)	(((m) & S_IFMT) == S_IFBLK)
+#define S_ISFIFO(m)	(((m) & S_IFMT) == S_IFIFO)
+
+#define S_IRWXU 00700
+#define S_IRUSR 00400
+#define S_IWUSR 00200
+#define S_IXUSR 00100
+
+#define S_IRWXG 00070
+#define S_IRGRP 00040
+#define S_IWGRP 00020
+#define S_IXGRP 00010
+
+#define S_IRWXO 00007
+#define S_IROTH 00004
+#define S_IWOTH 00002
+#define S_IXOTH 00001
+
+extern int chmod(const char *_path, mode_t mode);
+extern int fstat(int fildes, struct stat *stat_buf);
+extern int mkdir(const char *_path, mode_t mode);
+extern int mkfifo(const char *_path, mode_t mode);
+extern int stat(const char *filename, struct stat *stat_buf);
+extern mode_t umask(mode_t mask);
+
+#endif
diff --git a/include/sys/times.h b/include/sys/times.h
new file mode 100644
index 0000000..68d5bfb
--- /dev/null
+++ b/include/sys/times.h
@@ -0,0 +1,15 @@
+#ifndef _TIMES_H
+#define _TIMES_H
+
+#include <sys/types.h>
+
+struct tms {
+	time_t tms_utime;
+	time_t tms_stime;
+	time_t tms_cutime;
+	time_t tms_cstime;
+};
+
+extern time_t times(struct tms * tp);
+
+#endif
diff --git a/include/sys/types.h b/include/sys/types.h
new file mode 100644
index 0000000..557aa31
--- /dev/null
+++ b/include/sys/types.h
@@ -0,0 +1,46 @@
+#ifndef _SYS_TYPES_H
+#define _SYS_TYPES_H
+
+#ifndef _SIZE_T
+#define _SIZE_T
+typedef unsigned int size_t;
+#endif
+
+#ifndef _TIME_T
+#define _TIME_T
+typedef long time_t;
+#endif
+
+#ifndef _PTRDIFF_T
+#define _PTRDIFF_T
+typedef long ptrdiff_t;
+#endif
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+typedef int pid_t;
+typedef unsigned short uid_t;
+typedef unsigned char gid_t;
+typedef unsigned short dev_t;
+typedef unsigned short ino_t;
+typedef unsigned short mode_t;
+typedef unsigned short umode_t;
+typedef unsigned char nlink_t;
+typedef int daddr_t;
+typedef long off_t;
+typedef unsigned char u_char;
+typedef unsigned short ushort;
+
+typedef struct { int quot,rem; } div_t;
+typedef struct { long quot,rem; } ldiv_t;
+
+struct ustat {
+	daddr_t f_tfree;
+	ino_t f_tinode;
+	char f_fname[6];
+	char f_fpack[6];
+};
+
+#endif
diff --git a/include/sys/utsname.h b/include/sys/utsname.h
new file mode 100644
index 0000000..0a1c5a0
--- /dev/null
+++ b/include/sys/utsname.h
@@ -0,0 +1,16 @@
+#ifndef _SYS_UTSNAME_H
+#define _SYS_UTSNAME_H
+
+#include <sys/types.h>
+
+struct utsname {
+	char sysname[9];
+	char nodename[9];
+	char release[9];
+	char version[9];
+	char machine[9];
+};
+
+extern int uname(struct utsname * utsbuf);
+
+#endif
diff --git a/include/sys/wait.h b/include/sys/wait.h
new file mode 100644
index 0000000..53190c2
--- /dev/null
+++ b/include/sys/wait.h
@@ -0,0 +1,23 @@
+#ifndef _SYS_WAIT_H
+#define _SYS_WAIT_H
+
+#include <sys/types.h>
+
+#define _LOW(v)		( (v) & 0377)
+#define _HIGH(v)	( ((v) >> 8) & 0377)
+
+/* options for waitpid, WUNTRACED not supported */
+#define WNOHANG		1
+#define WUNTRACED	2
+
+#define WIFEXITED(s)	(!((s)&0xFF)
+#define WIFSTOPPED(s)	(((s)&0xFF)==0x7F)
+#define WEXITSTATUS(s)	(((s)>>8)&0xFF)
+#define WTERMSIG(s)	((s)&0x7F)
+#define WSTOPSIG(s)	(((s)>>8)&0xFF)
+#define WIFSIGNALED(s)	(((unsigned int)(s)-1 & 0xFFFF) < 0xFF)
+
+pid_t wait(int *stat_loc);
+pid_t waitpid(pid_t pid, int *stat_loc, int options);
+
+#endif
diff --git a/include/termios.h b/include/termios.h
new file mode 100644
index 0000000..f707674
--- /dev/null
+++ b/include/termios.h
@@ -0,0 +1,222 @@
+#ifndef _TERMIOS_H
+#define _TERMIOS_H
+
+#define TTY_BUF_SIZE 1024
+
+/* 0x54 is just a magic number to make these relatively uniqe ('T') */
+
+#define TCGETS		0x5401
+#define TCSETS		0x5402
+#define TCSETSW		0x5403
+#define TCSETSF		0x5404
+#define TCGETA		0x5405
+#define TCSETA		0x5406
+#define TCSETAW		0x5407
+#define TCSETAF		0x5408
+#define TCSBRK		0x5409
+#define TCXONC		0x540A
+#define TCFLSH		0x540B
+#define TIOCEXCL	0x540C
+#define TIOCNXCL	0x540D
+#define TIOCSCTTY	0x540E
+#define TIOCGPGRP	0x540F
+#define TIOCSPGRP	0x5410
+#define TIOCOUTQ	0x5411
+#define TIOCSTI		0x5412
+#define TIOCGWINSZ	0x5413
+#define TIOCSWINSZ	0x5414
+#define TIOCMGET	0x5415
+#define TIOCMBIS	0x5416
+#define TIOCMBIC	0x5417
+#define TIOCMSET	0x5418
+#define TIOCGSOFTCAR	0x5419
+#define TIOCSSOFTCAR	0x541A
+
+struct winsize {
+	unsigned short ws_row;
+	unsigned short ws_col;
+	unsigned short ws_xpixel;
+	unsigned short ws_ypixel;
+};
+
+#define NCC 8
+struct termio {
+	unsigned short c_iflag;		/* input mode flags */
+	unsigned short c_oflag;		/* output mode flags */
+	unsigned short c_cflag;		/* control mode flags */
+	unsigned short c_lflag;		/* local mode flags */
+	unsigned char c_line;		/* line discipline */
+	unsigned char c_cc[NCC];	/* control characters */
+};
+
+#define NCCS 17
+struct termios {
+	unsigned long c_iflag;		/* input mode flags */
+	unsigned long c_oflag;		/* output mode flags */
+	unsigned long c_cflag;		/* control mode flags */
+	unsigned long c_lflag;		/* local mode flags */
+	unsigned char c_line;		/* line discipline */
+	unsigned char c_cc[NCCS];	/* control characters */
+};
+
+/* c_cc characters */
+#define VINTR 0
+#define VQUIT 1
+#define VERASE 2
+#define VKILL 3
+#define VEOF 4
+#define VTIME 5
+#define VMIN 6
+#define VSWTC 7
+#define VSTART 8
+#define VSTOP 9
+#define VSUSP 10
+#define VEOL 11
+#define VREPRINT 12
+#define VDISCARD 13
+#define VWERASE 14
+#define VLNEXT 15
+#define VEOL2 16
+
+/* c_iflag bits */
+#define IGNBRK	0000001
+#define BRKINT	0000002
+#define IGNPAR	0000004
+#define PARMRK	0000010
+#define INPCK	0000020
+#define ISTRIP	0000040
+#define INLCR	0000100
+#define IGNCR	0000200
+#define ICRNL	0000400
+#define IUCLC	0001000
+#define IXON	0002000
+#define IXANY	0004000
+#define IXOFF	0010000
+#define IMAXBEL	0020000
+
+/* c_oflag bits */
+#define OPOST	0000001
+#define OLCUC	0000002
+#define ONLCR	0000004
+#define OCRNL	0000010
+#define ONOCR	0000020
+#define ONLRET	0000040
+#define OFILL	0000100
+#define OFDEL	0000200
+#define NLDLY	0000400
+#define   NL0	0000000
+#define   NL1	0000400
+#define CRDLY	0003000
+#define   CR0	0000000
+#define   CR1	0001000
+#define   CR2	0002000
+#define   CR3	0003000
+#define TABDLY	0014000
+#define   TAB0	0000000
+#define   TAB1	0004000
+#define   TAB2	0010000
+#define   TAB3	0014000
+#define   XTABS	0014000
+#define BSDLY	0020000
+#define   BS0	0000000
+#define   BS1	0020000
+#define VTDLY	0040000
+#define   VT0	0000000
+#define   VT1	0040000
+#define FFDLY	0040000
+#define   FF0	0000000
+#define   FF1	0040000
+
+/* c_cflag bit meaning */
+#define CBAUD	0000017
+#define  B0	0000000		/* hang up */
+#define  B50	0000001
+#define  B75	0000002
+#define  B110	0000003
+#define  B134	0000004
+#define  B150	0000005
+#define  B200	0000006
+#define  B300	0000007
+#define  B600	0000010
+#define  B1200	0000011
+#define  B1800	0000012
+#define  B2400	0000013
+#define  B4800	0000014
+#define  B9600	0000015
+#define  B19200	0000016
+#define  B38400	0000017
+#define CSIZE	0000060
+#define   CS5	0000000
+#define   CS6	0000020
+#define   CS7	0000040
+#define   CS8	0000060
+#define CSTOPB	0000100
+#define CREAD	0000200
+#define CPARENB	0000400
+#define CPARODD	0001000
+#define HUPCL	0002000
+#define CLOCAL	0004000
+#define CIBAUD	03600000		/* input baud rate (not used) */
+#define CRTSCTS	020000000000		/* flow control */
+
+/* c_lflag bits */
+#define ISIG	0000001
+#define ICANON	0000002
+#define XCASE	0000004
+#define ECHO	0000010
+#define ECHOE	0000020
+#define ECHOK	0000040
+#define ECHONL	0000100
+#define NOFLSH	0000200
+#define TOSTOP	0000400
+#define ECHOCTL	0001000
+#define ECHOPRT	0002000
+#define ECHOKE	0004000
+#define FLUSHO	0010000
+#define PENDIN	0040000
+#define IEXTEN	0100000
+
+/* modem lines */
+#define TIOCM_LE	0x001
+#define TIOCM_DTR	0x002
+#define TIOCM_RTS	0x004
+#define TIOCM_ST	0x008
+#define TIOCM_SR	0x010
+#define TIOCM_CTS	0x020
+#define TIOCM_CAR	0x040
+#define TIOCM_RNG	0x080
+#define TIOCM_DSR	0x100
+#define TIOCM_CD	TIOCM_CAR
+#define TIOCM_RI	TIOCM_RNG
+
+/* tcflow() and TCXONC use these */
+#define	TCOOFF		0
+#define	TCOON		1
+#define	TCIOFF		2
+#define	TCION		3
+
+/* tcflush() and TCFLSH use these */
+#define	TCIFLUSH	0
+#define	TCOFLUSH	1
+#define	TCIOFLUSH	2
+
+/* tcsetattr uses these */
+#define	TCSANOW		0
+#define	TCSADRAIN	1
+#define	TCSAFLUSH	2
+
+typedef int speed_t;
+
+extern speed_t cfgetispeed(struct termios *termios_p);
+extern speed_t cfgetospeed(struct termios *termios_p);
+extern int cfsetispeed(struct termios *termios_p, speed_t speed);
+extern int cfsetospeed(struct termios *termios_p, speed_t speed);
+extern int tcdrain(int fildes);
+extern int tcflow(int fildes, int action);
+extern int tcflush(int fildes, int queue_selector);
+extern int tcgetattr(int fildes, struct termios *termios_p);
+extern int tcsendbreak(int fildes, int duration);
+extern int tcsetattr(int fildes, int optional_actions,
+	struct termios *termios_p);
+
+#endif
diff --git a/include/time.h b/include/time.h
new file mode 100644
index 0000000..d0a765d
--- /dev/null
+++ b/include/time.h
@@ -0,0 +1,42 @@
+#ifndef _TIME_H
+#define _TIME_H
+
+#ifndef _TIME_T
+#define _TIME_T
+typedef long time_t;
+#endif
+
+#ifndef _SIZE_T
+#define _SIZE_T
+typedef unsigned int size_t;
+#endif
+
+#define CLOCKS_PER_SEC 100
+
+typedef long clock_t;
+
+struct tm {
+	int tm_sec;
+	int tm_min;
+	int tm_hour;
+	int tm_mday;
+	int tm_mon;
+	int tm_year;
+	int tm_wday;
+	int tm_yday;
+	int tm_isdst;
+};
+
+clock_t clock(void);
+time_t time(time_t * tp);
+double difftime(time_t time2, time_t time1);
+time_t mktime(struct tm * tp);
+
+char * asctime(const struct tm * tp);
+char * ctime(const time_t * tp);
+struct tm * gmtime(const time_t *tp);
+struct tm *localtime(const time_t * tp);
+size_t strftime(char * s, size_t smax, const char * fmt, const struct tm * tp);
+void tzset(void);
+
+#endif
diff --git a/include/unistd.h b/include/unistd.h
new file mode 100644
index 0000000..eb10771
--- /dev/null
+++ b/include/unistd.h
@@ -0,0 +1,247 @@
+#ifndef _UNISTD_H
+#define _UNISTD_H
+
+/* ok, this may be a joke, but I'm working on it */
+#define _POSIX_VERSION 198808L
+
+#define _POSIX_CHOWN_RESTRICTED	/* only root can do a chown (I think..) */
+/* #define _POSIX_NO_TRUNC*/	/* pathname truncation (but see in kernel) */
+#define _POSIX_VDISABLE '\0'	/* character to disable things like ^C */
+/*#define _POSIX_SAVED_IDS */	/* we'll get to this yet */
+/*#define _POSIX_JOB_CONTROL */	/* we aren't there quite yet. Soon hopefully */
+
+#define STDIN_FILENO	0
+#define STDOUT_FILENO	1
+#define STDERR_FILENO	2
+
+#ifndef NULL
+#define NULL    ((void *)0)
+#endif
+
+/* access */
+#define F_OK	0
+#define X_OK	1
+#define W_OK	2
+#define R_OK	4
+
+/* lseek */
+#define SEEK_SET	0
+#define SEEK_CUR	1
+#define SEEK_END	2
+
+/* _SC stands for System Configuration. We don't use them much */
+#define _SC_ARG_MAX		1
+#define _SC_CHILD_MAX		2
+#define _SC_CLOCKS_PER_SEC	3
+#define _SC_NGROUPS_MAX		4
+#define _SC_OPEN_MAX		5
+#define _SC_JOB_CONTROL		6
+#define _SC_SAVED_IDS		7
+#define _SC_VERSION		8
+
+/* more (possibly) configurable things - now pathnames */
+#define _PC_LINK_MAX		1
+#define _PC_MAX_CANON		2
+#define _PC_MAX_INPUT		3
+#define _PC_NAME_MAX		4
+#define _PC_PATH_MAX		5
+#define _PC_PIPE_BUF		6
+#define _PC_NO_TRUNC		7
+#define _PC_VDISABLE		8
+#define _PC_CHOWN_RESTRICTED	9
+
+#include <sys/stat.h>
+#include <sys/times.h>
+#include <sys/utsname.h>
+#include <utime.h>
+
+#ifdef __LIBRARY__
+
+#define __NR_setup	0	/* used only by init, to get system going */
+#define __NR_exit	1
+#define __NR_fork	2
+#define __NR_read	3
+#define __NR_write	4
+#define __NR_open	5
+#define __NR_close	6
+#define __NR_waitpid	7
+#define __NR_creat	8
+#define __NR_link	9
+#define __NR_unlink	10
+#define __NR_execve	11
+#define __NR_chdir	12
+#define __NR_time	13
+#define __NR_mknod	14
+#define __NR_chmod	15
+#define __NR_chown	16
+#define __NR_break	17
+#define __NR_stat	18
+#define __NR_lseek	19
+#define __NR_getpid	20
+#define __NR_mount	21
+#define __NR_umount	22
+#define __NR_setuid	23
+#define __NR_getuid	24
+#define __NR_stime	25
+#define __NR_ptrace	26
+#define __NR_alarm	27
+#define __NR_fstat	28
+#define __NR_pause	29
+#define __NR_utime	30
+#define __NR_stty	31
+#define __NR_gtty	32
+#define __NR_access	33
+#define __NR_nice	34
+#define __NR_ftime	35
+#define __NR_sync	36
+#define __NR_kill	37
+#define __NR_rename	38
+#define __NR_mkdir	39
+#define __NR_rmdir	40
+#define __NR_dup	41
+#define __NR_pipe	42
+#define __NR_times	43
+#define __NR_prof	44
+#define __NR_brk	45
+#define __NR_setgid	46
+#define __NR_getgid	47
+#define __NR_signal	48
+#define __NR_geteuid	49
+#define __NR_getegid	50
+#define __NR_acct	51
+#define __NR_phys	52
+#define __NR_lock	53
+#define __NR_ioctl	54
+#define __NR_fcntl	55
+#define __NR_mpx	56
+#define __NR_setpgid	57
+#define __NR_ulimit	58
+#define __NR_uname	59
+#define __NR_umask	60
+#define __NR_chroot	61
+#define __NR_ustat	62
+#define __NR_dup2	63
+#define __NR_getppid	64
+#define __NR_getpgrp	65
+#define __NR_setsid	66
+
+#define _syscall0(type,name) \
+type name(void) \
+{ \
+type __res; \
+__asm__ volatile ("int $0x80" \
+	: "=a" (__res) \
+	: "0" (__NR_##name)); \
+if (__res >= 0) \
+	return __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall1(type,name,atype,a) \
+type name(atype a) \
+{ \
+type __res; \
+__asm__ volatile ("int $0x80" \
+	: "=a" (__res) \
+	: "0" (__NR_##name),"b" (a)); \
+if (__res >= 0) \
+	return __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall2(type,name,atype,a,btype,b) \
+type name(atype a,btype b) \
+{ \
+type __res; \
+__asm__ volatile ("int $0x80" \
+	: "=a" (__res) \
+	: "0" (__NR_##name),"b" (a),"c" (b)); \
+if (__res >= 0) \
+	return __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
+type name(atype a,btype b,ctype c) \
+{ \
+type __res; \
+__asm__ volatile ("int $0x80" \
+	: "=a" (__res) \
+	: "0" (__NR_##name),"b" (a),"c" (b),"d" (c)); \
+if (__res<0) \
+	errno=-__res , __res = -1; \
+return __res;\
+}
+
+#endif /* __LIBRARY__ */
+
+extern int errno;
+
+int access(const char * filename, mode_t mode);
+int acct(const char * filename);
+int alarm(int sec);
+int brk(void * end_data_segment);
+void * sbrk(ptrdiff_t increment);
+int chdir(const char * filename);
+int chmod(const char * filename, mode_t mode);
+int chown(const char * filename, uid_t owner, gid_t group);
+int chroot(const char * filename);
+int close(int fildes);
+int creat(const char * filename, mode_t mode);
+int dup(int fildes);
+int execve(const char * filename, char ** argv, char ** envp);
+int execv(const char * pathname, char ** argv);
+int execvp(const char * file, char ** argv);
+int execl(const char * pathname, char * arg0, ...);
+int execlp(const char * file, char * arg0, ...);
+int execle(const char * pathname, char * arg0, ...);
+volatile void exit(int status);
+volatile void _exit(int status);
+int fcntl(int fildes, int cmd, ...);
+int fork(void);
+int getpid(void);
+int getuid(void);
+int geteuid(void);
+int getgid(void);
+int getegid(void);
+int ioctl(int fildes, int cmd, ...);
+int kill(pid_t pid, int signal);
+int link(const char * filename1, const char * filename2);
+int lseek(int fildes, off_t offset, int origin);
+int mknod(const char * filename, mode_t mode, dev_t dev);
+int mount(const char * specialfile, const char * dir, int rwflag);
+int nice(int val);
+int open(const char * filename, int flag, ...);
+int pause(void);
+int pipe(int * fildes);
+int read(int fildes, char * buf, off_t count);
+int setpgrp(void);
+int setpgid(pid_t pid,pid_t pgid);
+int setuid(uid_t uid);
+int setgid(gid_t gid);
+void (*signal(int sig, void (*fn)(int)))(int);
+int stat(const char * filename, struct stat * stat_buf);
+int fstat(int fildes, struct stat * stat_buf);
+int stime(time_t * tptr);
+int sync(void);
+time_t time(time_t * tloc);
+time_t times(struct tms * tbuf);
+int ulimit(int cmd, long limit);
+mode_t umask(mode_t mask);
+int umount(const char * specialfile);
+int uname(struct utsname * name);
+int unlink(const char * filename);
+int ustat(dev_t dev, struct ustat * ubuf);
+int utime(const char * filename, struct utimbuf * times);
+pid_t waitpid(pid_t pid,int * wait_stat,int options);
+pid_t wait(int * wait_stat);
+int write(int fildes, const char * buf, off_t count);
+int dup2(int oldfd, int newfd);
+int getppid(void);
+pid_t getpgrp(void);
+pid_t setsid(void);
+
+#endif
diff --git a/include/utime.h b/include/utime.h
new file mode 100644
index 0000000..83f07c7
--- /dev/null
+++ b/include/utime.h
@@ -0,0 +1,13 @@
+#ifndef _UTIME_H
+#define _UTIME_H
+
+#include <sys/types.h>	/* I know - shouldn't do this, but .. */
+
+struct utimbuf {
+	time_t actime;
+	time_t modtime;
+};
+
+extern int utime(const char *filename, struct utimbuf *times);
+
+#endif
diff --git a/init/main.c b/init/main.c
new file mode 100644
index 0000000..1192119
--- /dev/null
+++ b/init/main.c
@@ -0,0 +1,147 @@
+#define __LIBRARY__
+#include <unistd.h>
+#include <time.h>
+
+/*
+ * we need this inline - forking from kernel space will result
+ * in NO COPY ON WRITE (!!!), until an execve is executed. This
+ * is no problem, but for the stack. This is handled by not letting
+ * main() use the stack at all after fork(). Thus, no function
+ * calls - which means inline code for fork too, as otherwise we
+ * would use the stack upon exit from 'fork()'.
+ *
+ * Actually only pause and fork are needed inline, so that there
+ * won't be any messing with the stack from main(), but we define
+ * some others too.
+ */
+static inline _syscall0(int,fork)
+static inline _syscall0(int,pause)
+static inline _syscall0(int,setup)
+static inline _syscall0(int,sync)
+
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include <linux/fs.h>
+
+static char printbuf[1024];
+
+extern int vsprintf();
+extern void init(void);
+extern void hd_init(void);
+extern long kernel_mktime(struct tm * tm);
+extern long startup_time;
+
+/*
+ * Yeah, yeah, it's ugly, but I cannot find how to do this correctly
+ * and this seems to work. I anybody has more info on the real-time
+ * clock I'd be interested. Most of this was trial and error, and some
+ * bios-listing reading. Urghh.
+ */
+
+#define CMOS_READ(addr) ({ \
+outb_p(0x80|addr,0x70); \
+inb_p(0x71); \
+})
+
+#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
+
+static void time_init(void)
+{
+	struct tm time;
+
+	do {
+		time.tm_sec = CMOS_READ(0);
+		time.tm_min = CMOS_READ(2);
+		time.tm_hour = CMOS_READ(4);
+		time.tm_mday = CMOS_READ(7);
+		time.tm_mon = CMOS_READ(8)-1;
+		time.tm_year = CMOS_READ(9);
+	} while (time.tm_sec != CMOS_READ(0));
+	BCD_TO_BIN(time.tm_sec);
+	BCD_TO_BIN(time.tm_min);
+	BCD_TO_BIN(time.tm_hour);
+	BCD_TO_BIN(time.tm_mday);
+	BCD_TO_BIN(time.tm_mon);
+	BCD_TO_BIN(time.tm_year);
+	startup_time = kernel_mktime(&time);
+}
+
+void main(void)		/* This really IS void, no error here. */
+{			/* The startup routine assumes (well, ...) this */
+/*
+ * Interrupts are still disabled. Do necessary setups, then
+ * enable them
+ */
+	time_init();
+	tty_init();
+	trap_init();
+	sched_init();
+	buffer_init();
+	hd_init();
+	sti();
+	move_to_user_mode();
+	if (!fork()) {		/* we count on this going ok */
+		init();
+	}
+/*
+ *   NOTE!!   For any other task 'pause()' would mean we have to get a
+ * signal to awaken, but task0 is the sole exception (see 'schedule()')
+ * as task 0 gets activated at every idle moment (when no other tasks
+ * can run). For task0 'pause()' just means we go check if some other
+ * task can run, and if not we return here.
+ */
+	for(;;) pause();
+}
+
+static int printf(const char *fmt, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, fmt);
+	write(1,printbuf,i=vsprintf(printbuf, fmt, args));
+	va_end(args);
+	return i;
+}
+
+static char * argv[] = { "-",NULL };
+static char * envp[] = { "HOME=/usr/root", NULL };
+
+void init(void)
+{
+	int i,j;
+
+	setup();
+	if (!fork())
+		_exit(execve("/bin/update",NULL,NULL));
+	(void) open("/dev/tty0",O_RDWR,0);
+	(void) dup(0);
+	(void) dup(0);
+	printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
+		NR_BUFFERS*BLOCK_SIZE);
+	printf(" Ok.\n\r");
+	if ((i=fork())<0)
+		printf("Fork failed in init\r\n");
+	else if (!i) {
+		close(0);close(1);close(2);
+		setsid();
+		(void) open("/dev/tty0",O_RDWR,0);
+		(void) dup(0);
+		(void) dup(0);
+		_exit(execve("/bin/sh",argv,envp));
+	}
+	j=wait(&i);
+	printf("child %d died with code %04x\n",j,i);
+	sync();
+	_exit(0);	/* NOTE! _exit, not exit() */
+}
diff --git a/kernel/Makefile b/kernel/Makefile
new file mode 100644
index 0000000..23fe7dd
--- /dev/null
+++ b/kernel/Makefile
@@ -0,0 +1,90 @@
+#
+# Makefile for the FREAX-kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+
+AR	=gar
+AS	=gas
+LD	=gld
+LDFLAGS	=-s -x
+CC	=gcc
+CFLAGS	=-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs \
+	-finline-functions -mstring-insns -nostdinc -I../include
+CPP	=gcc -E -nostdinc -I../include
+
+.c.s:
+	$(CC) $(CFLAGS) \
+	-S -o $*.s $<
+.s.o:
+	$(AS) -c -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) \
+	-c -o $*.o $<
+
+OBJS  = sched.o system_call.o traps.o asm.o fork.o \
+	panic.o printk.o vsprintf.o tty_io.o console.o \
+	keyboard.o rs_io.o hd.o sys.o exit.o serial.o \
+	mktime.o
+
+kernel.o: $(OBJS)
+	$(LD) -r -o kernel.o $(OBJS)
+	sync
+
+clean:
+	rm -f core *.o *.a tmp_make
+	for i in *.c;do rm -f `basename $$i .c`.s;done
+
+dep:
+	sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
+	(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
+		$(CPP) -M $$i;done) >> tmp_make
+	cp tmp_make Makefile
+
+### Dependencies:
+console.s console.o : console.c ../include/linux/sched.h ../include/linux/head.h \
+  ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
+  ../include/linux/tty.h ../include/termios.h ../include/asm/io.h \
+  ../include/asm/system.h 
+exit.s exit.o : exit.c ../include/errno.h ../include/signal.h \
+  ../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
+  ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \
+  ../include/asm/segment.h 
+fork.s fork.o : fork.c ../include/errno.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+  ../include/linux/mm.h ../include/linux/kernel.h ../include/asm/segment.h \
+  ../include/asm/system.h 
+hd.s hd.o : hd.c ../include/linux/config.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+  ../include/linux/mm.h ../include/linux/kernel.h ../include/linux/hdreg.h \
+  ../include/asm/system.h ../include/asm/io.h ../include/asm/segment.h 
+mktime.s mktime.o : mktime.c ../include/time.h 
+panic.s panic.o : panic.c ../include/linux/kernel.h 
+printk.s printk.o : printk.c ../include/stdarg.h ../include/stddef.h \
+  ../include/linux/kernel.h 
+sched.s sched.o : sched.c ../include/linux/sched.h ../include/linux/head.h \
+  ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
+  ../include/linux/kernel.h ../include/signal.h ../include/linux/sys.h \
+  ../include/asm/system.h ../include/asm/io.h ../include/asm/segment.h 
+serial.s serial.o : serial.c ../include/linux/tty.h ../include/termios.h \
+  ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
+  ../include/sys/types.h ../include/linux/mm.h ../include/asm/system.h \
+  ../include/asm/io.h 
+sys.s sys.o : sys.c ../include/errno.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+  ../include/linux/mm.h ../include/linux/tty.h ../include/termios.h \
+  ../include/linux/kernel.h ../include/asm/segment.h ../include/sys/times.h \
+  ../include/sys/utsname.h 
+traps.s traps.o : traps.c ../include/string.h ../include/linux/head.h \
+  ../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \
+  ../include/linux/mm.h ../include/linux/kernel.h ../include/asm/system.h \
+  ../include/asm/segment.h 
+tty_io.s tty_io.o : tty_io.c ../include/ctype.h ../include/errno.h \
+  ../include/signal.h ../include/sys/types.h ../include/linux/sched.h \
+  ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
+  ../include/linux/tty.h ../include/termios.h ../include/asm/segment.h \
+  ../include/asm/system.h 
+vsprintf.s vsprintf.o : vsprintf.c ../include/stdarg.h ../include/string.h 
diff --git a/kernel/asm.s b/kernel/asm.s
new file mode 100644
index 0000000..6fe1981
--- /dev/null
+++ b/kernel/asm.s
@@ -0,0 +1,157 @@
+/*
+ * asm.s contains the low-level code for most hardware faults.
+ * page_exception is handled by the mm, so that isn't here. This
+ * file also handles (hopefully) fpu-exceptions due to TS-bit, as
+ * the fpu must be properly saved/resored. This hasn't been tested.
+ */
+
+.globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op
+.globl _device_not_available,_double_fault,_coprocessor_segment_overrun
+.globl _invalid_TSS,_segment_not_present,_stack_segment
+.globl _general_protection,_coprocessor_error,_reserved
+
+_divide_error:
+	pushl $_do_divide_error
+no_error_code:
+	xchgl %eax,(%esp)
+	pushl %ebx
+	pushl %ecx
+	pushl %edx
+	pushl %edi
+	pushl %esi
+	pushl %ebp
+	push %ds
+	push %es
+	push %fs
+	pushl $0		# "error code"
+	lea 44(%esp),%edx
+	pushl %edx
+	movl $0x10,%edx
+	mov %dx,%ds
+	mov %dx,%es
+	mov %dx,%fs
+	call *%eax
+	addl $8,%esp
+	pop %fs
+	pop %es
+	pop %ds
+	popl %ebp
+	popl %esi
+	popl %edi
+	popl %edx
+	popl %ecx
+	popl %ebx
+	popl %eax
+	iret
+
+_debug:
+	pushl $_do_int3		# _do_debug
+	jmp no_error_code
+
+_nmi:
+	pushl $_do_nmi
+	jmp no_error_code
+
+_int3:
+	pushl $_do_int3
+	jmp no_error_code
+
+_overflow:
+	pushl $_do_overflow
+	jmp no_error_code
+
+_bounds:
+	pushl $_do_bounds
+	jmp no_error_code
+
+_invalid_op:
+	pushl $_do_invalid_op
+	jmp no_error_code
+
+math_emulate:
+	popl %eax
+	pushl $_do_device_not_available
+	jmp no_error_code
+_device_not_available:
+	pushl %eax
+	movl %cr0,%eax
+	bt $2,%eax			# EM (math emulation bit)
+	jc math_emulate
+	clts				# clear TS so that we can use math
+	movl _current,%eax
+	cmpl _last_task_used_math,%eax
+	je 1f				# shouldn't happen really ...
+	pushl %ecx
+	pushl %edx
+	push %ds
+	movl $0x10,%eax
+	mov %ax,%ds
+	call _math_state_restore
+	pop %ds
+	popl %edx
+	popl %ecx
+1:	popl %eax
+	iret
+
+_coprocessor_segment_overrun:
+	pushl $_do_coprocessor_segment_overrun
+	jmp no_error_code
+
+_reserved:
+	pushl $_do_reserved
+	jmp no_error_code
+
+_coprocessor_error:
+	pushl $_do_coprocessor_error
+	jmp no_error_code
+
+_double_fault:
+	pushl $_do_double_fault
+error_code:
+	xchgl %eax,4(%esp)		# error code <-> %eax
+	xchgl %ebx,(%esp)		# &function <-> %ebx
+	pushl %ecx
+	pushl %edx
+	pushl %edi
+	pushl %esi
+	pushl %ebp
+	push %ds
+	push %es
+	push %fs
+	pushl %eax			# error code
+	lea 44(%esp),%eax		# offset
+	pushl %eax
+	movl $0x10,%eax
+	mov %ax,%ds
+	mov %ax,%es
+	mov %ax,%fs
+	call *%ebx
+	addl $8,%esp
+	pop %fs
+	pop %es
+	pop %ds
+	popl %ebp
+	popl %esi
+	popl %edi
+	popl %edx
+	popl %ecx
+	popl %ebx
+	popl %eax
+	iret
+
+_invalid_TSS:
+	pushl $_do_invalid_TSS
+	jmp error_code
+
+_segment_not_present:
+	pushl $_do_segment_not_present
+	jmp error_code
+
+_stack_segment:
+	pushl $_do_stack_segment
+	jmp error_code
+
+_general_protection:
+	pushl $_do_general_protection
+	jmp error_code
+
diff --git a/kernel/console.c b/kernel/console.c
new file mode 100644
index 0000000..9e00b31
--- /dev/null
+++ b/kernel/console.c
@@ -0,0 +1,550 @@
+/*
+ *	console.c
+ *
+ * This module implements the console io functions
+ *	'void con_init(void)'
+ *	'void con_write(struct tty_queue * queue)'
+ * Hopefully this will be a rather complete VT102 implementation.
+ *
+ */
+
+/*
+ *  NOTE!!! We sometimes disable and enable interrupts for a short while
+ * (to put a word in video IO), but this will work even for keyboard
+ * interrupts. We know interrupts aren't enabled when getting a keyboard
+ * interrupt, as we use trap-gates. Hopefully all is well.
+ */
+
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#define SCREEN_START 0xb8000
+#define SCREEN_END   0xc0000
+#define LINES 25
+#define COLUMNS 80
+#define NPAR 16
+
+extern void keyboard_interrupt(void);
+
+static unsigned long origin=SCREEN_START;
+static unsigned long scr_end=SCREEN_START+LINES*COLUMNS*2;
+static unsigned long pos;
+static unsigned long x,y;
+static unsigned long top=0,bottom=LINES;
+static unsigned long lines=LINES,columns=COLUMNS;
+static unsigned long state=0;
+static unsigned long npar,par[NPAR];
+static unsigned long ques=0;
+static unsigned char attr=0x07;
+
+/*
+ * this is what the terminal answers to a ESC-Z or csi0c
+ * query (= vt100 response).
+ */
+#define RESPONSE "\033[?1;2c"
+
+static inline void gotoxy(unsigned int new_x,unsigned int new_y)
+{
+	if (new_x>=columns || new_y>=lines)
+		return;
+	x=new_x;
+	y=new_y;
+	pos=origin+((y*columns+x)<<1);
+}
+
+static inline void set_origin(void)
+{
+	cli();
+	outb_p(12,0x3d4);
+	outb_p(0xff&((origin-SCREEN_START)>>9),0x3d5);
+	outb_p(13,0x3d4);
+	outb_p(0xff&((origin-SCREEN_START)>>1),0x3d5);
+	sti();
+}
+
+static void scrup(void)
+{
+	if (!top && bottom==lines) {
+		origin += columns<<1;
+		pos += columns<<1;
+		scr_end += columns<<1;
+		if (scr_end>SCREEN_END) {
+			__asm__("cld\n\t"
+				"rep\n\t"
+				"movsl\n\t"
+				"movl _columns,%1\n\t"
+				"rep\n\t"
+				"stosw"
+				::"a" (0x0720),
+				"c" ((lines-1)*columns>>1),
+				"D" (SCREEN_START),
+				"S" (origin)
+				:"cx","di","si");
+			scr_end -= origin-SCREEN_START;
+			pos -= origin-SCREEN_START;
+			origin = SCREEN_START;
+		} else {
+			__asm__("cld\n\t"
+				"rep\n\t"
+				"stosl"
+				::"a" (0x07200720),
+				"c" (columns>>1),
+				"D" (scr_end-(columns<<1))
+				:"cx","di");
+		}
+		set_origin();
+	} else {
+		__asm__("cld\n\t"
+			"rep\n\t"
+			"movsl\n\t"
+			"movl _columns,%%ecx\n\t"
+			"rep\n\t"
+			"stosw"
+			::"a" (0x0720),
+			"c" ((bottom-top-1)*columns>>1),
+			"D" (origin+(columns<<1)*top),
+			"S" (origin+(columns<<1)*(top+1))
+			:"cx","di","si");
+	}
+}
+
+static void scrdown(void)
+{
+	__asm__("std\n\t"
+		"rep\n\t"
+		"movsl\n\t"
+		"addl $2,%%edi\n\t"	/* %edi has been decremented by 4 */
+		"movl _columns,%%ecx\n\t"
+		"rep\n\t"
+		"stosw"
+		::"a" (0x0720),
+		"c" ((bottom-top-1)*columns>>1),
+		"D" (origin+(columns<<1)*bottom-4),
+		"S" (origin+(columns<<1)*(bottom-1)-4)
+		:"ax","cx","di","si");
+}
+
+static void lf(void)
+{
+	if (y+1<bottom) {
+		y++;
+		pos += columns<<1;
+		return;
+	}
+	scrup();
+}
+
+static void ri(void)
+{
+	if (y>top) {
+		y--;
+		pos -= columns<<1;
+		return;
+	}
+	scrdown();
+}
+
+static void cr(void)
+{
+	pos -= x<<1;
+	x=0;
+}
+
+static void del(void)
+{
+	if (x) {
+		pos -= 2;
+		x--;
+		*(unsigned short *)pos = 0x0720;
+	}
+}
+
+static void csi_J(int par)
+{
+	long count __asm__("cx");
+	long start __asm__("di");
+
+	switch (par) {
+		case 0:	/* erase from cursor to end of display */
+			count = (scr_end-pos)>>1;
+			start = pos;
+			break;
+		case 1:	/* erase from start to cursor */
+			count = (pos-origin)>>1;
+			start = origin;
+			break;
+		case 2: /* erase whole display */
+			count = columns*lines;
+			start = origin;
+			break;
+		default:
+			return;
+	}
+	__asm__("cld\n\t"
+		"rep\n\t"
+		"stosw\n\t"
+		::"c" (count),
+		"D" (start),"a" (0x0720)
+		:"cx","di");
+}
+
+static void csi_K(int par)
+{
+	long count __asm__("cx");
+	long start __asm__("di");
+
+	switch (par) {
+		case 0:	/* erase from cursor to end of line */
+			if (x>=columns)
+				return;
+			count = columns-x;
+			start = pos;
+			break;
+		case 1:	/* erase from start of line to cursor */
+			start = pos - (x<<1);
+			count = (x<columns)?x:columns;
+			break;
+		case 2: /* erase whole line */
+			start = pos - (x<<1);
+			count = columns;
+			break;
+		default:
+			return;
+	}
+	__asm__("cld\n\t"
+		"rep\n\t"
+		"stosw\n\t"
+		::"c" (count),
+		"D" (start),"a" (0x0720)
+		:"cx","di");
+}
+
+void csi_m(void)
+{
+	int i;
+
+	for (i=0;i<=npar;i++)
+		switch (par[i]) {
+			case 0:attr=0x07;break;
+			case 1:attr=0x0f;break;
+			case 4:attr=0x0f;break;
+			case 7:attr=0x70;break;
+			case 27:attr=0x07;break;
+		}
+}
+
+static inline void set_cursor(void)
+{
+	cli();
+	outb_p(14,0x3d4);
+	outb_p(0xff&((pos-SCREEN_START)>>9),0x3d5);
+	outb_p(15,0x3d4);
+	outb_p(0xff&((pos-SCREEN_START)>>1),0x3d5);
+	sti();
+}
+
+static void respond(struct tty_struct * tty)
+{
+	char * p = RESPONSE;
+
+	cli();
+	while (*p) {
+		PUTCH(*p,tty->read_q);
+		p++;
+	}
+	sti();
+	copy_to_cooked(tty);
+}
+
+static void insert_char(void)
+{
+	int i=x;
+	unsigned short tmp,old=0x0720;
+	unsigned short * p = (unsigned short *) pos;
+
+	while (i++<columns) {
+		tmp=*p;
+		*p=old;
+		old=tmp;
+		p++;
+	}
+}
+
+static void insert_line(void)
+{
+	int oldtop,oldbottom;
+
+	oldtop=top;
+	oldbottom=bottom;
+	top=y;
+	bottom=lines;
+	scrdown();
+	top=oldtop;
+	bottom=oldbottom;
+}
+
+static void delete_char(void)
+{
+	int i;
+	unsigned short * p = (unsigned short *) pos;
+
+	if (x>=columns)
+		return;
+	i = x;
+	while (++i < columns) {
+		*p = *(p+1);
+		p++;
+	}
+	*p=0x0720;
+}
+
+static void delete_line(void)
+{
+	int oldtop,oldbottom;
+
+	oldtop=top;
+	oldbottom=bottom;
+	top=y;
+	bottom=lines;
+	scrup();
+	top=oldtop;
+	bottom=oldbottom;
+}
+
+static void csi_at(int nr)
+{
+	if (nr>columns)
+		nr=columns;
+	else if (!nr)
+		nr=1;
+	while (nr--)
+		insert_char();
+}
+
+static void csi_L(int nr)
+{
+	if (nr>lines)
+		nr=lines;
+	else if (!nr)
+		nr=1;
+	while (nr--)
+		insert_line();
+}
+
+static void csi_P(int nr)
+{
+	if (nr>columns)
+		nr=columns;
+	else if (!nr)
+		nr=1;
+	while (nr--)
+		delete_char();
+}
+
+static void csi_M(int nr)
+{
+	if (nr>lines)
+		nr=lines;
+	else if (!nr)
+		nr=1;
+	while (nr--)
+		delete_line();
+}
+
+static int saved_x=0;
+static int saved_y=0;
+
+static void save_cur(void)
+{
+	saved_x=x;
+	saved_y=y;
+}
+
+static void restore_cur(void)
+{
+	x=saved_x;
+	y=saved_y;
+	pos=origin+((y*columns+x)<<1);
+}
+
+void con_write(struct tty_struct * tty)
+{
+	int nr;
+	char c;
+
+	nr = CHARS(tty->write_q);
+	while (nr--) {
+		GETCH(tty->write_q,c);
+		switch(state) {
+			case 0:
+				if (c>31 && c<127) {
+					if (x>=columns) {
+						x -= columns;
+						pos -= columns<<1;
+						lf();
+					}
+					__asm__("movb _attr,%%ah\n\t"
+						"movw %%ax,%1\n\t"
+						::"a" (c),"m" (*(short *)pos)
+						:"ax");
+					pos += 2;
+					x++;
+				} else if (c==27)
+					state=1;
+				else if (c==10 || c==11 || c==12)
+					lf();
+				else if (c==13)
+					cr();
+				else if (c==ERASE_CHAR(tty))
+					del();
+				else if (c==8) {
+					if (x) {
+						x--;
+						pos -= 2;
+					}
+				} else if (c==9) {
+					c=8-(x&7);
+					x += c;
+					pos += c<<1;
+					if (x>columns) {
+						x -= columns;
+						pos -= columns<<1;
+						lf();
+					}
+					c=9;
+				}
+				break;
+			case 1:
+				state=0;
+				if (c=='[')
+					state=2;
+				else if (c=='E')
+					gotoxy(0,y+1);
+				else if (c=='M')
+					ri();
+				else if (c=='D')
+					lf();
+				else if (c=='Z')
+					respond(tty);
+				else if (x=='7')
+					save_cur();
+				else if (x=='8')
+					restore_cur();
+				break;
+			case 2:
+				for(npar=0;npar<NPAR;npar++)
+					par[npar]=0;
+				npar=0;
+				state=3;
+				if (ques=(c=='?'))
+					break;
+			case 3:
+				if (c==';' && npar<NPAR-1) {
+					npar++;
+					break;
+				} else if (c>='0' && c<='9') {
+					par[npar]=10*par[npar]+c-'0';
+					break;
+				} else state=4;
+			case 4:
+				state=0;
+				switch(c) {
+					case 'G': case '`':
+						if (par[0]) par[0]--;
+						gotoxy(par[0],y);
+						break;
+					case 'A':
+						if (!par[0]) par[0]++;
+						gotoxy(x,y-par[0]);
+						break;
+					case 'B': case 'e':
+						if (!par[0]) par[0]++;
+						gotoxy(x,y+par[0]);
+						break;
+					case 'C': case 'a':
+						if (!par[0]) par[0]++;
+						gotoxy(x+par[0],y);
+						break;
+					case 'D':
+						if (!par[0]) par[0]++;
+						gotoxy(x-par[0],y);
+						break;
+					case 'E':
+						if (!par[0]) par[0]++;
+						gotoxy(0,y+par[0]);
+						break;
+					case 'F':
+						if (!par[0]) par[0]++;
+						gotoxy(0,y-par[0]);
+						break;
+					case 'd':
+						if (par[0]) par[0]--;
+						gotoxy(x,par[0]);
+						break;
+					case 'H': case 'f':
+						if (par[0]) par[0]--;
+						if (par[1]) par[1]--;
+						gotoxy(par[1],par[0]);
+						break;
+					case 'J':
+						csi_J(par[0]);
+						break;
+					case 'K':
+						csi_K(par[0]);
+						break;
+					case 'L':
+						csi_L(par[0]);
+						break;
+					case 'M':
+						csi_M(par[0]);
+						break;
+					case 'P':
+						csi_P(par[0]);
+						break;
+					case '@':
+						csi_at(par[0]);
+						break;
+					case 'm':
+						csi_m();
+						break;
+					case 'r':
+						if (par[0]) par[0]--;
+						if (!par[1]) par[1]=lines;
+						if (par[0] < par[1] &&
+						    par[1] <= lines) {
+							top=par[0];
+							bottom=par[1];
+						}
+						break;
+					case 's':
+						save_cur();
+						break;
+					case 'u':
+						restore_cur();
+						break;
+				}
+		}
+	}
+	set_cursor();
+}
+
+/*
+ *  void con_init(void);
+ *
+ * This routine initalizes console interrupts, and does nothing
+ * else. If you want the screen to clear, call tty_write with
+ * the appropriate escape-sequece.
+ */
+void con_init(void)
+{
+	register unsigned char a;
+
+	gotoxy(*(unsigned char *)(0x90000+510),*(unsigned char *)(0x90000+511));
+	set_trap_gate(0x21,&keyboard_interrupt);
+	outb_p(inb_p(0x21)&0xfd,0x21);
+	a=inb_p(0x61);
+	outb_p(a|0x80,0x61);
+	outb(a,0x61);
+}
diff --git a/kernel/exit.c b/kernel/exit.c
new file mode 100644
index 0000000..3402c33
--- /dev/null
+++ b/kernel/exit.c
@@ -0,0 +1,135 @@
+#include <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <asm/segment.h>
+
+int sys_pause(void);
+int sys_close(int fd);
+
+void release(struct task_struct * p)
+{
+	int i;
+
+	if (!p)
+		return;
+	for (i=1 ; i<NR_TASKS ; i++)
+		if (task[i]==p) {
+			task[i]=NULL;
+			free_page((long)p);
+			schedule();
+			return;
+		}
+	panic("trying to release non-existent task");
+}
+
+static inline void send_sig(long sig,struct task_struct * p,int priv)
+{
+	if (!p || sig<1 || sig>32)
+		return;
+	if (priv ||
+		current->uid==p->uid ||
+		current->euid==p->uid ||
+		current->uid==p->euid ||
+		current->euid==p->euid)
+		p->signal |= (1<<(sig-1));
+}
+
+void do_kill(long pid,long sig,int priv)
+{
+	struct task_struct **p = NR_TASKS + task;
+
+	if (!pid) while (--p > &FIRST_TASK) {
+		if (*p && (*p)->pgrp == current->pid)
+			send_sig(sig,*p,priv);
+	} else if (pid>0) while (--p > &FIRST_TASK) {
+		if (*p && (*p)->pid == pid)
+			send_sig(sig,*p,priv);
+	} else if (pid == -1) while (--p > &FIRST_TASK)
+		send_sig(sig,*p,priv);
+	else while (--p > &FIRST_TASK)
+		if (*p && (*p)->pgrp == -pid)
+			send_sig(sig,*p,priv);
+}
+
+int sys_kill(int pid,int sig)
+{
+	do_kill(pid,sig,!(current->uid || current->euid));
+	return 0;
+}
+
+int do_exit(long code)
+{
+	int i;
+
+	free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
+	free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
+	for (i=0 ; i<NR_TASKS ; i++)
+		if (task[i] && task[i]->father == current->pid)
+			task[i]->father = 0;
+	for (i=0 ; i<NR_OPEN ; i++)
+		if (current->filp[i])
+			sys_close(i);
+	iput(current->pwd);
+	current->pwd=NULL;
+	iput(current->root);
+	current->root=NULL;
+	if (current->leader && current->tty >= 0)
+		tty_table[current->tty].pgrp = 0;
+	if (last_task_used_math == current)
+		last_task_used_math = NULL;
+	if (current->father) {
+		current->state = TASK_ZOMBIE;
+		do_kill(current->father,SIGCHLD,1);
+		current->exit_code = code;
+	} else
+		release(current);
+	schedule();
+	return (-1);	/* just to suppress warnings */
+}
+
+int sys_exit(int error_code)
+{
+	return do_exit((error_code&0xff)<<8);
+}
+
+int sys_waitpid(pid_t pid,int * stat_addr, int options)
+{
+	int flag=0;
+	struct task_struct ** p;
+
+	verify_area(stat_addr,4);
+repeat:
+	for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
+		if (*p && *p != current &&
+		   (pid==-1 || (*p)->pid==pid ||
+		   (pid==0 && (*p)->pgrp==current->pgrp) ||
+		   (pid<0 && (*p)->pgrp==-pid)))
+			if ((*p)->father == current->pid) {
+				flag=1;
+				if ((*p)->state==TASK_ZOMBIE) {
+					put_fs_long((*p)->exit_code,
+						(unsigned long *) stat_addr);
+					current->cutime += (*p)->utime;
+					current->cstime += (*p)->stime;
+					flag = (*p)->pid;
+					release(*p);
+					return flag;
+				}
+			}
+	if (flag) {
+		if (options & WNOHANG)
+			return 0;
+		sys_pause();
+		if (!(current->signal &= ~(1<<(SIGCHLD-1))))
+			goto repeat;
+		else
+			return -EINTR;
+	}
+	return -ECHILD;
+}
+
+
diff --git a/kernel/fork.c b/kernel/fork.c
new file mode 100644
index 0000000..70f9ddd
--- /dev/null
+++ b/kernel/fork.c
@@ -0,0 +1,136 @@
+/*
+ *  'fork.c' contains the help-routines for the 'fork' system call
+ * (see also system_call.s), and some misc functions ('verify_area').
+ * Fork is rather simple, once you get the hang of it, but the memory
+ * management can be a bitch. See 'mm/mm.c': 'copy_page_tables()'
+ */
+#include <errno.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+extern void write_verify(unsigned long address);
+
+long last_pid=0;
+
+void verify_area(void * addr,int size)
+{
+	unsigned long start;
+
+	start = (unsigned long) addr;
+	size += start & 0xfff;
+	start &= 0xfffff000;
+	start += get_base(current->ldt[2]);
+	while (size>0) {
+		size -= 4096;
+		write_verify(start);
+		start += 4096;
+	}
+}
+
+int copy_mem(int nr,struct task_struct * p)
+{
+	unsigned long old_data_base,new_data_base,data_limit;
+	unsigned long old_code_base,new_code_base,code_limit;
+
+	code_limit=get_limit(0x0f);
+	data_limit=get_limit(0x17);
+	old_code_base = get_base(current->ldt[1]);
+	old_data_base = get_base(current->ldt[2]);
+	if (old_data_base != old_code_base)
+		panic("We don't support separate I&D");
+	if (data_limit < code_limit)
+		panic("Bad data_limit");
+	new_data_base = new_code_base = nr * 0x4000000;
+	set_base(p->ldt[1],new_code_base);
+	set_base(p->ldt[2],new_data_base);
+	if (copy_page_tables(old_data_base,new_data_base,data_limit)) {
+		free_page_tables(new_data_base,data_limit);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+/*
+ *  Ok, this is the main fork-routine. It copies the system process
+ * information (task[nr]) and sets up the necessary registers. It
+ * also copies the data segment in it's entirety.
+ */
+int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
+		long ebx,long ecx,long edx,
+		long fs,long es,long ds,
+		long eip,long cs,long eflags,long esp,long ss)
+{
+	struct task_struct *p;
+	int i;
+	struct file *f;
+
+	p = (struct task_struct *) get_free_page();
+	if (!p)
+		return -EAGAIN;
+	*p = *current;	/* NOTE! this doesn't copy the supervisor stack */
+	p->state = TASK_RUNNING;
+	p->pid = last_pid;
+	p->father = current->pid;
+	p->counter = p->priority;
+	p->signal = 0;
+	p->alarm = 0;
+	p->leader = 0;		/* process leadership doesn't inherit */
+	p->utime = p->stime = 0;
+	p->cutime = p->cstime = 0;
+	p->start_time = jiffies;
+	p->tss.back_link = 0;
+	p->tss.esp0 = PAGE_SIZE + (long) p;
+	p->tss.ss0 = 0x10;
+	p->tss.eip = eip;
+	p->tss.eflags = eflags;
+	p->tss.eax = 0;
+	p->tss.ecx = ecx;
+	p->tss.edx = edx;
+	p->tss.ebx = ebx;
+	p->tss.esp = esp;
+	p->tss.ebp = ebp;
+	p->tss.esi = esi;
+	p->tss.edi = edi;
+	p->tss.es = es & 0xffff;
+	p->tss.cs = cs & 0xffff;
+	p->tss.ss = ss & 0xffff;
+	p->tss.ds = ds & 0xffff;
+	p->tss.fs = fs & 0xffff;
+	p->tss.gs = gs & 0xffff;
+	p->tss.ldt = _LDT(nr);
+	p->tss.trace_bitmap = 0x80000000;
+	if (last_task_used_math == current)
+		__asm__("fnsave %0"::"m" (p->tss.i387));
+	if (copy_mem(nr,p)) {
+		free_page((long) p);
+		return -EAGAIN;
+	}
+	for (i=0; i<NR_OPEN;i++)
+		if (f=p->filp[i])
+			f->f_count++;
+	if (current->pwd)
+		current->pwd->i_count++;
+	if (current->root)
+		current->root->i_count++;
+	set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
+	set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
+	task[nr] = p;	/* do this last, just in case */
+	return last_pid;
+}
+
+int find_empty_process(void)
+{
+	int i;
+
+	repeat:
+		if ((++last_pid)<0) last_pid=1;
+		for(i=0 ; i<NR_TASKS ; i++)
+			if (task[i] && task[i]->pid == last_pid) goto repeat;
+	for(i=1 ; i<NR_TASKS ; i++)
+		if (!task[i])
+			return i;
+	return -EAGAIN;
+}
diff --git a/kernel/hd.c b/kernel/hd.c
new file mode 100644
index 0000000..d3e6140
--- /dev/null
+++ b/kernel/hd.c
@@ -0,0 +1,413 @@
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/hdreg.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+/*
+ * This code handles all hd-interrupts, and read/write requests to
+ * the hard-disk. It is relatively straigthforward (not obvious maybe,
+ * but interrupts never are), while still being efficient, and never
+ * disabling interrupts (except to overcome possible race-condition).
+ * The elevator block-seek algorithm doesn't need to disable interrupts
+ * due to clever programming.
+ */
+
+/* Max read/write errors/sector */
+#define MAX_ERRORS	5
+#define MAX_HD		2
+#define NR_REQUEST	32
+
+/*
+ *  This struct defines the HD's and their types.
+ *  Currently defined for CP3044's, ie a modified
+ *  type 17.
+ */
+static struct hd_i_struct{
+	int head,sect,cyl,wpcom,lzone,ctl;
+	} hd_info[]= { HD_TYPE };
+
+#define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct)))
+
+static struct hd_struct {
+	long start_sect;
+	long nr_sects;
+} hd[5*MAX_HD]={{0,0},};
+
+static struct hd_request {
+	int hd;		/* -1 if no request */
+	int nsector;
+	int sector;
+	int head;
+	int cyl;
+	int cmd;
+	int errors;
+	struct buffer_head * bh;
+	struct hd_request * next;
+} request[NR_REQUEST];
+
+#define IN_ORDER(s1,s2) \
+((s1)->hd<(s2)->hd || (s1)->hd==(s2)->hd && \
+((s1)->cyl<(s2)->cyl || (s1)->cyl==(s2)->cyl && \
+((s1)->head<(s2)->head || (s1)->head==(s2)->head && \
+((s1)->sector<(s2)->sector))))
+
+static struct hd_request * this_request = NULL;
+
+static int sorting=0;
+
+static void do_request(void);
+static void reset_controller(void);
+static void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head,
+	unsigned int cyl,struct buffer_head * bh);
+void hd_init(void);
+
+#define port_read(port,buf,nr) \
+__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
+
+#define port_write(port,buf,nr) \
+__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
+
+extern void hd_interrupt(void);
+
+static struct task_struct * wait_for_request=NULL;
+
+static inline void lock_buffer(struct buffer_head * bh)
+{
+	if (bh->b_lock)
+		printk("hd.c: buffer multiply locked\n");
+	bh->b_lock=1;
+}
+
+static inline void unlock_buffer(struct buffer_head * bh)
+{
+	if (!bh->b_lock)
+		printk("hd.c: free buffer being unlocked\n");
+	bh->b_lock=0;
+	wake_up(&bh->b_wait);
+}
+
+static inline void wait_on_buffer(struct buffer_head * bh)
+{
+	cli();
+	while (bh->b_lock)
+		sleep_on(&bh->b_wait);
+	sti();
+}
+
+void rw_hd(int rw, struct buffer_head * bh)
+{
+	unsigned int block,dev;
+	unsigned int sec,head,cyl;
+
+	block = bh->b_blocknr << 1;
+	dev = MINOR(bh->b_dev);
+	if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects)
+		return;
+	block += hd[dev].start_sect;
+	dev /= 5;
+	__asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0),
+		"r" (hd_info[dev].sect));
+	__asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0),
+		"r" (hd_info[dev].head));
+	rw_abs_hd(rw,dev,sec+1,head,cyl,bh);
+}
+
+/* This may be used only once, enforced by 'static int callable' */
+int sys_setup(void)
+{
+	static int callable = 1;
+	int i,drive;
+	struct partition *p;
+
+	if (!callable)
+		return -1;
+	callable = 0;
+	for (drive=0 ; drive<NR_HD ; drive++) {
+		rw_abs_hd(READ,drive,1,0,0,(struct buffer_head *) start_buffer);
+		if (!start_buffer->b_uptodate) {
+			printk("Unable to read partition table of drive %d\n\r",
+				drive);
+			panic("");
+		}
+		if (start_buffer->b_data[510] != 0x55 || (unsigned char)
+		    start_buffer->b_data[511] != 0xAA) {
+			printk("Bad partition table on drive %d\n\r",drive);
+			panic("");
+		}
+		p = 0x1BE + (void *)start_buffer->b_data;
+		for (i=1;i<5;i++,p++) {
+			hd[i+5*drive].start_sect = p->start_sect;
+			hd[i+5*drive].nr_sects = p->nr_sects;
+		}
+	}
+	printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
+	mount_root();
+	return (0);
+}
+
+/*
+ * This is the pointer to a routine to be executed at every hd-interrupt.
+ * Interesting way of doing things, but should be rather practical.
+ */
+void (*do_hd)(void) = NULL;
+
+static int controller_ready(void)
+{
+	int retries=1000;
+
+	while (--retries && (inb(HD_STATUS)&0xc0)!=0x40);
+	return (retries);
+}
+
+static int win_result(void)
+{
+	int i=inb(HD_STATUS);
+
+	if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
+		== (READY_STAT | SEEK_STAT))
+		return(0); /* ok */
+	if (i&1) i=inb(HD_ERROR);
+	return (1);
+}
+
+static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
+		unsigned int head,unsigned int cyl,unsigned int cmd,
+		void (*intr_addr)(void))
+{
+	register int port asm("dx");
+
+	if (drive>1 || head>15)
+		panic("Trying to write bad sector");
+	if (!controller_ready())
+		panic("HD controller not ready");
+	do_hd = intr_addr;
+	outb(_CTL,HD_CMD);
+	port=HD_DATA;
+	outb_p(_WPCOM,++port);
+	outb_p(nsect,++port);
+	outb_p(sect,++port);
+	outb_p(cyl,++port);
+	outb_p(cyl>>8,++port);
+	outb_p(0xA0|(drive<<4)|head,++port);
+	outb(cmd,++port);
+}
+
+static int drive_busy(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < 100000; i++)
+		if (READY_STAT == (inb(HD_STATUS) & (BUSY_STAT | READY_STAT)))
+			break;
+	i = inb(HD_STATUS);
+	i &= BUSY_STAT | READY_STAT | SEEK_STAT;
+	if (i == READY_STAT | SEEK_STAT)
+		return(0);
+	printk("HD controller times out\n\r");
+	return(1);
+}
+
+static void reset_controller(void)
+{
+	int	i;
+
+	outb(4,HD_CMD);
+	for(i = 0; i < 1000; i++) nop();
+	outb(0,HD_CMD);
+	for(i = 0; i < 10000 && drive_busy(); i++) /* nothing */;
+	if (drive_busy())
+		printk("HD-controller still busy\n\r");
+	if((i = inb(ERR_STAT)) != 1)
+		printk("HD-controller reset failed: %02x\n\r",i);
+}
+
+static void reset_hd(int nr)
+{
+	reset_controller();
+	hd_out(nr,_SECT,_SECT,_HEAD-1,_CYL,WIN_SPECIFY,&do_request);
+}
+
+void unexpected_hd_interrupt(void)
+{
+	panic("Unexpected HD interrupt\n\r");
+}
+
+static void bad_rw_intr(void)
+{
+	int i = this_request->hd;
+
+	if (this_request->errors++ >= MAX_ERRORS) {
+		this_request->bh->b_uptodate = 0;
+		unlock_buffer(this_request->bh);
+		wake_up(&wait_for_request);
+		this_request->hd = -1;
+		this_request=this_request->next;
+	}
+	reset_hd(i);
+}
+
+static void read_intr(void)
+{
+	if (win_result()) {
+		bad_rw_intr();
+		return;
+	}
+	port_read(HD_DATA,this_request->bh->b_data+
+		512*(this_request->nsector&1),256);
+	this_request->errors = 0;
+	if (--this_request->nsector)
+		return;
+	this_request->bh->b_uptodate = 1;
+	this_request->bh->b_dirt = 0;
+	wake_up(&wait_for_request);
+	unlock_buffer(this_request->bh);
+	this_request->hd = -1;
+	this_request=this_request->next;
+	do_request();
+}
+
+static void write_intr(void)
+{
+	if (win_result()) {
+		bad_rw_intr();
+		return;
+	}
+	if (--this_request->nsector) {
+		port_write(HD_DATA,this_request->bh->b_data+512,256);
+		return;
+	}
+	this_request->bh->b_uptodate = 1;
+	this_request->bh->b_dirt = 0;
+	wake_up(&wait_for_request);
+	unlock_buffer(this_request->bh);
+	this_request->hd = -1;
+	this_request=this_request->next;
+	do_request();
+}
+
+static void do_request(void)
+{
+	int i,r;
+
+	if (sorting)
+		return;
+	if (!this_request) {
+		do_hd=NULL;
+		return;
+	}
+	if (this_request->cmd == WIN_WRITE) {
+		hd_out(this_request->hd,this_request->nsector,this_request->
+			sector,this_request->head,this_request->cyl,
+			this_request->cmd,&write_intr);
+		for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
+			/* nothing */ ;
+		if (!r) {
+			reset_hd(this_request->hd);
+			return;
+		}
+		port_write(HD_DATA,this_request->bh->b_data+
+			512*(this_request->nsector&1),256);
+	} else if (this_request->cmd == WIN_READ) {
+		hd_out(this_request->hd,this_request->nsector,this_request->
+			sector,this_request->head,this_request->cyl,
+			this_request->cmd,&read_intr);
+	} else
+		panic("unknown hd-command");
+}
+
+/*
+ * add-request adds a request to the linked list.
+ * It sets the 'sorting'-variable when doing something
+ * that interrupts shouldn't touch.
+ */
+static void add_request(struct hd_request * req)
+{
+	struct hd_request * tmp;
+
+	if (req->nsector != 2)
+		panic("nsector!=2 not implemented");
+/*
+ * Not to mess up the linked lists, we never touch the two first
+ * entries (not this_request, as it is used by current interrups,
+ * and not this_request->next, as it can be assigned to this_request).
+ * This is not too high a price to pay for the ability of not
+ * disabling interrupts.
+ */
+	sorting=1;
+	if (!(tmp=this_request))
+		this_request=req;
+	else {
+		if (!(tmp->next))
+			tmp->next=req;
+		else {
+			tmp=tmp->next;
+			for ( ; tmp->next ; tmp=tmp->next)
+				if ((IN_ORDER(tmp,req) ||
+				    !IN_ORDER(tmp,tmp->next)) &&
+				    IN_ORDER(req,tmp->next))
+					break;
+			req->next=tmp->next;
+			tmp->next=req;
+		}
+	}
+	sorting=0;
+/*
+ * NOTE! As a result of sorting, the interrupts may have died down,
+ * as they aren't redone due to locking with sorting=1. They might
+ * also never have started, if this is the first request in the queue,
+ * so we restart them if necessary.
+ */
+	if (!do_hd)
+		do_request();
+}
+
+void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head,
+	unsigned int cyl,struct buffer_head * bh)
+{
+	struct hd_request * req;
+
+	if (rw!=READ && rw!=WRITE)
+		panic("Bad hd command, must be R/W");
+	lock_buffer(bh);
+repeat:
+	for (req=0+request ; req<NR_REQUEST+request ; req++)
+		if (req->hd<0)
+			break;
+	if (req==NR_REQUEST+request) {
+		sleep_on(&wait_for_request);
+		goto repeat;
+	}
+	req->hd=nr;
+	req->nsector=2;
+	req->sector=sec;
+	req->head=head;
+	req->cyl=cyl;
+	req->cmd = ((rw==READ)?WIN_READ:WIN_WRITE);
+	req->bh=bh;
+	req->errors=0;
+	req->next=NULL;
+	add_request(req);
+	wait_on_buffer(bh);
+}
+
+void hd_init(void)
+{
+	int i;
+
+	for (i=0 ; i<NR_REQUEST ; i++) {
+		request[i].hd = -1;
+		request[i].next = NULL;
+	}
+	for (i=0 ; i<NR_HD ; i++) {
+		hd[i*5].start_sect = 0;
+		hd[i*5].nr_sects = hd_info[i].head*
+				hd_info[i].sect*hd_info[i].cyl;
+	}
+	set_trap_gate(0x2E,&hd_interrupt);
+	outb_p(inb_p(0x21)&0xfb,0x21);
+	outb(inb_p(0xA1)&0xbf,0xA1);
+}
diff --git a/kernel/keyboard.s b/kernel/keyboard.s
new file mode 100644
index 0000000..ba54be5
--- /dev/null
+++ b/kernel/keyboard.s
@@ -0,0 +1,409 @@
+/*
+ *	keyboard.s
+ */
+
+.text
+.globl _keyboard_interrupt
+
+/*
+ * these are for the keyboard read functions
+ */
+size	= 1024		/* must be a power of two ! And MUST be the same
+			   as in tty_io.c !!!! */
+head = 4
+tail = 8
+proc_list = 12
+buf = 16
+
+mode:	.byte 0		/* caps, alt, ctrl and shift mode */
+leds:	.byte 2		/* num-lock, caps, scroll-lock mode (nom-lock on) */
+e0:	.byte 0
+
+/*
+ *  con_int is the real interrupt routine that reads the
+ *  keyboard scan-code and converts it into the appropriate
+ *  ascii character(s).
+ */
+_keyboard_interrupt:
+	pushl %eax
+	pushl %ebx
+	pushl %ecx
+	pushl %edx
+	push %ds
+	push %es
+	movl $0x10,%eax
+	mov %ax,%ds
+	mov %ax,%es
+	xorl %al,%al		/* %eax is scan code */
+	inb $0x60,%al
+	cmpb $0xe0,%al
+	je set_e0
+	cmpb $0xe1,%al
+	je set_e1
+	call key_table(,%eax,4)
+	movb $0,e0
+e0_e1:	inb $0x61,%al
+	jmp 1f
+1:	jmp 1f
+1:	orb $0x80,%al
+	jmp 1f
+1:	jmp 1f
+1:	outb %al,$0x61
+	jmp 1f
+1:	jmp 1f
+1:	andb $0x7F,%al
+	outb %al,$0x61
+	movb $0x20,%al
+	outb %al,$0x20
+	pushl $0
+	call _do_tty_interrupt
+	addl $4,%esp
+	pop %es
+	pop %ds
+	popl %edx
+	popl %ecx
+	popl %ebx
+	popl %eax
+	iret
+set_e0:	movb $1,e0
+	jmp e0_e1
+set_e1:	movb $2,e0
+	jmp e0_e1
+
+/*
+ * This routine fills the buffer with max 8 bytes, taken from
+ * %ebx:%eax. (%edx is high). The bytes are written in the
+ * order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero.
+ */
+put_queue:
+	pushl %ecx
+	pushl %edx
+	movl _table_list,%edx		# read-queue for console
+	movl head(%edx),%ecx
+1:	movb %al,buf(%edx,%ecx)
+	incl %ecx
+	andl $size-1,%ecx
+	cmpl tail(%edx),%ecx		# buffer full - discard everything
+	je 3f
+	shrdl $8,%ebx,%eax
+	je 2f
+	shrl $8,%ebx
+	jmp 1b
+2:	movl %ecx,head(%edx)
+	movl proc_list(%edx),%ecx
+	testl %ecx,%ecx
+	je 3f
+	movl $0,(%ecx)
+3:	popl %edx
+	popl %ecx
+	ret
+
+ctrl:	movb $0x04,%al
+	jmp 1f
+alt:	movb $0x10,%al
+1:	cmpb $0,e0
+	je 2f
+	addb %al,%al
+2:	orb %al,mode
+	ret
+unctrl:	movb $0x04,%al
+	jmp 1f
+unalt:	movb $0x10,%al
+1:	cmpb $0,e0
+	je 2f
+	addb %al,%al
+2:	notb %al
+	andb %al,mode
+	ret
+
+lshift:
+	orb $0x01,mode
+	ret
+unlshift:
+	andb $0xfe,mode
+	ret
+rshift:
+	orb $0x02,mode
+	ret
+unrshift:
+	andb $0xfd,mode
+	ret
+
+caps:	testb $0x80,mode
+	jne 1f
+	xorb $4,leds
+	xorb $0x40,mode
+	orb $0x80,mode
+set_leds:
+	call kb_wait
+	movb $0xed,%al		/* set leds command */
+	outb %al,$0x60
+	call kb_wait
+	movb leds,%al
+	outb %al,$0x60
+	ret
+uncaps:	andb $0x7f,mode
+	ret
+scroll:
+	xorb $1,leds
+	jmp set_leds
+num:	xorb $2,leds
+	jmp set_leds
+
+/*
+ *  curosr-key/numeric keypad cursor keys are handled here.
+ *  checking for numeric keypad etc.
+ */
+cursor:
+	subb $0x47,%al
+	jb 1f
+	cmpb $12,%al
+	ja 1f
+	jne cur2		/* check for ctrl-alt-del */
+	testb $0x0c,mode
+	je cur2
+	testb $0x30,mode
+	jne reboot
+cur2:	cmpb $0x01,e0		/* e0 forces cursor movement */
+	je cur
+	testb $0x02,leds	/* not num-lock forces cursor */
+	je cur
+	testb $0x03,mode	/* shift forces cursor */
+	jne cur
+	xorl %ebx,%ebx
+	movb num_table(%eax),%al
+	jmp put_queue
+1:	ret
+
+cur:	movb cur_table(%eax),%al
+	cmpb $'9,%al
+	ja ok_cur
+	movb $'~,%ah
+ok_cur:	shll $16,%eax
+	movw $0x5b1b,%ax
+	xorl %ebx,%ebx
+	jmp put_queue
+
+num_table:
+	.ascii "789 456 1230,"
+cur_table:
+	.ascii "HA5 DGC YB623"
+
+/*
+ * this routine handles function keys
+ */
+func:
+	subb $0x3B,%al
+	jb end_func
+	cmpb $9,%al
+	jbe ok_func
+	subb $18,%al
+	cmpb $10,%al
+	jb end_func
+	cmpb $11,%al
+	ja end_func
+ok_func:
+	cmpl $4,%ecx		/* check that there is enough room */
+	jl end_func
+	movl func_table(,%eax,4),%eax
+	xorl %ebx,%ebx
+	jmp put_queue
+end_func:
+	ret
+
+/*
+ * function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc.
+ */
+func_table:
+	.long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b
+	.long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b
+	.long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b
+
+key_map:
+	.byte 0,27
+	.ascii "1234567890+'"
+	.byte 127,9
+	.ascii "qwertyuiop}"
+	.byte 0,10,0
+	.ascii "asdfghjkl|{"
+	.byte 0,0
+	.ascii "'zxcvbnm,.-"
+	.byte 0,'*,0,32		/* 36-39 */
+	.fill 16,1,0		/* 3A-49 */
+	.byte '-,0,0,0,'+	/* 4A-4E */
+	.byte 0,0,0,0,0,0,0	/* 4F-55 */
+	.byte '<
+	.fill 10,1,0
+
+shift_map:
+	.byte 0,27
+	.ascii "!\"#$%&/()=?`"
+	.byte 127,9
+	.ascii "QWERTYUIOP]^"
+	.byte 10,0
+	.ascii "ASDFGHJKL\\["
+	.byte 0,0
+	.ascii "*ZXCVBNM;:_"
+	.byte 0,'*,0,32		/* 36-39 */
+	.fill 16,1,0		/* 3A-49 */
+	.byte '-,0,0,0,'+	/* 4A-4E */
+	.byte 0,0,0,0,0,0,0	/* 4F-55 */
+	.byte '>
+	.fill 10,1,0
+
+alt_map:
+	.byte 0,0
+	.ascii "\0@\0$\0\0{[]}\\\0"
+	.byte 0,0
+	.byte 0,0,0,0,0,0,0,0,0,0,0
+	.byte '~,10,0
+	.byte 0,0,0,0,0,0,0,0,0,0,0
+	.byte 0,0
+	.byte 0,0,0,0,0,0,0,0,0,0,0
+	.byte 0,0,0,0		/* 36-39 */
+	.fill 16,1,0		/* 3A-49 */
+	.byte 0,0,0,0,0		/* 4A-4E */
+	.byte 0,0,0,0,0,0,0	/* 4F-55 */
+	.byte '|
+	.fill 10,1,0
+
+/*
+ * do_self handles "normal" keys, ie keys that don't change meaning
+ * and which have just one character returns.
+ */
+do_self:
+	lea alt_map,%ebx
+	testb $0x20,mode		/* alt-gr */
+	jne 1f
+	lea shift_map,%ebx
+	testb $0x03,mode
+	jne 1f
+	lea key_map,%ebx
+1:	movb (%ebx,%eax),%al
+	orb %al,%al
+	je none
+	testb $0x4c,mode		/* ctrl or caps */
+	je 2f
+	cmpb $'a,%al
+	jb 2f
+	cmpb $'z,%al
+	ja 2f
+	subb $32,%al
+2:	testb $0x0c,mode		/* ctrl */
+	je 3f
+	cmpb $64,%al
+	jb 3f
+	cmpb $64+32,%al
+	jae 3f
+	subb $64,%al
+3:	testb $0x10,mode		/* left alt */
+	je 4f
+	orb $0x80,%al
+4:	andl $0xff,%eax
+	xorl %ebx,%ebx
+	call put_queue
+none:	ret
+
+/*
+ * minus has a routine of it's own, as a 'E0h' before
+ * the scan code for minus means that the numeric keypad
+ * slash was pushed.
+ */
+minus:	cmpb $1,e0
+	jne do_self
+	movl $'/,%eax
+	xorl %ebx,%ebx
+	jmp put_queue
+
+/*
+ * This table decides which routine to call when a scan-code has been
+ * gotten. Most routines just call do_self, or none, depending if
+ * they are make or break.
+ */
+key_table:
+	.long none,do_self,do_self,do_self	/* 00-03 s0 esc 1 2 */
+	.long do_self,do_self,do_self,do_self	/* 04-07 3 4 5 6 */
+	.long do_self,do_self,do_self,do_self	/* 08-0B 7 8 9 0 */
+	.long do_self,do_self,do_self,do_self	/* 0C-0F + ' bs tab */
+	.long do_self,do_self,do_self,do_self	/* 10-13 q w e r */
+	.long do_self,do_self,do_self,do_self	/* 14-17 t y u i */
+	.long do_self,do_self,do_self,do_self	/* 18-1B o p } ^ */
+	.long do_self,ctrl,do_self,do_self	/* 1C-1F enter ctrl a s */
+	.long do_self,do_self,do_self,do_self	/* 20-23 d f g h */
+	.long do_self,do_self,do_self,do_self	/* 24-27 j k l | */
+	.long do_self,do_self,lshift,do_self	/* 28-2B { para lshift , */
+	.long do_self,do_self,do_self,do_self	/* 2C-2F z x c v */
+	.long do_self,do_self,do_self,do_self	/* 30-33 b n m , */
+	.long do_self,minus,rshift,do_self	/* 34-37 . - rshift * */
+	.long alt,do_self,caps,func		/* 38-3B alt sp caps f1 */
+	.long func,func,func,func		/* 3C-3F f2 f3 f4 f5 */
+	.long func,func,func,func		/* 40-43 f6 f7 f8 f9 */
+	.long func,num,scroll,cursor		/* 44-47 f10 num scr home */
+	.long cursor,cursor,do_self,cursor	/* 48-4B up pgup - left */
+	.long cursor,cursor,do_self,cursor	/* 4C-4F n5 right + end */
+	.long cursor,cursor,cursor,cursor	/* 50-53 dn pgdn ins del */
+	.long none,none,do_self,func		/* 54-57 sysreq ? < f11 */
+	.long func,none,none,none		/* 58-5B f12 ? ? ? */
+	.long none,none,none,none		/* 5C-5F ? ? ? ? */
+	.long none,none,none,none		/* 60-63 ? ? ? ? */
+	.long none,none,none,none		/* 64-67 ? ? ? ? */
+	.long none,none,none,none		/* 68-6B ? ? ? ? */
+	.long none,none,none,none		/* 6C-6F ? ? ? ? */
+	.long none,none,none,none		/* 70-73 ? ? ? ? */
+	.long none,none,none,none		/* 74-77 ? ? ? ? */
+	.long none,none,none,none		/* 78-7B ? ? ? ? */
+	.long none,none,none,none		/* 7C-7F ? ? ? ? */
+	.long none,none,none,none		/* 80-83 ? br br br */
+	.long none,none,none,none		/* 84-87 br br br br */
+	.long none,none,none,none		/* 88-8B br br br br */
+	.long none,none,none,none		/* 8C-8F br br br br */
+	.long none,none,none,none		/* 90-93 br br br br */
+	.long none,none,none,none		/* 94-97 br br br br */
+	.long none,none,none,none		/* 98-9B br br br br */
+	.long none,unctrl,none,none		/* 9C-9F br unctrl br br */
+	.long none,none,none,none		/* A0-A3 br br br br */
+	.long none,none,none,none		/* A4-A7 br br br br */
+	.long none,none,unlshift,none		/* A8-AB br br unlshift br */
+	.long none,none,none,none		/* AC-AF br br br br */
+	.long none,none,none,none		/* B0-B3 br br br br */
+	.long none,none,unrshift,none		/* B4-B7 br br unrshift br */
+	.long unalt,none,uncaps,none		/* B8-BB unalt br uncaps br */
+	.long none,none,none,none		/* BC-BF br br br br */
+	.long none,none,none,none		/* C0-C3 br br br br */
+	.long none,none,none,none		/* C4-C7 br br br br */
+	.long none,none,none,none		/* C8-CB br br br br */
+	.long none,none,none,none		/* CC-CF br br br br */
+	.long none,none,none,none		/* D0-D3 br br br br */
+	.long none,none,none,none		/* D4-D7 br br br br */
+	.long none,none,none,none		/* D8-DB br ? ? ? */
+	.long none,none,none,none		/* DC-DF ? ? ? ? */
+	.long none,none,none,none		/* E0-E3 e0 e1 ? ? */
+	.long none,none,none,none		/* E4-E7 ? ? ? ? */
+	.long none,none,none,none		/* E8-EB ? ? ? ? */
+	.long none,none,none,none		/* EC-EF ? ? ? ? */
+	.long none,none,none,none		/* F0-F3 ? ? ? ? */
+	.long none,none,none,none		/* F4-F7 ? ? ? ? */
+	.long none,none,none,none		/* F8-FB ? ? ? ? */
+	.long none,none,none,none		/* FC-FF ? ? ? ? */
+
+/*
+ * kb_wait waits for the keyboard controller buffer to empty.
+ * there is no timeout - if the buffer doesn't empty, we hang.
+ */
+kb_wait:
+	pushl %eax
+1:	inb $0x64,%al
+	testb $0x02,%al
+	jne 1b
+	popl %eax
+	ret
+/*
+ * This routine reboots the machine by asking the keyboard
+ * controller to pulse the reset-line low.
+ */
+reboot:
+	call kb_wait
+	movw $0x1234,0x472	/* don't do memory check */
+	movb $0xfc,%al		/* pulse reset and A20 low */
+	outb %al,$0x64
+die:	jmp die
diff --git a/kernel/mktime.c b/kernel/mktime.c
new file mode 100644
index 0000000..3ba79be
--- /dev/null
+++ b/kernel/mktime.c
@@ -0,0 +1,52 @@
+#include <time.h>
+
+/*
+ * This isn't the library routine, it is only used in the kernel.
+ * as such, we don't care about years<1970 etc, but assume everything
+ * is ok. Similarly, TZ etc is happily ignored. We just do everything
+ * as easily as possible. Let's find something public for the library
+ * routines (although I think minix times is public).
+ */
+/*
+ * PS. I hate whoever though up the year 1970 - couldn't they have gotten
+ * a leap-year instead? I also hate Gregorius, pope or no. I'm grumpy.
+ */
+#define MINUTE 60
+#define HOUR (60*MINUTE)
+#define DAY (24*HOUR)
+#define YEAR (365*DAY)
+
+/* interestingly, we assume leap-years */
+static int month[12] = {
+	0,
+	DAY*(31),
+	DAY*(31+29),
+	DAY*(31+29+31),
+	DAY*(31+29+31+30),
+	DAY*(31+29+31+30+31),
+	DAY*(31+29+31+30+31+30),
+	DAY*(31+29+31+30+31+30+31),
+	DAY*(31+29+31+30+31+30+31+31),
+	DAY*(31+29+31+30+31+30+31+31+30),
+	DAY*(31+29+31+30+31+30+31+31+30+31),
+	DAY*(31+29+31+30+31+30+31+31+30+31+30)
+};
+
+long kernel_mktime(struct tm * tm)
+{
+	long res;
+	int year;
+
+	year = tm->tm_year - 70;
+/* magic offsets (y+1) needed to get leapyears right.*/
+	res = YEAR*year + DAY*((year+1)/4);
+	res += month[tm->tm_mon];
+/* and (y+2) here. If it wasn't a leap-year, we have to adjust */
+	if (tm->tm_mon>1 && ((year+2)%4))
+		res -= DAY;
+	res += DAY*(tm->tm_mday-1);
+	res += HOUR*tm->tm_hour;
+	res += MINUTE*tm->tm_min;
+	res += tm->tm_sec;
+	return res;
+}
diff --git a/kernel/panic.c b/kernel/panic.c
new file mode 100644
index 0000000..feab0cc
--- /dev/null
+++ b/kernel/panic.c
@@ -0,0 +1,11 @@
+/*
+ * This function is used through-out the kernel (includeinh mm and fs)
+ * to indicate a major problem.
+ */
+#include <linux/kernel.h>
+
+volatile void panic(const char * s)
+{
+	printk("Kernel panic: %s\n\r",s);
+	for(;;);
+}
diff --git a/kernel/printk.c b/kernel/printk.c
new file mode 100644
index 0000000..7a70dc3
--- /dev/null
+++ b/kernel/printk.c
@@ -0,0 +1,33 @@
+/*
+ * When in kernel-mode, we cannot use printf, as fs is liable to
+ * point to 'interesting' things. Make a printf with fs-saving, and
+ * all is well.
+ */
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <linux/kernel.h>
+
+static char buf[1024];
+
+int printk(const char *fmt, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, fmt);
+	i=vsprintf(buf,fmt,args);
+	va_end(args);
+	__asm__("push %%fs\n\t"
+		"push %%ds\n\t"
+		"pop %%fs\n\t"
+		"pushl %0\n\t"
+		"pushl $_buf\n\t"
+		"pushl $0\n\t"
+		"call _tty_write\n\t"
+		"addl $8,%%esp\n\t"
+		"popl %0\n\t"
+		"pop %%fs"
+		::"r" (i):"ax","cx","dx");
+	return i;
+}
diff --git a/kernel/rs_io.s b/kernel/rs_io.s
new file mode 100644
index 0000000..62f075f
--- /dev/null
+++ b/kernel/rs_io.s
@@ -0,0 +1,141 @@
+/*
+ *	rs_io.s
+ *
+ * This module implements the rs232 io interrupts.
+ */
+
+.text
+.globl _rs1_interrupt,_rs2_interrupt
+
+size	= 1024				/* must be power of two !
+					   and must match the value
+					   in tty_io.c!!! */
+
+/* these are the offsets into the read/write buffer structures */
+rs_addr = 0
+head = 4
+tail = 8
+proc_list = 12
+buf = 16
+
+startup	= 256		/* chars left in write queue when we restart it */
+
+/*
+ * These are the actual interrupt routines. They look where
+ * the interrupt is coming from, and take appropriate action.
+ */
+.align 2
+_rs1_interrupt:
+	pushl $_table_list+8
+	jmp rs_int
+.align 2
+_rs2_interrupt:
+	pushl $_table_list+16
+rs_int:
+	pushl %edx
+	pushl %ecx
+	pushl %ebx
+	pushl %eax
+	push %es
+	push %ds		/* as this is an interrupt, we cannot */
+	pushl $0x10		/* know that bs is ok. Load it */
+	pop %ds
+	pushl $0x10
+	pop %es
+	movl 24(%esp),%edx
+	movl (%edx),%edx
+	movl rs_addr(%edx),%edx
+	addl $2,%edx		/* interrupt ident. reg */
+rep_int:
+	xorl %eax,%eax
+	inb %dx,%al
+	testb $1,%al
+	jne end
+	cmpb $6,%al		/* this shouldn't happen, but ... */
+	ja end
+	movl 24(%esp),%ecx
+	pushl %edx
+	subl $2,%edx
+	call jmp_table(,%eax,2)		/* NOTE! not *4, bit0 is 0 already */
+	popl %edx
+	jmp rep_int
+end:	movb $0x20,%al
+	outb %al,$0x20		/* EOI */
+	pop %ds
+	pop %es
+	popl %eax
+	popl %ebx
+	popl %ecx
+	popl %edx
+	addl $4,%esp		# jump over _table_list entry
+	iret
+
+jmp_table:
+	.long modem_status,write_char,read_char,line_status
+
+.align 2
+modem_status:
+	addl $6,%edx		/* clear intr by reading modem status reg */
+	inb %dx,%al
+	ret
+
+.align 2
+line_status:
+	addl $5,%edx		/* clear intr by reading line status reg. */
+	inb %dx,%al
+	ret
+
+.align 2
+read_char:
+	inb %dx,%al
+	movl %ecx,%edx
+	subl $_table_list,%edx
+	shrl $3,%edx
+	movl (%ecx),%ecx		# read-queue
+	movl head(%ecx),%ebx
+	movb %al,buf(%ecx,%ebx)
+	incl %ebx
+	andl $size-1,%ebx
+	cmpl tail(%ecx),%ebx
+	je 1f
+	movl %ebx,head(%ecx)
+	pushl %edx
+	call _do_tty_interrupt
+	addl $4,%esp
+1:	ret
+
+.align 2
+write_char:
+	movl 4(%ecx),%ecx		# write-queue
+	movl head(%ecx),%ebx
+	subl tail(%ecx),%ebx
+	andl $size-1,%ebx		# nr chars in queue
+	je write_buffer_empty
+	cmpl $startup,%ebx
+	ja 1f
+	movl proc_list(%ecx),%ebx	# wake up sleeping process
+	testl %ebx,%ebx			# is there any?
+	je 1f
+	movl $0,(%ebx)
+1:	movl tail(%ecx),%ebx
+	movb buf(%ecx,%ebx),%al
+	outb %al,%dx
+	incl %ebx
+	andl $size-1,%ebx
+	movl %ebx,tail(%ecx)
+	cmpl head(%ecx),%ebx
+	je write_buffer_empty
+	ret
+.align 2
+write_buffer_empty:
+	movl proc_list(%ecx),%ebx	# wake up sleeping process
+	testl %ebx,%ebx			# is there any?
+	je 1f
+	movl $0,(%ebx)
+1:	incl %edx
+	inb %dx,%al
+	jmp 1f
+1:	jmp 1f
+1:	andb $0xd,%al		/* disable transmit interrupt */
+	outb %al,%dx
+	ret
diff --git a/kernel/sched.c b/kernel/sched.c
new file mode 100644
index 0000000..03399fa
--- /dev/null
+++ b/kernel/sched.c
@@ -0,0 +1,254 @@
+/*
+ * 'sched.c' is the main kernel file. It contains scheduling primitives
+ * (sleep_on, wakeup, schedule etc) as well as a number of simple system
+ * call functions (type getpid(), which just extracts a field from
+ * current-task
+ */
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <signal.h>
+#include <linux/sys.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define LATCH (1193180/HZ)
+
+extern void mem_use(void);
+
+extern int timer_interrupt(void);
+extern int system_call(void);
+
+union task_union {
+	struct task_struct task;
+	char stack[PAGE_SIZE];
+};
+
+static union task_union init_task = {INIT_TASK,};
+
+long volatile jiffies=0;
+long startup_time=0;
+struct task_struct *current = &(init_task.task), *last_task_used_math = NULL;
+
+struct task_struct * task[NR_TASKS] = {&(init_task.task), };
+
+long user_stack [ PAGE_SIZE>>2 ] ;
+
+struct {
+	long * a;
+	short b;
+	} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
+/*
+ *  'math_state_restore()' saves the current math information in the
+ * old math state array, and gets the new ones from the current task
+ */
+void math_state_restore()
+{
+	if (last_task_used_math)
+		__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
+	if (current->used_math)
+		__asm__("frstor %0"::"m" (current->tss.i387));
+	else {
+		__asm__("fninit"::);
+		current->used_math=1;
+	}
+	last_task_used_math=current;
+}
+
+/*
+ *  'schedule()' is the scheduler function. This is GOOD CODE! There
+ * probably won't be any reason to change this, as it should work well
+ * in all circumstances (ie gives IO-bound processes good response etc).
+ * The one thing you might take a look at is the signal-handler code here.
+ *
+ *   NOTE!!  Task 0 is the 'idle' task, which gets called when no other
+ * tasks can run. It can not be killed, and it cannot sleep. The 'state'
+ * information in task[0] is never used.
+ */
+void schedule(void)
+{
+	int i,next,c;
+	struct task_struct ** p;
+
+/* check alarm, wake up any interruptible tasks that have got a signal */
+
+	for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
+		if (*p) {
+			if ((*p)->alarm && (*p)->alarm < jiffies) {
+					(*p)->signal |= (1<<(SIGALRM-1));
+					(*p)->alarm = 0;
+				}
+			if ((*p)->signal && (*p)->state==TASK_INTERRUPTIBLE)
+				(*p)->state=TASK_RUNNING;
+		}
+
+/* this is the scheduler proper: */
+
+	while (1) {
+		c = -1;
+		next = 0;
+		i = NR_TASKS;
+		p = &task[NR_TASKS];
+		while (--i) {
+			if (!*--p)
+				continue;
+			if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
+				c = (*p)->counter, next = i;
+		}
+		if (c) break;
+		for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
+			if (*p)
+				(*p)->counter = ((*p)->counter >> 1) +
+						(*p)->priority;
+	}
+	switch_to(next);
+}
+
+int sys_pause(void)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule();
+	return 0;
+}
+
+void sleep_on(struct task_struct **p)
+{
+	struct task_struct *tmp;
+
+	if (!p)
+		return;
+	if (current == &(init_task.task))
+		panic("task[0] trying to sleep");
+	tmp = *p;
+	*p = current;
+	current->state = TASK_UNINTERRUPTIBLE;
+	schedule();
+	if (tmp)
+		tmp->state=0;
+}
+
+void interruptible_sleep_on(struct task_struct **p)
+{
+	struct task_struct *tmp;
+
+	if (!p)
+		return;
+	if (current == &(init_task.task))
+		panic("task[0] trying to sleep");
+	tmp=*p;
+	*p=current;
+repeat:	current->state = TASK_INTERRUPTIBLE;
+	schedule();
+	if (*p && *p != current) {
+		(**p).state=0;
+		goto repeat;
+	}
+	*p=NULL;
+	if (tmp)
+		tmp->state=0;
+}
+
+void wake_up(struct task_struct **p)
+{
+	if (p && *p) {
+		(**p).state=0;
+		*p=NULL;
+	}
+}
+
+void do_timer(long cpl)
+{
+	if (cpl)
+		current->utime++;
+	else
+		current->stime++;
+	if ((--current->counter)>0) return;
+	current->counter=0;
+	if (!cpl) return;
+	schedule();
+}
+
+int sys_alarm(long seconds)
+{
+	current->alarm = (seconds>0)?(jiffies+HZ*seconds):0;
+	return seconds;
+}
+
+int sys_getpid(void)
+{
+	return current->pid;
+}
+
+int sys_getppid(void)
+{
+	return current->father;
+}
+
+int sys_getuid(void)
+{
+	return current->uid;
+}
+
+int sys_geteuid(void)
+{
+	return current->euid;
+}
+
+int sys_getgid(void)
+{
+	return current->gid;
+}
+
+int sys_getegid(void)
+{
+	return current->egid;
+}
+
+int sys_nice(long increment)
+{
+	if (current->priority-increment>0)
+		current->priority -= increment;
+	return 0;
+}
+
+int sys_signal(long signal,long addr,long restorer)
+{
+	long i;
+
+	switch (signal) {
+		case SIGHUP: case SIGINT: case SIGQUIT: case SIGILL:
+		case SIGTRAP: case SIGABRT: case SIGFPE: case SIGUSR1:
+		case SIGSEGV: case SIGUSR2: case SIGPIPE: case SIGALRM:
+		case SIGCHLD:
+			i=(long) current->sig_fn[signal-1];
+			current->sig_fn[signal-1] = (fn_ptr) addr;
+			current->sig_restorer = (fn_ptr) restorer;
+			return i;
+		default: return -1;
+	}
+}
+
+void sched_init(void)
+{
+	int i;
+	struct desc_struct * p;
+
+	set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));
+	set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
+	p = gdt+2+FIRST_TSS_ENTRY;
+	for(i=1;i<NR_TASKS;i++) {
+		task[i] = NULL;
+		p->a=p->b=0;
+		p++;
+		p->a=p->b=0;
+		p++;
+	}
+	ltr(0);
+	lldt(0);
+	outb_p(0x36,0x43);		/* binary, mode 3, LSB/MSB, ch 0 */
+	outb_p(LATCH & 0xff , 0x40);	/* LSB */
+	outb(LATCH >> 8 , 0x40);	/* MSB */
+	set_intr_gate(0x20,&timer_interrupt);
+	outb(inb_p(0x21)&~0x01,0x21);
+	set_system_gate(0x80,&system_call);
+}
diff --git a/kernel/serial.c b/kernel/serial.c
new file mode 100644
index 0000000..f542513
--- /dev/null
+++ b/kernel/serial.c
@@ -0,0 +1,53 @@
+/*
+ *	serial.c
+ *
+ * This module implements the rs232 io functions
+ *	void rs_write(struct tty_struct * queue);
+ *	void rs_init(void);
+ * and all interrupts pertaining to serial IO.
+ */
+
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#define WAKEUP_CHARS (TTY_BUF_SIZE/4)
+
+extern void rs1_interrupt(void);
+extern void rs2_interrupt(void);
+
+static void init(int port)
+{
+	outb_p(0x80,port+3);	/* set DLAB of line control reg */
+	outb_p(0x30,port);	/* LS of divisor (48 -> 2400 bps */
+	outb_p(0x00,port+1);	/* MS of divisor */
+	outb_p(0x03,port+3);	/* reset DLAB */
+	outb_p(0x0b,port+4);	/* set DTR,RTS, OUT_2 */
+	outb_p(0x0d,port+1);	/* enable all intrs but writes */
+	(void)inb(port);	/* read data port to reset things (?) */
+}
+
+void rs_init(void)
+{
+	set_intr_gate(0x24,rs1_interrupt);
+	set_intr_gate(0x23,rs2_interrupt);
+	init(tty_table[1].read_q.data);
+	init(tty_table[2].read_q.data);
+	outb(inb_p(0x21)&0xE7,0x21);
+}
+
+/*
+ * This routine gets called when tty_write has put something into
+ * the write_queue. It must check wheter the queue is empty, and
+ * set the interrupt register accordingly
+ *
+ *	void _rs_write(struct tty_struct * tty);
+ */
+void rs_write(struct tty_struct * tty)
+{
+	cli();
+	if (!EMPTY(tty->write_q))
+		outb(inb_p(tty->write_q.data+1)|0x02,tty->write_q.data+1);
+	sti();
+}
diff --git a/kernel/sys.c b/kernel/sys.c
new file mode 100644
index 0000000..f18ee7e
--- /dev/null
+++ b/kernel/sys.c
@@ -0,0 +1,216 @@
+#include <errno.h>
+
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+#include <sys/times.h>
+#include <sys/utsname.h>
+
+int sys_ftime()
+{
+	return -ENOSYS;
+}
+
+int sys_mknod()
+{
+	return -ENOSYS;
+}
+
+int sys_break()
+{
+	return -ENOSYS;
+}
+
+int sys_mount()
+{
+	return -ENOSYS;
+}
+
+int sys_umount()
+{
+	return -ENOSYS;
+}
+
+int sys_ustat(int dev,struct ustat * ubuf)
+{
+	return -1;
+}
+
+int sys_ptrace()
+{
+	return -ENOSYS;
+}
+
+int sys_stty()
+{
+	return -ENOSYS;
+}
+
+int sys_gtty()
+{
+	return -ENOSYS;
+}
+
+int sys_rename()
+{
+	return -ENOSYS;
+}
+
+int sys_prof()
+{
+	return -ENOSYS;
+}
+
+int sys_setgid(int gid)
+{
+	if (current->euid && current->uid)
+		if (current->gid==gid || current->sgid==gid)
+			current->egid=gid;
+		else
+			return -EPERM;
+	else
+		current->gid=current->egid=gid;
+	return 0;
+}
+
+int sys_acct()
+{
+	return -ENOSYS;
+}
+
+int sys_phys()
+{
+	return -ENOSYS;
+}
+
+int sys_lock()
+{
+	return -ENOSYS;
+}
+
+int sys_mpx()
+{
+	return -ENOSYS;
+}
+
+int sys_ulimit()
+{
+	return -ENOSYS;
+}
+
+int sys_time(long * tloc)
+{
+	int i;
+
+	i = CURRENT_TIME;
+	if (tloc) {
+		verify_area(tloc,4);
+		put_fs_long(i,(unsigned long *)tloc);
+	}
+	return i;
+}
+
+int sys_setuid(int uid)
+{
+	if (current->euid && current->uid)
+		if (uid==current->uid || current->suid==current->uid)
+			current->euid=uid;
+		else
+			return -EPERM;
+	else
+		current->euid=current->uid=uid;
+	return 0;
+}
+
+int sys_stime(long * tptr)
+{
+	if (current->euid && current->uid)
+		return -1;
+	startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ;
+	return 0;
+}
+
+int sys_times(struct tms * tbuf)
+{
+	if (!tbuf)
+		return jiffies;
+	verify_area(tbuf,sizeof *tbuf);
+	put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime);
+	put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime);
+	put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime);
+	put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime);
+	return jiffies;
+}
+
+int sys_brk(unsigned long end_data_seg)
+{
+	if (end_data_seg >= current->end_code &&
+	    end_data_seg < current->start_stack - 16384)
+		current->brk = end_data_seg;
+	return current->brk;
+}
+
+/*
+ * This needs some heave checking ...
+ * I just haven't get the stomach for it. I also don't fully
+ * understand sessions/pgrp etc. Let somebody who does explain it.
+ */
+int sys_setpgid(int pid, int pgid)
+{
+	int i;
+
+	if (!pid)
+		pid = current->pid;
+	if (!pgid)
+		pgid = pid;
+	for (i=0 ; i<NR_TASKS ; i++)
+		if (task[i] && task[i]->pid==pid) {
+			if (task[i]->leader)
+				return -EPERM;
+			if (task[i]->session != current->session)
+				return -EPERM;
+			task[i]->pgrp = pgid;
+			return 0;
+		}
+	return -ESRCH;
+}
+
+int sys_getpgrp(void)
+{
+	return current->pgrp;
+}
+
+int sys_setsid(void)
+{
+	if (current->uid && current->euid)
+		return -EPERM;
+	if (current->leader)
+		return -EPERM;
+	current->leader = 1;
+	current->session = current->pgrp = current->pid;
+	current->tty = -1;
+	return current->pgrp;
+}
+
+int sys_uname(struct utsname * name)
+{
+	static struct utsname thisname = {
+		"linux .0","nodename","release ","version ","machine "
+	};
+	int i;
+
+	if (!name) return -1;
+	verify_area(name,sizeof *name);
+	for(i=0;i<sizeof *name;i++)
+		put_fs_byte(((char *) &thisname)[i],i+(char *) name);
+	return (0);
+}
+
+int sys_umask(int mask)
+{
+	int old = current->umask;
+
+	current->umask = mask & 0777;
+	return (old);
+}
diff --git a/kernel/system_call.s b/kernel/system_call.s
new file mode 100644
index 0000000..df4f072
--- /dev/null
+++ b/kernel/system_call.s
@@ -0,0 +1,219 @@
+/*
+ *  system_call.s  contains the system-call low-level handling routines.
+ * This also contains the timer-interrupt handler, as some of the code is
+ * the same. The hd-interrupt is also here.
+ *
+ * NOTE: This code handles signal-recognition, which happens every time
+ * after a timer-interrupt and after each system call. Ordinary interrupts
+ * don't handle signal-recognition, as that would clutter them up totally
+ * unnecessarily.
+ *
+ * Stack layout in 'ret_from_system_call':
+ *
+ *	 0(%esp) - %eax
+ *	 4(%esp) - %ebx
+ *	 8(%esp) - %ecx
+ *	 C(%esp) - %edx
+ *	10(%esp) - %fs
+ *	14(%esp) - %es
+ *	18(%esp) - %ds
+ *	1C(%esp) - %eip
+ *	20(%esp) - %cs
+ *	24(%esp) - %eflags
+ *	28(%esp) - %oldesp
+ *	2C(%esp) - %oldss
+ */
+
+SIG_CHLD	= 17
+EAX		= 0x00
+EBX		= 0x04
+ECX		= 0x08
+EDX		= 0x0C
+FS		= 0x10
+ES		= 0x14
+DS		= 0x18
+EIP		= 0x1C
+CS		= 0x20
+EFLAGS		= 0x24
+OLDESP		= 0x28
+OLDSS		= 0x2C
+
+state	= 0		# these are offsets into the task-struct.
+counter	= 4
+priority = 8
+signal	= 12
+restorer = 16		# address of info-restorer
+sig_fn	= 20		# table of 32 signal addresses
+
+nr_system_calls = 67
+
+.globl _system_call,_sys_fork,_timer_interrupt,_hd_interrupt,_sys_execve
+
+.align 2
+bad_sys_call:
+	movl $-1,%eax
+	iret
+.align 2
+reschedule:
+	pushl $ret_from_sys_call
+	jmp _schedule
+.align 2
+_system_call:
+	cmpl $nr_system_calls-1,%eax
+	ja bad_sys_call
+	push %ds
+	push %es
+	push %fs
+	pushl %edx
+	pushl %ecx		# push %ebx,%ecx,%edx as parameters
+	pushl %ebx		# to the system call
+	movl $0x10,%edx		# set up ds,es to kernel space
+	mov %dx,%ds
+	mov %dx,%es
+	movl $0x17,%edx		# fs points to local data space
+	mov %dx,%fs
+	call _sys_call_table(,%eax,4)
+	pushl %eax
+	movl _current,%eax
+	cmpl $0,state(%eax)		# state
+	jne reschedule
+	cmpl $0,counter(%eax)		# counter
+	je reschedule
+ret_from_sys_call:
+	movl _current,%eax		# task[0] cannot have signals
+	cmpl _task,%eax
+	je 3f
+	movl CS(%esp),%ebx		# was old code segment supervisor
+	testl $3,%ebx			# mode? If so - don't check signals
+	je 3f
+	cmpw $0x17,OLDSS(%esp)		# was stack segment = 0x17 ?
+	jne 3f
+2:	movl signal(%eax),%ebx		# signals (bitmap, 32 signals)
+	bsfl %ebx,%ecx			# %ecx is signal nr, return if none
+	je 3f
+	btrl %ecx,%ebx			# clear it
+	movl %ebx,signal(%eax)
+	movl sig_fn(%eax,%ecx,4),%ebx	# %ebx is signal handler address
+	cmpl $1,%ebx
+	jb default_signal		# 0 is default signal handler - exit
+	je 2b				# 1 is ignore - find next signal
+	movl $0,sig_fn(%eax,%ecx,4)	# reset signal handler address
+	incl %ecx
+	xchgl %ebx,EIP(%esp)		# put new return address on stack
+	subl $28,OLDESP(%esp)
+	movl OLDESP(%esp),%edx		# push old return address on stack
+	pushl %eax			# but first check that it's ok.
+	pushl %ecx
+	pushl $28
+	pushl %edx
+	call _verify_area
+	popl %edx
+	addl $4,%esp
+	popl %ecx
+	popl %eax
+	movl restorer(%eax),%eax
+	movl %eax,%fs:(%edx)		# flag/reg restorer
+	movl %ecx,%fs:4(%edx)		# signal nr
+	movl EAX(%esp),%eax
+	movl %eax,%fs:8(%edx)		# old eax
+	movl ECX(%esp),%eax
+	movl %eax,%fs:12(%edx)		# old ecx
+	movl EDX(%esp),%eax
+	movl %eax,%fs:16(%edx)		# old edx
+	movl EFLAGS(%esp),%eax
+	movl %eax,%fs:20(%edx)		# old eflags
+	movl %ebx,%fs:24(%edx)		# old return addr
+3:	popl %eax
+	popl %ebx
+	popl %ecx
+	popl %edx
+	pop %fs
+	pop %es
+	pop %ds
+	iret
+
+default_signal:
+	incl %ecx
+	cmpl $SIG_CHLD,%ecx
+	je 2b
+	pushl %ecx
+	call _do_exit		# remember to set bit 7 when dumping core
+	addl $4,%esp
+	jmp 3b
+
+.align 2
+_timer_interrupt:
+	push %ds		# save ds,es and put kernel data space
+	push %es		# into them. %fs is used by _system_call
+	push %fs
+	pushl %edx		# we save %eax,%ecx,%edx as gcc doesn't
+	pushl %ecx		# save those across function calls. %ebx
+	pushl %ebx		# is saved as we use that in ret_sys_call
+	pushl %eax
+	movl $0x10,%eax
+	mov %ax,%ds
+	mov %ax,%es
+	movl $0x17,%eax
+	mov %ax,%fs
+	incl _jiffies
+	movb $0x20,%al		# EOI to interrupt controller #1
+	outb %al,$0x20
+	movl CS(%esp),%eax
+	andl $3,%eax		# %eax is CPL (0 or 3, 0=supervisor)
+	pushl %eax
+	call _do_timer		# 'do_timer(long CPL)' does everything from
+	addl $4,%esp		# task switching to accounting ...
+	jmp ret_from_sys_call
+
+.align 2
+_sys_execve:
+	lea EIP(%esp),%eax
+	pushl %eax
+	call _do_execve
+	addl $4,%esp
+	ret
+
+.align 2
+_sys_fork:
+	call _find_empty_process
+	testl %eax,%eax
+	js 1f
+	push %gs
+	pushl %esi
+	pushl %edi
+	pushl %ebp
+	pushl %eax
+	call _copy_process
+	addl $20,%esp
+1:	ret
+
+_hd_interrupt:
+	pushl %eax
+	pushl %ecx
+	pushl %edx
+	push %ds
+	push %es
+	push %fs
+	movl $0x10,%eax
+	mov %ax,%ds
+	mov %ax,%es
+	movl $0x17,%eax
+	mov %ax,%fs
+	movb $0x20,%al
+	outb %al,$0x20		# EOI to interrupt controller #1
+	jmp 1f			# give port chance to breathe
+1:	jmp 1f
+1:	outb %al,$0xA0		# same to controller #2
+	movl _do_hd,%eax
+	testl %eax,%eax
+	jne 1f
+	movl $_unexpected_hd_interrupt,%eax
+1:	call *%eax		# "interesting" way of handling intr.
+	pop %fs
+	pop %es
+	pop %ds
+	popl %edx
+	popl %ecx
+	popl %eax
+	iret
+
diff --git a/kernel/traps.c b/kernel/traps.c
new file mode 100644
index 0000000..b6e8bdb
--- /dev/null
+++ b/kernel/traps.c
@@ -0,0 +1,199 @@
+/*
+ * 'Traps.c' handles hardware traps and faults after we have saved some
+ * state in 'asm.s'. Currently mostly a debugging-aid, will be extended
+ * to mainly kill the offending process (probably by giving it a signal,
+ * but possibly by killing it outright if necessary).
+ */
+#include <string.h>
+
+#include <linux/head.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#define get_seg_byte(seg,addr) ({ \
+register char __res; \
+__asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \
+	:"=a" (__res):"0" (seg),"m" (*(addr))); \
+__res;})
+
+#define get_seg_long(seg,addr) ({ \
+register unsigned long __res; \
+__asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \
+	:"=a" (__res):"0" (seg),"m" (*(addr))); \
+__res;})
+
+#define _fs() ({ \
+register unsigned short __res; \
+__asm__("mov %%fs,%%ax":"=a" (__res):); \
+__res;})
+
+int do_exit(long code);
+
+void page_exception(void);
+
+void divide_error(void);
+void debug(void);
+void nmi(void);
+void int3(void);
+void overflow(void);
+void bounds(void);
+void invalid_op(void);
+void device_not_available(void);
+void double_fault(void);
+void coprocessor_segment_overrun(void);
+void invalid_TSS(void);
+void segment_not_present(void);
+void stack_segment(void);
+void general_protection(void);
+void page_fault(void);
+void coprocessor_error(void);
+void reserved(void);
+
+static void die(char * str,long esp_ptr,long nr)
+{
+	long * esp = (long *) esp_ptr;
+	int i;
+
+	printk("%s: %04x\n\r",str,nr&0xffff);
+	printk("EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n",
+		esp[1],esp[0],esp[2],esp[4],esp[3]);
+	printk("fs: %04x\n",_fs());
+	printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17));
+	if (esp[4] == 0x17) {
+		printk("Stack: ");
+		for (i=0;i<4;i++)
+			printk("%p ",get_seg_long(0x17,i+(long *)esp[3]));
+		printk("\n");
+	}
+	str(i);
+	printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i);
+	for(i=0;i<10;i++)
+		printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0])));
+	printk("\n\r");
+	do_exit(11);		/* play segment exception */
+}
+
+void do_double_fault(long esp, long error_code)
+{
+	die("double fault",esp,error_code);
+}
+
+void do_general_protection(long esp, long error_code)
+{
+	die("general protection",esp,error_code);
+}
+
+void do_divide_error(long esp, long error_code)
+{
+	die("divide error",esp,error_code);
+}
+
+void do_int3(long * esp, long error_code,
+		long fs,long es,long ds,
+		long ebp,long esi,long edi,
+		long edx,long ecx,long ebx,long eax)
+{
+	int tr;
+
+	__asm__("str %%ax":"=a" (tr):"0" (0));
+	printk("eax\t\tebx\t\tecx\t\tedx\n\r%8x\t%8x\t%8x\t%8x\n\r",
+		eax,ebx,ecx,edx);
+	printk("esi\t\tedi\t\tebp\t\tesp\n\r%8x\t%8x\t%8x\t%8x\n\r",
+		esi,edi,ebp,(long) esp);
+	printk("\n\rds\tes\tfs\ttr\n\r%4x\t%4x\t%4x\t%4x\n\r",
+		ds,es,fs,tr);
+	printk("EIP: %8x   CS: %4x  EFLAGS: %8x\n\r",esp[0],esp[1],esp[2]);
+}
+
+void do_nmi(long esp, long error_code)
+{
+	die("nmi",esp,error_code);
+}
+
+void do_debug(long esp, long error_code)
+{
+	die("debug",esp,error_code);
+}
+
+void do_overflow(long esp, long error_code)
+{
+	die("overflow",esp,error_code);
+}
+
+void do_bounds(long esp, long error_code)
+{
+	die("bounds",esp,error_code);
+}
+
+void do_invalid_op(long esp, long error_code)
+{
+	die("invalid operand",esp,error_code);
+}
+
+void do_device_not_available(long esp, long error_code)
+{
+	die("device not available",esp,error_code);
+}
+
+void do_coprocessor_segment_overrun(long esp, long error_code)
+{
+	die("coprocessor segment overrun",esp,error_code);
+}
+
+void do_invalid_TSS(long esp,long error_code)
+{
+	die("invalid TSS",esp,error_code);
+}
+
+void do_segment_not_present(long esp,long error_code)
+{
+	die("segment not present",esp,error_code);
+}
+
+void do_stack_segment(long esp,long error_code)
+{
+	die("stack segment",esp,error_code);
+}
+
+void do_coprocessor_error(long esp, long error_code)
+{
+	die("coprocessor error",esp,error_code);
+}
+
+void do_reserved(long esp, long error_code)
+{
+	die("reserved (15,17-31) error",esp,error_code);
+}
+
+void trap_init(void)
+{
+	int i;
+
+	set_trap_gate(0,&divide_error);
+	set_trap_gate(1,&debug);
+	set_trap_gate(2,&nmi);
+	set_system_gate(3,&int3);	/* int3-5 can be called from all */
+	set_system_gate(4,&overflow);
+	set_system_gate(5,&bounds);
+	set_trap_gate(6,&invalid_op);
+	set_trap_gate(7,&device_not_available);
+	set_trap_gate(8,&double_fault);
+	set_trap_gate(9,&coprocessor_segment_overrun);
+	set_trap_gate(10,&invalid_TSS);
+	set_trap_gate(11,&segment_not_present);
+	set_trap_gate(12,&stack_segment);
+	set_trap_gate(13,&general_protection);
+	set_trap_gate(14,&page_fault);
+	set_trap_gate(15,&reserved);
+	set_trap_gate(16,&coprocessor_error);
+	for (i=17;i<32;i++)
+		set_trap_gate(i,&reserved);
+/*	__asm__("movl $0x3ff000,%%eax\n\t"
+		"movl %%eax,%%db0\n\t"
+		"movl $0x000d0303,%%eax\n\t"
+		"movl %%eax,%%db7"
+		:::"ax");*/
+}
+
diff --git a/kernel/tty_io.c b/kernel/tty_io.c
new file mode 100644
index 0000000..68a390c
--- /dev/null
+++ b/kernel/tty_io.c
@@ -0,0 +1,306 @@
+/*
+ * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
+ * or rs-channels. It also implements echoing, cooked mode etc (well,
+ * not currently, but ...)
+ */
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+
+#define ALRMMASK (1<<(SIGALRM-1))
+
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#define _L_FLAG(tty,f)	((tty)->termios.c_lflag & f)
+#define _I_FLAG(tty,f)	((tty)->termios.c_iflag & f)
+#define _O_FLAG(tty,f)	((tty)->termios.c_oflag & f)
+
+#define L_CANON(tty)	_L_FLAG((tty),ICANON)
+#define L_ISIG(tty)	_L_FLAG((tty),ISIG)
+#define L_ECHO(tty)	_L_FLAG((tty),ECHO)
+#define L_ECHOE(tty)	_L_FLAG((tty),ECHOE)
+#define L_ECHOK(tty)	_L_FLAG((tty),ECHOK)
+#define L_ECHOCTL(tty)	_L_FLAG((tty),ECHOCTL)
+#define L_ECHOKE(tty)	_L_FLAG((tty),ECHOKE)
+
+#define I_UCLC(tty)	_I_FLAG((tty),IUCLC)
+#define I_NLCR(tty)	_I_FLAG((tty),INLCR)
+#define I_CRNL(tty)	_I_FLAG((tty),ICRNL)
+#define I_NOCR(tty)	_I_FLAG((tty),IGNCR)
+
+#define O_POST(tty)	_O_FLAG((tty),OPOST)
+#define O_NLCR(tty)	_O_FLAG((tty),ONLCR)
+#define O_CRNL(tty)	_O_FLAG((tty),OCRNL)
+#define O_NLRET(tty)	_O_FLAG((tty),ONLRET)
+#define O_LCUC(tty)	_O_FLAG((tty),OLCUC)
+
+struct tty_struct tty_table[] = {
+	{
+		{0,
+		OPOST|ONLCR,	/* change outgoing NL to CRNL */
+		0,
+		ICANON | ECHO | ECHOCTL | ECHOKE,
+		0,		/* console termio */
+		INIT_C_CC},
+		0,			/* initial pgrp */
+		0,			/* initial stopped */
+		con_write,
+		{0,0,0,0,""},		/* console read-queue */
+		{0,0,0,0,""},		/* console write-queue */
+		{0,0,0,0,""}		/* console secondary queue */
+	},{
+		{0, /*IGNCR*/
+		OPOST | ONLRET,		/* change outgoing NL to CR */
+		B2400 | CS8,
+		0,
+		0,
+		INIT_C_CC},
+		0,
+		0,
+		rs_write,
+		{0x3f8,0,0,0,""},		/* rs 1 */
+		{0x3f8,0,0,0,""},
+		{0,0,0,0,""}
+	},{
+		{0, /*IGNCR*/
+		OPOST | ONLRET,		/* change outgoing NL to CR */
+		B2400 | CS8,
+		0,
+		0,
+		INIT_C_CC},
+		0,
+		0,
+		rs_write,
+		{0x2f8,0,0,0,""},		/* rs 2 */
+		{0x2f8,0,0,0,""},
+		{0,0,0,0,""}
+	}
+};
+
+/*
+ * these are the tables used by the machine code handlers.
+ * you can implement pseudo-tty's or something by changing
+ * them. Currently not done.
+ */
+struct tty_queue * table_list[]={
+	&tty_table[0].read_q, &tty_table[0].write_q,
+	&tty_table[1].read_q, &tty_table[1].write_q,
+	&tty_table[2].read_q, &tty_table[2].write_q
+	};
+
+void tty_init(void)
+{
+	rs_init();
+	con_init();
+}
+
+void tty_intr(struct tty_struct * tty, int signal)
+{
+	int i;
+
+	if (tty->pgrp <= 0)
+		return;
+	for (i=0;i<NR_TASKS;i++)
+		if (task[i] && task[i]->pgrp==tty->pgrp)
+			task[i]->signal |= 1<<(signal-1);
+}
+
+static void sleep_if_empty(struct tty_queue * queue)
+{
+	cli();
+	while (!current->signal && EMPTY(*queue))
+		interruptible_sleep_on(&queue->proc_list);
+	sti();
+}
+
+static void sleep_if_full(struct tty_queue * queue)
+{
+	if (!FULL(*queue))
+		return;
+	cli();
+	while (!current->signal && LEFT(*queue)<128)
+		interruptible_sleep_on(&queue->proc_list);
+	sti();
+}
+
+void copy_to_cooked(struct tty_struct * tty)
+{
+	signed char c;
+
+	while (!EMPTY(tty->read_q) && !FULL(tty->secondary)) {
+		GETCH(tty->read_q,c);
+		if (c==13)
+			if (I_CRNL(tty))
+				c=10;
+			else if (I_NOCR(tty))
+				continue;
+			else ;
+		else if (c==10 && I_NLCR(tty))
+			c=13;
+		if (I_UCLC(tty))
+			c=tolower(c);
+		if (L_CANON(tty)) {
+			if (c==ERASE_CHAR(tty)) {
+				if (EMPTY(tty->secondary) ||
+				   (c=LAST(tty->secondary))==10 ||
+				   c==EOF_CHAR(tty))
+					continue;
+				if (L_ECHO(tty)) {
+					if (c<32)
+						PUTCH(127,tty->write_q);
+					PUTCH(127,tty->write_q);
+					tty->write(tty);
+				}
+				DEC(tty->secondary.head);
+				continue;
+			}
+			if (c==STOP_CHAR(tty)) {
+				tty->stopped=1;
+				continue;
+			}
+			if (c==START_CHAR(tty)) {
+				tty->stopped=0;
+				continue;
+			}
+		}
+		if (!L_ISIG(tty)) {
+			if (c==INTR_CHAR(tty)) {
+				tty_intr(tty,SIGINT);
+				continue;
+			}
+		}
+		if (c==10 || c==EOF_CHAR(tty))
+			tty->secondary.data++;
+		if (L_ECHO(tty)) {
+			if (c==10) {
+				PUTCH(10,tty->write_q);
+				PUTCH(13,tty->write_q);
+			} else if (c<32) {
+				if (L_ECHOCTL(tty)) {
+					PUTCH('^',tty->write_q);
+					PUTCH(c+64,tty->write_q);
+				}
+			} else
+				PUTCH(c,tty->write_q);
+			tty->write(tty);
+		}
+		PUTCH(c,tty->secondary);
+	}
+	wake_up(&tty->secondary.proc_list);
+}
+
+int tty_read(unsigned channel, char * buf, int nr)
+{
+	struct tty_struct * tty;
+	char c, * b=buf;
+	int minimum,time,flag=0;
+	long oldalarm;
+
+	if (channel>2 || nr<0) return -1;
+	tty = &tty_table[channel];
+	oldalarm = current->alarm;
+	time = (unsigned) 10*tty->termios.c_cc[VTIME];
+	minimum = (unsigned) tty->termios.c_cc[VMIN];
+	if (time && !minimum) {
+		minimum=1;
+		if (flag=(!oldalarm || time+jiffies<oldalarm))
+			current->alarm = time+jiffies;
+	}
+	if (minimum>nr)
+		minimum=nr;
+	while (nr>0) {
+		if (flag && (current->signal & ALRMMASK)) {
+			current->signal &= ~ALRMMASK;
+			break;
+		}
+		if (current->signal)
+			break;
+		if (EMPTY(tty->secondary) || (L_CANON(tty) &&
+		!tty->secondary.data && LEFT(tty->secondary)>20)) {
+			sleep_if_empty(&tty->secondary);
+			continue;
+		}
+		do {
+			GETCH(tty->secondary,c);
+			if (c==EOF_CHAR(tty) || c==10)
+				tty->secondary.data--;
+			if (c==EOF_CHAR(tty) && L_CANON(tty))
+				return (b-buf);
+			else {
+				put_fs_byte(c,b++);
+				if (!--nr)
+					break;
+			}
+		} while (nr>0 && !EMPTY(tty->secondary));
+		if (time && !L_CANON(tty))
+			if (flag=(!oldalarm || time+jiffies<oldalarm))
+				current->alarm = time+jiffies;
+			else
+				current->alarm = oldalarm;
+		if (L_CANON(tty)) {
+			if (b-buf)
+				break;
+		} else if (b-buf >= minimum)
+			break;
+	}
+	current->alarm = oldalarm;
+	if (current->signal && !(b-buf))
+		return -EINTR;
+	return (b-buf);
+}
+
+int tty_write(unsigned channel, char * buf, int nr)
+{
+	static cr_flag=0;
+	struct tty_struct * tty;
+	char c, *b=buf;
+
+	if (channel>2 || nr<0) return -1;
+	tty = channel + tty_table;
+	while (nr>0) {
+		sleep_if_full(&tty->write_q);
+		if (current->signal)
+			break;
+		while (nr>0 && !FULL(tty->write_q)) {
+			c=get_fs_byte(b);
+			if (O_POST(tty)) {
+				if (c=='\r' && O_CRNL(tty))
+					c='\n';
+				else if (c=='\n' && O_NLRET(tty))
+					c='\r';
+				if (c=='\n' && !cr_flag && O_NLCR(tty)) {
+					cr_flag = 1;
+					PUTCH(13,tty->write_q);
+					continue;
+				}
+				if (O_LCUC(tty))
+					c=toupper(c);
+			}
+			b++; nr--;
+			cr_flag = 0;
+			PUTCH(c,tty->write_q);
+		}
+		tty->write(tty);
+		if (nr>0)
+			schedule();
+	}
+	return (b-buf);
+}
+
+/*
+ * Jeh, sometimes I really like the 386.
+ * This routine is called from an interrupt,
+ * and there should be absolutely no problem
+ * with sleeping even in an interrupt (I hope).
+ * Of course, if somebody proves me wrong, I'll
+ * hate intel for all time :-). We'll have to
+ * be careful and see to reinstating the interrupt
+ * chips before calling this, though.
+ */
+void do_tty_interrupt(int tty)
+{
+	copy_to_cooked(tty_table+tty);
+}
diff --git a/kernel/vsprintf.c b/kernel/vsprintf.c
new file mode 100644
index 0000000..69c0578
--- /dev/null
+++ b/kernel/vsprintf.c
@@ -0,0 +1,227 @@
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+#include <stdarg.h>
+#include <string.h>
+
+/* we use this so that we can do without the ctype library */
+#define is_digit(c)	((c) >= '0' && (c) <= '9')
+
+static int skip_atoi(const char **s)
+{
+	int i=0;
+
+	while (is_digit(**s))
+		i = i*10 + *((*s)++) - '0';
+	return i;
+}
+
+#define ZEROPAD	1		/* pad with zero */
+#define SIGN	2		/* unsigned/signed long */
+#define PLUS	4		/* show plus */
+#define SPACE	8		/* space if plus */
+#define LEFT	16		/* left justified */
+#define SPECIAL	32		/* 0x */
+#define SMALL	64		/* use 'abcdef' instead of 'ABCDEF' */
+
+#define do_div(n,base) ({ \
+int __res; \
+__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \
+__res; })
+
+static char * number(char * str, int num, int base, int size, int precision
+	,int type)
+{
+	char c,sign,tmp[36];
+	const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	int i;
+
+	if (type&SMALL) digits="0123456789abcdefghijklmnopqrstuvwxyz";
+	if (type&LEFT) type &= ~ZEROPAD;
+	if (base<2 || base>36)
+		return 0;
+	c = (type & ZEROPAD) ? '0' : ' ' ;
+	if (type&SIGN && num<0) {
+		sign='-';
+		num = -num;
+	} else
+		sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0);
+	if (sign) size--;
+	if (type&SPECIAL)
+		if (base==16) size -= 2;
+		else if (base==8) size--;
+	i=0;
+	if (num==0)
+		tmp[i++]='0';
+	else while (num!=0)
+		tmp[i++]=digits[do_div(num,base)];
+	if (i>precision) precision=i;
+	size -= precision;
+	if (!(type&(ZEROPAD+LEFT)))
+		while(size-->0)
+			*str++ = ' ';
+	if (sign)
+		*str++ = sign;
+	if (type&SPECIAL)
+		if (base==8)
+			*str++ = '0';
+		else if (base==16) {
+			*str++ = '0';
+			*str++ = digits[33];
+		}
+	if (!(type&LEFT))
+		while(size-->0)
+			*str++ = c;
+	while(i<precision--)
+		*str++ = '0';
+	while(i-->0)
+		*str++ = tmp[i];
+	while(size-->0)
+		*str++ = ' ';
+	return str;
+}
+
+int vsprintf(char *buf, const char *fmt, va_list args)
+{
+	int len;
+	int i;
+	char * str;
+	char *s;
+	int *ip;
+
+	int flags;		/* flags to number() */
+
+	int field_width;	/* width of output field */
+	int precision;		/* min. # of digits for integers; max
+				   number of chars for from string */
+	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
+
+	for (str=buf ; *fmt ; ++fmt) {
+		if (*fmt != '%') {
+			*str++ = *fmt;
+			continue;
+		}
+			
+		/* process flags */
+		flags = 0;
+		repeat:
+			++fmt;		/* this also skips first '%' */
+			switch (*fmt) {
+				case '-': flags |= LEFT; goto repeat;
+				case '+': flags |= PLUS; goto repeat;
+				case ' ': flags |= SPACE; goto repeat;
+				case '#': flags |= SPECIAL; goto repeat;
+				case '0': flags |= ZEROPAD; goto repeat;
+				}
+		
+		/* get field width */
+		field_width = -1;
+		if (is_digit(*fmt))
+			field_width = skip_atoi(&fmt);
+		else if (*fmt == '*') {
+			/* it's the next argument */
+			field_width = va_arg(args, int);
+			if (field_width < 0) {
+				field_width = -field_width;
+				flags |= LEFT;
+			}
+		}
+
+		/* get the precision */
+		precision = -1;
+		if (*fmt == '.') {
+			++fmt;	
+			if (is_digit(*fmt))
+				precision = skip_atoi(&fmt);
+			else if (*fmt == '*') {
+				/* it's the next argument */
+				precision = va_arg(args, int);
+			}
+			if (precision < 0)
+				precision = 0;
+		}
+
+		/* get the conversion qualifier */
+		qualifier = -1;
+		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
+			qualifier = *fmt;
+			++fmt;
+		}
+
+		switch (*fmt) {
+		case 'c':
+			if (!(flags & LEFT))
+				while (--field_width > 0)
+					*str++ = ' ';
+			*str++ = (unsigned char) va_arg(args, int);
+			while (--field_width > 0)
+				*str++ = ' ';
+			break;
+
+		case 's':
+			s = va_arg(args, char *);
+			len = strlen(s);
+			if (precision < 0)
+				precision = len;
+			else if (len > precision)
+				len = precision;
+
+			if (!(flags & LEFT))
+				while (len < field_width--)
+					*str++ = ' ';
+			for (i = 0; i < len; ++i)
+				*str++ = *s++;
+			while (len < field_width--)
+				*str++ = ' ';
+			break;
+
+		case 'o':
+			str = number(str, va_arg(args, unsigned long), 8,
+				field_width, precision, flags);
+			break;
+
+		case 'p':
+			if (field_width == -1) {
+				field_width = 8;
+				flags |= ZEROPAD;
+			}
+			str = number(str,
+				(unsigned long) va_arg(args, void *), 16,
+				field_width, precision, flags);
+			break;
+
+		case 'x':
+			flags |= SMALL;
+		case 'X':
+			str = number(str, va_arg(args, unsigned long), 16,
+				field_width, precision, flags);
+			break;
+
+		case 'd':
+		case 'i':
+			flags |= SIGN;
+		case 'u':
+			str = number(str, va_arg(args, unsigned long), 10,
+				field_width, precision, flags);
+			break;
+
+		case 'n':
+			ip = va_arg(args, int *);
+			*ip = (str - buf);
+			break;
+
+		default:
+			if (*fmt != '%')
+				*str++ = '%';
+			if (*fmt)
+				*str++ = *fmt;
+			else
+				--fmt;
+			break;
+		}
+	}
+	*str = '\0';
+	return str-buf;
+}
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 0000000..a06698d
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,44 @@
+#
+# Makefile for some libs needed in the kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+
+AR	=gar
+AS	=gas
+LD	=gld
+LDFLAGS	=-s -x
+CC	=gcc
+CFLAGS	=-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs \
+	-finline-functions -mstring-insns -nostdinc -I../include
+CPP	=gcc -E -nostdinc -I../include
+
+.c.s:
+	$(CC) $(CFLAGS) \
+	-S -o $*.s $<
+.s.o:
+	$(AS) -c -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) \
+	-c -o $*.o $<
+
+OBJS  = ctype.o _exit.o open.o close.o errno.o write.o dup.o setsid.o \
+	execve.o wait.o string.o
+
+lib.a: $(OBJS)
+	$(AR) rcs lib.a $(OBJS)
+	sync
+
+clean:
+	rm -f core *.o *.a tmp_make
+	for i in *.c;do rm -f `basename $$i .c`.s;done
+
+dep:
+	sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
+	(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
+		$(CPP) -M $$i;done) >> tmp_make
+	cp tmp_make Makefile
+
+### Dependencies:
diff --git a/lib/_exit.c b/lib/_exit.c
new file mode 100644
index 0000000..44a74e1
--- /dev/null
+++ b/lib/_exit.c
@@ -0,0 +1,7 @@
+#define __LIBRARY__
+#include <unistd.h>
+
+volatile void _exit(int exit_code)
+{
+	__asm__("int $0x80"::"a" (__NR_exit),"b" (exit_code));
+}
diff --git a/lib/close.c b/lib/close.c
new file mode 100644
index 0000000..182d7eb
--- /dev/null
+++ b/lib/close.c
@@ -0,0 +1,4 @@
+#define __LIBRARY__
+#include <unistd.h>
+
+_syscall1(int,close,int,fd)
diff --git a/lib/ctype.c b/lib/ctype.c
new file mode 100644
index 0000000..bf58aac
--- /dev/null
+++ b/lib/ctype.c
@@ -0,0 +1,29 @@
+#include <ctype.h>
+
+char _ctmp;
+unsigned char _ctype[] = {0x00,			/* EOF */
+_C,_C,_C,_C,_C,_C,_C,_C,			/* 0-7 */
+_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C,		/* 8-15 */
+_C,_C,_C,_C,_C,_C,_C,_C,			/* 16-23 */
+_C,_C,_C,_C,_C,_C,_C,_C,			/* 24-31 */
+_S|_SP,_P,_P,_P,_P,_P,_P,_P,			/* 32-39 */
+_P,_P,_P,_P,_P,_P,_P,_P,			/* 40-47 */
+_D,_D,_D,_D,_D,_D,_D,_D,			/* 48-55 */
+_D,_D,_P,_P,_P,_P,_P,_P,			/* 56-63 */
+_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U,	/* 64-71 */
+_U,_U,_U,_U,_U,_U,_U,_U,			/* 72-79 */
+_U,_U,_U,_U,_U,_U,_U,_U,			/* 80-87 */
+_U,_U,_U,_P,_P,_P,_P,_P,			/* 88-95 */
+_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L,	/* 96-103 */
+_L,_L,_L,_L,_L,_L,_L,_L,			/* 104-111 */
+_L,_L,_L,_L,_L,_L,_L,_L,			/* 112-119 */
+_L,_L,_L,_P,_P,_P,_P,_C,			/* 120-127 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 128-143 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 144-159 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 160-175 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 176-191 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 192-207 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 208-223 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 224-239 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};		/* 240-255 */
+
diff --git a/lib/dup.c b/lib/dup.c
new file mode 100644
index 0000000..03bd5d0
--- /dev/null
+++ b/lib/dup.c
@@ -0,0 +1,4 @@
+#define __LIBRARY__
+#include <unistd.h>
+
+_syscall1(int,dup,int,fd)
diff --git a/lib/errno.c b/lib/errno.c
new file mode 100644
index 0000000..6e7bb62
--- /dev/null
+++ b/lib/errno.c
@@ -0,0 +1 @@
+int errno;
diff --git a/lib/execve.c b/lib/execve.c
new file mode 100644
index 0000000..03772e3
--- /dev/null
+++ b/lib/execve.c
@@ -0,0 +1,4 @@
+#define __LIBRARY__
+#include <unistd.h>
+
+_syscall3(int,execve,const char *,file,char **,argv,char **,envp)
diff --git a/lib/open.c b/lib/open.c
new file mode 100644
index 0000000..057039c
--- /dev/null
+++ b/lib/open.c
@@ -0,0 +1,19 @@
+#define __LIBRARY__
+#include <unistd.h>
+#include <stdarg.h>
+
+int open(const char * filename, int flag, ...)
+{
+	register int res;
+	va_list arg;
+
+	va_start(arg,flag);
+	__asm__("int $0x80"
+		:"=a" (res)
+		:"0" (__NR_open),"b" (filename),"c" (flag),
+		"d" (va_arg(arg,int)));
+	if (res>=0)
+		return res;
+	errno = -res;
+	return -1;
+}
diff --git a/lib/setsid.c b/lib/setsid.c
new file mode 100644
index 0000000..730abf0
--- /dev/null
+++ b/lib/setsid.c
@@ -0,0 +1,4 @@
+#define __LIBRARY__
+#include <unistd.h>
+
+_syscall0(pid_t,setsid)
diff --git a/lib/string.c b/lib/string.c
new file mode 100644
index 0000000..f6befd9
--- /dev/null
+++ b/lib/string.c
@@ -0,0 +1,8 @@
+#ifndef __GNUC__
+#error I want gcc!
+#endif
+
+#define extern
+#define inline
+#define __LIBRARY__
+#include <string.h>
diff --git a/lib/wait.c b/lib/wait.c
new file mode 100644
index 0000000..a14555c
--- /dev/null
+++ b/lib/wait.c
@@ -0,0 +1,10 @@
+#define __LIBRARY__
+#include <unistd.h>
+#include <sys/wait.h>
+
+_syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+
+pid_t wait(int * wait_stat)
+{
+	return waitpid(-1,wait_stat,0);
+}
diff --git a/lib/write.c b/lib/write.c
new file mode 100644
index 0000000..2613f17
--- /dev/null
+++ b/lib/write.c
@@ -0,0 +1,4 @@
+#define __LIBRARY__
+#include <unistd.h>
+
+_syscall3(int,write,int,fd,const char *,buf,off_t,count)
diff --git a/mm/Makefile b/mm/Makefile
new file mode 100644
index 0000000..cee1f09
--- /dev/null
+++ b/mm/Makefile
@@ -0,0 +1,37 @@
+CC	=gcc
+CFLAGS	=-O -Wall -fstrength-reduce -fcombine-regs -fomit-frame-pointer \
+	-finline-functions -nostdinc -I../include
+AS	=gas
+AR	=gar
+LD	=gld
+CPP	=gcc -E -nostdinc -I../include
+
+.c.o:
+	$(CC) $(CFLAGS) \
+	-c -o $*.o $<
+.s.o:
+	$(AS) -o $*.o $<
+.c.s:
+	$(CC) $(CFLAGS) \
+	-S -o $*.s $<
+
+OBJS	= memory.o page.o
+
+all: mm.o
+
+mm.o: $(OBJS)
+	$(LD) -r -o mm.o $(OBJS)
+
+clean:
+	rm -f core *.o *.a tmp_make
+	for i in *.c;do rm -f `basename $$i .c`.s;done
+
+dep:
+	sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
+	(for i in *.c;do $(CPP) -M $$i;done) >> tmp_make
+	cp tmp_make Makefile
+
+### Dependencies:
+memory.o : memory.c ../include/signal.h ../include/sys/types.h \
+  ../include/linux/config.h ../include/linux/head.h ../include/linux/kernel.h \
+  ../include/asm/system.h 
diff --git a/mm/memory.c b/mm/memory.c
new file mode 100644
index 0000000..7cdcfb6
--- /dev/null
+++ b/mm/memory.c
@@ -0,0 +1,264 @@
+#include <signal.h>
+
+#include <linux/config.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+
+int do_exit(long code);
+
+#define invalidate() \
+__asm__("movl %%eax,%%cr3"::"a" (0))
+
+#if (BUFFER_END < 0x100000)
+#define LOW_MEM 0x100000
+#else
+#define LOW_MEM BUFFER_END
+#endif
+
+/* these are not to be changed - thay are calculated from the above */
+#define PAGING_MEMORY (HIGH_MEMORY - LOW_MEM)
+#define PAGING_PAGES (PAGING_MEMORY/4096)
+#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
+
+#if (PAGING_PAGES < 10)
+#error "Won't work"
+#endif
+
+#define copy_page(from,to) \
+__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
+
+static unsigned short mem_map [ PAGING_PAGES ] = {0,};
+
+/*
+ * Get physical address of first (actually last :-) free page, and mark it
+ * used. If no free pages left, return 0.
+ */
+unsigned long get_free_page(void)
+{
+register unsigned long __res asm("ax");
+
+__asm__("std ; repne ; scasw\n\t"
+	"jne 1f\n\t"
+	"movw $1,2(%%edi)\n\t"
+	"sall $12,%%ecx\n\t"
+	"movl %%ecx,%%edx\n\t"
+	"addl %2,%%edx\n\t"
+	"movl $1024,%%ecx\n\t"
+	"leal 4092(%%edx),%%edi\n\t"
+	"rep ; stosl\n\t"
+	"movl %%edx,%%eax\n"
+	"1:"
+	:"=a" (__res)
+	:"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
+	"D" (mem_map+PAGING_PAGES-1)
+	:"di","cx","dx");
+return __res;
+}
+
+/*
+ * Free a page of memory at physical address 'addr'. Used by
+ * 'free_page_tables()'
+ */
+void free_page(unsigned long addr)
+{
+	if (addr<LOW_MEM) return;
+	if (addr>HIGH_MEMORY)
+		panic("trying to free nonexistent page");
+	addr -= LOW_MEM;
+	addr >>= 12;
+	if (mem_map[addr]--) return;
+	mem_map[addr]=0;
+	panic("trying to free free page");
+}
+
+/*
+ * This function frees a continuos block of page tables, as needed
+ * by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks.
+ */
+int free_page_tables(unsigned long from,unsigned long size)
+{
+	unsigned long *pg_table;
+	unsigned long * dir, nr;
+
+	if (from & 0x3fffff)
+		panic("free_page_tables called with wrong alignment");
+	if (!from)
+		panic("Trying to free up swapper memory space");
+	size = (size + 0x3fffff) >> 22;
+	dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
+	for ( ; size-->0 ; dir++) {
+		if (!(1 & *dir))
+			continue;
+		pg_table = (unsigned long *) (0xfffff000 & *dir);
+		for (nr=0 ; nr<1024 ; nr++) {
+			if (1 & *pg_table)
+				free_page(0xfffff000 & *pg_table);
+			*pg_table = 0;
+			pg_table++;
+		}
+		free_page(0xfffff000 & *dir);
+		*dir = 0;
+	}
+	invalidate();
+	return 0;
+}
+
+/*
+ *  Well, here is one of the most complicated functions in mm. It
+ * copies a range of linerar addresses by copying only the pages.
+ * Let's hope this is bug-free, 'cause this one I don't want to debug :-)
+ *
+ * Note! We don't copy just any chunks of memory - addresses have to
+ * be divisible by 4Mb (one page-directory entry), as this makes the
+ * function easier. It's used only by fork anyway.
+ *
+ * NOTE 2!! When from==0 we are copying kernel space for the first
+ * fork(). Then we DONT want to copy a full page-directory entry, as
+ * that would lead to some serious memory waste - we just copy the
+ * first 160 pages - 640kB. Even that is more than we need, but it
+ * doesn't take any more memory - we don't copy-on-write in the low
+ * 1 Mb-range, so the pages can be shared with the kernel. Thus the
+ * special case for nr=xxxx.
+ */
+int copy_page_tables(unsigned long from,unsigned long to,long size)
+{
+	unsigned long * from_page_table;
+	unsigned long * to_page_table;
+	unsigned long this_page;
+	unsigned long * from_dir, * to_dir;
+	unsigned long nr;
+
+	if ((from&0x3fffff) || (to&0x3fffff))
+		panic("copy_page_tables called with wrong alignment");
+	from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
+	to_dir = (unsigned long *) ((to>>20) & 0xffc);
+	size = ((unsigned) (size+0x3fffff)) >> 22;
+	for( ; size-->0 ; from_dir++,to_dir++) {
+		if (1 & *to_dir)
+			panic("copy_page_tables: already exist");
+		if (!(1 & *from_dir))
+			continue;
+		from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
+		if (!(to_page_table = (unsigned long *) get_free_page()))
+			return -1;	/* Out of memory, see freeing */
+		*to_dir = ((unsigned long) to_page_table) | 7;
+		nr = (from==0)?0xA0:1024;
+		for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
+			this_page = *from_page_table;
+			if (!(1 & this_page))
+				continue;
+			this_page &= ~2;
+			*to_page_table = this_page;
+			if (this_page > LOW_MEM) {
+				*from_page_table = this_page;
+				this_page -= LOW_MEM;
+				this_page >>= 12;
+				mem_map[this_page]++;
+			}
+		}
+	}
+	invalidate();
+	return 0;
+}
+
+/*
+ * This function puts a page in memory at the wanted address.
+ * It returns the physical address of the page gotten, 0 if
+ * out of memory (either when trying to access page-table or
+ * page.)
+ */
+unsigned long put_page(unsigned long page,unsigned long address)
+{
+	unsigned long tmp, *page_table;
+
+/* NOTE !!! This uses the fact that _pg_dir=0 */
+
+	if (page < LOW_MEM || page > HIGH_MEMORY)
+		printk("Trying to put page %p at %p\n",page,address);
+	if (mem_map[(page-LOW_MEM)>>12] != 1)
+		printk("mem_map disagrees with %p at %p\n",page,address);
+	page_table = (unsigned long *) ((address>>20) & 0xffc);
+	if ((*page_table)&1)
+		page_table = (unsigned long *) (0xfffff000 & *page_table);
+	else {
+		if (!(tmp=get_free_page()))
+			return 0;
+		*page_table = tmp|7;
+		page_table = (unsigned long *) tmp;
+	}
+	page_table[(address>>12) & 0x3ff] = page | 7;
+	return page;
+}
+
+void un_wp_page(unsigned long * table_entry)
+{
+	unsigned long old_page,new_page;
+
+	old_page = 0xfffff000 & *table_entry;
+	if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
+		*table_entry |= 2;
+		return;
+	}
+	if (!(new_page=get_free_page()))
+		do_exit(SIGSEGV);
+	if (old_page >= LOW_MEM)
+		mem_map[MAP_NR(old_page)]--;
+	*table_entry = new_page | 7;
+	copy_page(old_page,new_page);
+}	
+
+/*
+ * This routine handles present pages, when users try to write
+ * to a shared page. It is done by copying the page to a new address
+ * and decrementing the shared-page counter for the old page.
+ */
+void do_wp_page(unsigned long error_code,unsigned long address)
+{
+	un_wp_page((unsigned long *)
+		(((address>>10) & 0xffc) + (0xfffff000 &
+		*((unsigned long *) ((address>>20) &0xffc)))));
+
+}
+
+void write_verify(unsigned long address)
+{
+	unsigned long page;
+
+	if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
+		return;
+	page &= 0xfffff000;
+	page += ((address>>10) & 0xffc);
+	if ((3 & *(unsigned long *) page) == 1)  /* non-writeable, present */
+		un_wp_page((unsigned long *) page);
+	return;
+}
+
+void do_no_page(unsigned long error_code,unsigned long address)
+{
+	unsigned long tmp;
+
+	if (tmp=get_free_page())
+		if (put_page(tmp,address))
+			return;
+	do_exit(SIGSEGV);
+}
+
+void calc_mem(void)
+{
+	int i,j,k,free=0;
+	long * pg_tbl;
+
+	for(i=0 ; i<PAGING_PAGES ; i++)
+		if (!mem_map[i]) free++;
+	printk("%d pages free (of %d)\n\r",free,PAGING_PAGES);
+	for(i=2 ; i<1024 ; i++) {
+		if (1&pg_dir[i]) {
+			pg_tbl=(long *) (0xfffff000 & pg_dir[i]);
+			for(j=k=0 ; j<1024 ; j++)
+				if (pg_tbl[j]&1)
+					k++;
+			printk("Pg-dir[%d] uses %d pages\n",i,k);
+		}
+	}
+}
diff --git a/mm/page.s b/mm/page.s
new file mode 100644
index 0000000..27488c2
--- /dev/null
+++ b/mm/page.s
@@ -0,0 +1,34 @@
+/*
+ * page.s contains the low-level page-exception code.
+ * the real work is done in mm.c
+ */
+
+.globl _page_fault
+
+_page_fault:
+	xchgl %eax,(%esp)
+	pushl %ecx
+	pushl %edx
+	push %ds
+	push %es
+	push %fs
+	movl $0x10,%edx
+	mov %dx,%ds
+	mov %dx,%es
+	mov %dx,%fs
+	movl %cr2,%edx
+	pushl %edx
+	pushl %eax
+	testl $1,%eax
+	jne 1f
+	call _do_no_page
+	jmp 2f
+1:	call _do_wp_page
+2:	addl $8,%esp
+	pop %fs
+	pop %es
+	pop %ds
+	popl %edx
+	popl %ecx
+	popl %eax
+	iret
diff --git a/tools/build.c b/tools/build.c
new file mode 100644
index 0000000..6afe58c
--- /dev/null
+++ b/tools/build.c
@@ -0,0 +1,68 @@
+#include <stdio.h>	/* fprintf */
+#include <stdlib.h>	/* contains exit */
+#include <sys/types.h>	/* unistd.h needs this */
+#include <unistd.h>	/* contains read/write */
+#include <fcntl.h>
+
+#define MINIX_HEADER 32
+#define GCC_HEADER 1024
+
+void die(char * str)
+{
+	fprintf(stderr,"%s\n",str);
+	exit(1);
+}
+
+void usage(void)
+{
+	die("Usage: build boot system [> image]");
+}
+
+int main(int argc, char ** argv)
+{
+	int i,c,id;
+	char buf[1024];
+
+	if (argc != 3)
+		usage();
+	for (i=0;i<sizeof buf; i++) buf[i]=0;
+	if ((id=open(argv[1],O_RDONLY,0))<0)
+		die("Unable to open 'boot'");
+	if (read(id,buf,MINIX_HEADER) != MINIX_HEADER)
+		die("Unable to read header of 'boot'");
+	if (((long *) buf)[0]!=0x04100301)
+		die("Non-Minix header of 'boot'");
+	if (((long *) buf)[1]!=MINIX_HEADER)
+		die("Non-Minix header of 'boot'");
+	if (((long *) buf)[3]!=0)
+		die("Illegal data segment in 'boot'");
+	if (((long *) buf)[4]!=0)
+		die("Illegal bss in 'boot'");
+	if (((long *) buf)[5] != 0)
+		die("Non-Minix header of 'boot'");
+	if (((long *) buf)[7] != 0)
+		die("Illegal symbol table in 'boot'");
+	i=read(id,buf,sizeof buf);
+	fprintf(stderr,"Boot sector %d bytes.\n",i);
+	if (i>510)
+		die("Boot block may not exceed 510 bytes");
+	buf[510]=0x55;
+	buf[511]=0xAA;
+	i=write(1,buf,512);
+	if (i!=512)
+		die("Write call failed");
+	close (id);
+	
+	if ((id=open(argv[2],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 (((long *) buf)[5] != 0)
+		die("Non-GCC header of 'system'");
+	for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )
+		if (write(1,buf,c)!=c)
+			die("Write call failed");
+	close(id);
+	fprintf(stderr,"System %d bytes.\n",i);
+	return(0);
+}