diff --git a/Kernel/arch/x86/errors.c b/Kernel/arch/x86/errors.c
index d87b1c627f85261e7e9114089296028359aaf63b..2f94972cc93667fe7e176a5a39be419b7d85f4a7 100644
--- a/Kernel/arch/x86/errors.c
+++ b/Kernel/arch/x86/errors.c
@@ -17,6 +17,12 @@ extern void Threads_Dump();
 void	Error_Backtrace(Uint eip, Uint ebp);
 
 // === CODE ===
+void __stack_chk_fail()
+{
+	Panic("FATAL ERROR: Stack Check Failed\n");
+	for(;;);
+}
+
 /**
  * \fn void ErrorHandler(tRegs *Regs)
  * \brief General Error Handler
diff --git a/Kernel/arch/x86/proc.c b/Kernel/arch/x86/proc.c
index 5301401eefc84e618d024aca242abc495f2d8fe9..d7f460fa24b4c370d8e57c7580f6af9a3a5c350f 100644
--- a/Kernel/arch/x86/proc.c
+++ b/Kernel/arch/x86/proc.c
@@ -560,10 +560,11 @@ void Proc_Scheduler(int CPU)
 	__asm__ __volatile__ ("mov %0, %%cr3"::"a"(gCurrentThread->MemState.CR3));
 	// Switch threads
 	__asm__ __volatile__ (
-		"mov %1, %%esp\n\t"
-		"mov %2, %%ebp\n\t"
-		"jmp *%3" : :
+		"mov %1, %%esp\n\t"	// Restore ESP
+		"mov %2, %%ebp\n\t"	// and EBP
+		"jmp *%3" : :	// And return to where we saved state (Proc_Clone or Proc_Scheduler)
 		"a"(SWITCH_MAGIC), "b"(gCurrentThread->SavedState.ESP),
-		"d"(gCurrentThread->SavedState.EBP), "c"(gCurrentThread->SavedState.EIP));
+		"d"(gCurrentThread->SavedState.EBP), "c"(gCurrentThread->SavedState.EIP)
+		);
 	for(;;);	// Shouldn't reach here
 }
diff --git a/Kernel/drv/vterm.c b/Kernel/drv/vterm.c
index 09f85ff6c658dd0b3f32129923b3efaf41f05994..87f71edea340387052d29ade81b501b27ba182ee 100644
--- a/Kernel/drv/vterm.c
+++ b/Kernel/drv/vterm.c
@@ -16,8 +16,8 @@
 #define MAX_INPUT_CHARS32	64
 #define MAX_INPUT_CHARS8	(MAX_INPUT_CHARS32*4)
 #define VT_SCROLLBACK	1	// 2 Screens of text
-#define DEFAULT_OUTPUT	"VGA"
-//#define DEFAULT_OUTPUT	"BochsGA"
+//#define DEFAULT_OUTPUT	"VGA"
+#define DEFAULT_OUTPUT	"BochsGA"
 #define DEFAULT_INPUT	"PS2Keyboard"
 #define	DEFAULT_WIDTH	80
 #define	DEFAULT_HEIGHT	25
@@ -197,7 +197,7 @@ int VT_Install(char **Arguments)
 void VT_InitOutput()
 {
 	giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE);
-	LOG("giVT_OutputDevHandle = %x\n", giVT_OutputDevHandle);
+	Log("giVT_OutputDevHandle = %x\n", giVT_OutputDevHandle);
 	VT_SetTerminal( 0 );
 }
 
@@ -896,7 +896,7 @@ void VT_int_ChangeMode(tVTerm *Term, int NewMode)
 Uint8	*VT_Font_GetChar(Uint32 Codepoint);
 
 // === GLOBALS ===
-int	giVT_CharWidth = FONT_WIDTH;
+int	giVT_CharWidth = FONT_WIDTH+1;
 int	giVT_CharHeight = FONT_HEIGHT;
 
 // === CODE ===
@@ -909,13 +909,14 @@ void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Pitch, Uint32 BGC, Uint3
 	Uint8	*font;
 	Uint32	*buf = Buffer;
 	 int	x, y;
+	
 	font = VT_Font_GetChar(Codepoint);
 	
 	for(y = 0; y < FONT_HEIGHT; y ++)
 	{
 		for(x = 0; x < FONT_WIDTH; x ++)
 		{
-			if(*font & (1 << (FONT_WIDTH-x)))
+			if(*font & (1 << (FONT_WIDTH-x-1)))
 				buf[x] = FGC;
 			else
 				buf[x] = BGC;
diff --git a/Kernel/modules.c b/Kernel/modules.c
index b1d858c7d0d2672a39fe61e8460b815239e06759..a4e639b8308b22f75af05180c261b9319b36239d 100644
--- a/Kernel/modules.c
+++ b/Kernel/modules.c
@@ -46,6 +46,7 @@ int Modules_LoadBuiltins()
 					if(strcmp(deps[j], gKernelModules[k].Name) == 0)
 						break;
 				}
+				Log("%s requires %s\n", gKernelModules[i].Name, deps[j]);
 				if(k == giNumBuiltinModules) {
 					Warning("Unable to find dependency '%s' for '%s' in kernel",
 						deps[j], gKernelModules[i].Name);
@@ -64,6 +65,8 @@ int Modules_LoadBuiltins()
 		for( i = 0; i < giNumBuiltinModules; i++ )
 		{
 			if( baIsLoaded[i] )	continue;	// Ignore already loaded modules
+		
+			deps = gKernelModules[i].Dependencies;
 			
 			if( deps )
 			{
@@ -75,6 +78,7 @@ int Modules_LoadBuiltins()
 					}
 					// `k` is assumed to be less than `giNumBuiltinModules`
 					
+					Log("baIsLoaded[%i(%s)] = %i\n", k, deps[j], baIsLoaded[k]);
 					// If a dependency failed, skip and mark as failed
 					if( baIsLoaded[k] == -1 ) {
 						baIsLoaded[i] = -1;
diff --git a/Kernel/vfs/fs/ext2.c b/Kernel/vfs/fs/ext2.c
deleted file mode 100644
index 0b60e5427930921cc559588ee7e433d0d88d6844..0000000000000000000000000000000000000000
--- a/Kernel/vfs/fs/ext2.c
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
- * Acess OS
- * Ext2 Driver Version 1
- */
-/**
- * \file fs/ext2.c
- * \brief Second Extended Filesystem Driver
- * \todo Implement file full write support
- */
-#define DEBUG	1
-#define VERBOSE	0
-#include <common.h>
-#include <vfs.h>
-#include <modules.h>
-#include "fs_ext2.h"
-
-#define EXT2_UPDATE_WRITEBACK	1
-
-// === STRUCTURES ===
-typedef struct {
-	 int	FD;
-	 int	CacheID;
-	tVFS_Node	RootNode;
-	
-	tExt2_SuperBlock	SuperBlock;
-	 int	BlockSize;
-	 
-	 int	GroupCount;
-	tExt2_Group		Groups[];
-} tExt2_Disk;
-
-// === PROTOTYPES ===
- int	Ext2_Install(char **Arguments);
-// Interface Functions
-tVFS_Node	*Ext2_InitDevice(char *Device, char **Options);
-void		Ext2_Unmount(tVFS_Node *Node);
-Uint64		Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
-Uint64		Ext2_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
-void		Ext2_CloseFile(tVFS_Node *Node);
-char		*Ext2_ReadDir(tVFS_Node *Node, int Pos);
-tVFS_Node	*Ext2_FindDir(tVFS_Node *Node, char *FileName);
- int		Ext2_MkNod(tVFS_Node *Node, char *Name, Uint Flags);
-// Internal Helpers
- int		Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode);
-tVFS_Node	*Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId, char *Name);
- int		Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode);
-Uint64		Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);
-Uint32		Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent);
-Uint32		Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock);
-void		Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);
-
-// === SEMI-GLOBALS ===
-MODULE_DEFINE(0, 0x5B /*v0.90*/, EXT2, Ext2_Install, NULL);
-tExt2_Disk	gExt2_disks[6];
- int	giExt2_count = 0;
-tVFS_Driver	gExt2_FSInfo = {
-	"ext2", 0, Ext2_InitDevice, Ext2_Unmount, NULL
-	};
-
-// === CODE ===
-
-/**
- * \fn int Ext2_Install(char **Arguments)
- * \brief Install the Ext2 Filesystem Driver
- */
-int Ext2_Install(char **Arguments)
-{
-	VFS_AddDriver( &gExt2_FSInfo );
-	return 1;
-}
-
-/**
- \fn tVFS_Node *Ext2_InitDevice(char *Device, char **Options)
- \brief Initializes a device to be read by by the driver
- \param Device	String - Device to read from
- \param Options	NULL Terminated array of option strings
- \return Root Node
-*/
-tVFS_Node *Ext2_InitDevice(char *Device, char **Options)
-{
-	tExt2_Disk	*disk;
-	 int	fd;
-	 int	groupCount;
-	tExt2_SuperBlock	sb;
-	tExt2_Inode	inode;
-	
-	ENTER("sDevice pOptions", Device, Options);
-	
-	// Open Disk
-	fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);		//Open Device
-	if(fd == -1) {
-		Warning("[EXT2 ] Unable to open '%s'", Device);
-		LEAVE('n');
-		return NULL;
-	}
-	
-	// Read Superblock at offset 1024
-	VFS_ReadAt(fd, 1024, 1024, &sb);	// Read Superblock
-	
-	// Sanity Check Magic value
-	if(sb.s_magic != 0xEF53) {
-		Warning("[EXT2 ] Volume '%s' is not an EXT2 volume", Device);
-		VFS_Close(fd);
-		LEAVE('n');
-		return NULL;
-	}
-	
-	// Get Group count
-	groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);
-	LOG("groupCount = %i", groupCount);
-	
-	// Allocate Disk Information
-	disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);
-	if(!disk) {
-		Warning("[EXT2 ] Unable to allocate disk structure");
-		VFS_Close(fd);
-		LEAVE('n');
-		return NULL;
-	}
-	disk->FD = fd;
-	memcpy(&disk->SuperBlock, &sb, 1024);
-	disk->GroupCount = groupCount;
-	
-	// Get an inode cache handle
-	disk->CacheID = Inode_GetHandle();
-	
-	// Get Block Size
-	LOG("s_log_block_size = 0x%x", sb.s_log_block_size);
-	disk->BlockSize = 1024 << sb.s_log_block_size;
-	
-	// Read Group Information
-	VFS_ReadAt(
-		disk->FD,
-		sb.s_first_data_block * disk->BlockSize + 1024,
-		sizeof(tExt2_Group)*groupCount,
-		disk->Groups
-		);
-	
-	#if VERBOSE
-	LOG("Block Group 0");
-	LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap);
-	LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap);
-	LOG(".bg_inode_table = 0x%x", disk->Groups[0].bg_inode_table);
-	LOG("Block Group 1");
-	LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap);
-	LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap);
-	LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table);
-	#endif
-	
-	// Get root Inode
-	Ext2_int_ReadInode(disk, 2, &inode);
-	
-	// Create Root Node
-	memset(&disk->RootNode, 0, sizeof(tVFS_Node));
-	disk->RootNode.Inode = 2;	// Root inode ID
-	disk->RootNode.ImplPtr = disk;	// Save disk pointer
-	disk->RootNode.Size = -1;	// Fill in later (on readdir)
-	disk->RootNode.Flags = VFS_FFLAG_DIRECTORY;
-	
-	disk->RootNode.ReadDir = Ext2_ReadDir;
-	disk->RootNode.FindDir = Ext2_FindDir;
-	//disk->RootNode.Relink = Ext2_Relink;
-	
-	// Complete root node
-	disk->RootNode.UID = inode.i_uid;
-	disk->RootNode.GID = inode.i_gid;
-	disk->RootNode.NumACLs = 1;
-	disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW;
-	
-	#if DEBUG
-	LOG("inode.i_size = 0x%x", inode.i_size);
-	LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
-	#endif
-	
-	LEAVE('p', &disk->RootNode);
-	return &disk->RootNode;
-}
-
-/**
- * \fn void Ext2_Unmount(tVFS_Node *Node)
- * \brief Close a mounted device
- */
-void Ext2_Unmount(tVFS_Node *Node)
-{
-	tExt2_Disk	*disk = Node->ImplPtr;
-	
-	VFS_Close( disk->FD );
-	Inode_ClearCache( disk->CacheID );
-	memset(disk, 0, sizeof(tExt2_Disk)+disk->GroupCount*sizeof(tExt2_Group));
-	free(disk);
-}
-
-/**
- * \fn Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Read from a file
- */
-Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
-	tExt2_Disk	*disk = Node->ImplPtr;
-	tExt2_Inode	inode;
-	Uint64	base;
-	Uint	block;
-	Uint64	remLen;
-	
-	ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
-	
-	// Get Inode
-	Ext2_int_GetInode(Node, &inode);
-	
-	// Sanity Checks
-	if(Offset >= inode.i_size) {
-		LEAVE('i', 0);
-		return 0;
-	}
-	if(Offset + Length > inode.i_size)
-		Length = inode.i_size - Offset;
-	
-	block = Offset / disk->BlockSize;
-	Offset = Offset / disk->BlockSize;
-	base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
-	if(base == 0) {
-		Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode);
-		LEAVE('i', 0);
-		return 0;
-	}
-	
-	// Read only block
-	if(Length <= disk->BlockSize - Offset)
-	{
-		VFS_ReadAt( disk->FD, base+Offset, Length, Buffer);
-		LEAVE('X', Length);
-		return Length;
-	}
-	
-	// Read first block
-	remLen = Length;
-	VFS_ReadAt( disk->FD, base + Offset, disk->BlockSize - Offset, Buffer);
-	remLen -= disk->BlockSize - Offset;
-	Buffer += disk->BlockSize - Offset;
-	block ++;
-	
-	// Read middle blocks
-	while(remLen > disk->BlockSize)
-	{
-		base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
-		if(base == 0) {
-			Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode);
-			LEAVE('i', 0);
-			return 0;
-		}
-		VFS_ReadAt( disk->FD, base, disk->BlockSize, Buffer);
-		Buffer += disk->BlockSize;
-		remLen -= disk->BlockSize;
-		block ++;
-	}
-	
-	// Read last block
-	base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
-	VFS_ReadAt( disk->FD, base, remLen, Buffer);
-	
-	LEAVE('X', Length);
-	return Length;
-}
-
-/**
- * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Write to a file
- */
-Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
-	tExt2_Disk	*disk = Node->ImplPtr;
-	tExt2_Inode	inode;
-	Uint64	base;
-	Uint64	retLen;
-	Uint	block;
-	Uint64	allocSize;
-	 int	bNewBlocks = 0;
-	
-	Debug_HexDump("Ext2_Write", Buffer, Length);
-	
-	Ext2_int_GetInode(Node, &inode);
-	
-	// Get the ammount of space already allocated
-	// - Round size up to block size
-	// - block size is a power of two, so this will work
-	allocSize = (inode.i_size + disk->BlockSize) & ~(disk->BlockSize-1);
-	
-	// Are we writing to inside the allocated space?
-	if( Offset < allocSize )
-	{
-		// Will we go out of it?
-		if(Offset + Length > allocSize) {
-			bNewBlocks = 1;
-			retLen = allocSize - Offset;
-		} else
-			retLen = Length;
-		
-		// Within the allocated space
-		block = Offset / disk->BlockSize;
-		Offset %= disk->BlockSize;
-		base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
-		
-		// Write only block (if only one)
-		if(Offset + retLen <= disk->BlockSize) {
-			VFS_WriteAt(disk->FD, base+Offset, retLen, Buffer);
-			if(!bNewBlocks)	return Length;
-			goto addBlocks;	// Ugh! A goto, but it seems unavoidable
-		}
-		
-		// Write First Block
-		VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer);
-		Buffer += disk->BlockSize-Offset;
-		retLen -= disk->BlockSize-Offset;
-		block ++;
-		
-		// Write middle blocks
-		while(retLen > disk->BlockSize)
-		{
-			base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
-			VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
-			Buffer += disk->BlockSize;
-			retLen -= disk->BlockSize;
-			block ++;
-		}
-		
-		// Write last block
-		base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
-		VFS_WriteAt(disk->FD, base, retLen, Buffer);
-		if(!bNewBlocks)	return Length;	// Writing in only allocated space
-	}
-	
-addBlocks:
-	///\todo Implement block allocation
-	Warning("[EXT2] File extending is not yet supported");
-	
-	return 0;
-}
-
-/**
- * \fn void Ext2_CloseFile(tVFS_Node *Node)
- * \brief Close a file (Remove it from the cache)
- */
-void Ext2_CloseFile(tVFS_Node *Node)
-{
-	tExt2_Disk	*disk = Node->ImplPtr;
-	Inode_UncacheNode(disk->CacheID, Node->Inode);
-	return ;
-}
-
-/**
- \fn char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
- \brief Reads a directory entry
-*/
-char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
-{
-	tExt2_Inode	inode;
-	char	namebuf[EXT2_NAME_LEN+1];
-	tExt2_DirEnt	dirent;
-	Uint64	Base;	// Block's Base Address
-	 int	block = 0, ofs = 0;
-	 int	entNum = 0;
-	tExt2_Disk	*disk = Node->ImplPtr;
-	Uint	size;
-	
-	ENTER("pNode iPos", Node, Pos);
-	
-	// Read directory's inode
-	Ext2_int_GetInode(Node, &inode);
-	size = inode.i_size;
-	
-	LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
-	
-	// Find Entry
-	// Get First Block
-	// - Do this ourselves as it is a simple operation
-	Base = inode.i_block[0] * disk->BlockSize;
-	while(Pos -- && size > 0)
-	{
-		VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
-		ofs += dirent.rec_len;
-		size -= dirent.rec_len;
-		entNum ++;
-		
-		if(ofs >= disk->BlockSize) {
-			block ++;
-			if( ofs > disk->BlockSize ) {
-				Warning("[EXT2] Directory Entry %i of inode %i extends over a block boundary, ignoring",
-					entNum-1, Node->Inode);
-			}
-			ofs = 0;
-			Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
-		}
-	}
-	
-	// Check for the end of the list
-	if(size <= 0) {
-		LEAVE('n');
-		return NULL;
-	}
-	
-	// Read Entry
-	VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
-	//LOG("dirent.inode = %i", dirent.inode);
-	//LOG("dirent.rec_len = %i", dirent.rec_len);
-	//LOG("dirent.name_len = %i", dirent.name_len);
-	VFS_ReadAt( disk->FD, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );
-	namebuf[ dirent.name_len ] = '\0';	// Cap off string
-	
-	
-	// Ignore . and .. (these are done in the VFS)
-	if( (namebuf[0] == '.' && namebuf[1] == '\0')
-	||  (namebuf[0] == '.' && namebuf[1] == '.' && namebuf[2]=='\0')) {
-		LEAVE('p', VFS_SKIP);
-		return VFS_SKIP;	// Skip
-	}
-	
-	LEAVE('s', namebuf);
-	// Create new node
-	return strdup(namebuf);
-}
-
-/**
- \fn tVFS_Node *Ext2_FindDir(tVFS_Node *node, char *filename)
- \brief Gets information about a file
- \param node	vfs node - Parent Node
- \param filename	String - Name of file
- \return VFS Node of file
-*/
-tVFS_Node *Ext2_FindDir(tVFS_Node *Node, char *Filename)
-{
-	tExt2_Disk	*disk = Node->ImplPtr;
-	tExt2_Inode	inode;
-	char	namebuf[EXT2_NAME_LEN+1];
-	tExt2_DirEnt	dirent;
-	Uint64	Base;	// Block's Base Address
-	 int	block = 0, ofs = 0;
-	 int	entNum = 0;
-	Uint	size;
-	
-	// Read directory's inode
-	Ext2_int_GetInode(Node, &inode);
-	size = inode.i_size;
-	
-	// Get First Block
-	// - Do this ourselves as it is a simple operation
-	Base = inode.i_block[0] * disk->BlockSize;
-	// Find File
-	while(size > 0)
-	{
-		VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
-		VFS_ReadAt( disk->FD, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );
-		namebuf[ dirent.name_len ] = '\0';	// Cap off string
-		// If it matches, create a node and return it
-		if(strcmp(namebuf, Filename) == 0)
-			return Ext2_int_CreateNode( disk, dirent.inode, namebuf );
-		// Increment pointers
-		ofs += dirent.rec_len;
-		size -= dirent.rec_len;
-		entNum ++;
-		
-		// Check for end of block
-		if(ofs >= disk->BlockSize) {
-			block ++;
-			if( ofs > disk->BlockSize ) {
-				Warning("[EXT2 ] Directory Entry %i of inode %i extends over a block boundary, ignoring",
-					entNum-1, Node->Inode);
-			}
-			ofs = 0;
-			Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
-		}
-	}
-	
-	return NULL;
-}
-
-/**
- * \fn int Ext2_MkNod(tVFS_Node *Parent, char *Name, Uint Flags)
- * \brief Create a new node
- */
-int Ext2_MkNod(tVFS_Node *Parent, char *Name, Uint Flags)
-{
-	return 0;
-}
-
-//==================================
-//=       INTERNAL FUNCTIONS       =
-//==================================
-
-
-/**
- * \fn int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode)
- * \brief Gets the inode descriptor for a node
- * \param Node	node to get the Inode of
- * \param Inode	Destination
- */
-int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode)
-{
-	return Ext2_int_ReadInode(Node->ImplPtr, Node->Inode, Inode);
-}
-
-/**
- * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)
- * \brief Create a new VFS Node
- */
-tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)
-{
-	tExt2_Inode	inode;
-	tVFS_Node	retNode;
-	tVFS_Node	*tmpNode;
-	
-	if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
-		return NULL;
-	
-	if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
-		return tmpNode;
-	
-	
-	// Set identifiers
-	retNode.Inode = InodeID;
-	retNode.ImplPtr = Disk;
-	
-	// Set file length
-	retNode.Size = inode.i_size;
-	
-	// Set Access Permissions
-	retNode.UID = inode.i_uid;
-	retNode.GID = inode.i_gid;
-	retNode.NumACLs = 3;
-	retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
-	
-	//  Set Function Pointers
-	retNode.Read = Ext2_Read;
-	retNode.Write = Ext2_Write;
-	retNode.Close = Ext2_CloseFile;
-	
-	switch(inode.i_mode & EXT2_S_IFMT)
-	{
-	// Symbolic Link
-	case EXT2_S_IFLNK:
-		retNode.Flags = VFS_FFLAG_SYMLINK;
-		break;
-	// Regular File
-	case EXT2_S_IFREG:
-		retNode.Flags = 0;
-		retNode.Size |= (Uint64)inode.i_dir_acl << 32;
-		break;
-	// Directory
-	case EXT2_S_IFDIR:
-		retNode.ReadDir = Ext2_ReadDir;
-		retNode.FindDir = Ext2_FindDir;
-		retNode.MkNod = Ext2_MkNod;
-		//retNode.Relink = Ext2_Relink;
-		retNode.Flags = VFS_FFLAG_DIRECTORY;
-		break;
-	// Unknown, Write protect and hide it to be safe 
-	default:
-		retNode.Flags = VFS_FFLAG_READONLY;//|VFS_FFLAG_HIDDEN;
-		break;
-	}
-	
-	// Check if the file should be hidden
-	//if(Name[0] == '.')	retNode.Flags |= VFS_FFLAG_HIDDEN;
-	
-	// Set Timestamps
-	retNode.ATime = now();
-	retNode.MTime = inode.i_mtime * 1000;
-	retNode.CTime = inode.i_ctime * 1000;
-	
-	// Save in node cache and return saved node
-	return Inode_CacheNode(Disk->CacheID, &retNode);
-}
-
-/**
- * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)
- * \brief Read an inode into memory
- */
-int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)
-{
-	 int	group, subId;
-	
-	//LogF("Ext2_int_ReadInode: (Disk=%p, InodeId=%i, Inode=%p)", Disk, InodeId, Inode);
-	//ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);
-	
-	if(InodeId == 0)	return 0;
-	
-	InodeId --;	// Inodes are numbered starting at 1
-	
-	group = InodeId / Disk->SuperBlock.s_inodes_per_group;
-	subId = InodeId % Disk->SuperBlock.s_inodes_per_group;
-	
-	//LOG("group=%i, subId = %i", group, subId);
-	
-	// Read Inode
-	VFS_ReadAt(Disk->FD,
-		Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,
-		sizeof(tExt2_Inode),
-		Inode);
-	return 1;
-}
-
-/**
- * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)
- * \brief Get the address of a block from an inode's list
- * \param Disk	Disk information structure
- * \param Blocks	Pointer to an inode's block list
- * \param BlockNum	Block index in list
- */
-Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)
-{
-	Uint32	*iBlocks;
-	// Direct Blocks
-	if(BlockNum < 12)
-		return (Uint64)Blocks[BlockNum] * Disk->BlockSize;
-	
-	// Single Indirect Blocks
-	iBlocks = malloc( Disk->BlockSize );
-	VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);
-	
-	BlockNum -= 12;
-	if(BlockNum < 256) {
-		BlockNum = iBlocks[BlockNum];
-		free(iBlocks);
-		return (Uint64)BlockNum * Disk->BlockSize;
-	}
-	
-	// Double Indirect Blocks
-	if(BlockNum < 256*256)
-	{
-		VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);
-		VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/256]*Disk->BlockSize, Disk->BlockSize, iBlocks);
-		BlockNum = iBlocks[BlockNum%256];
-		free(iBlocks);
-		return (Uint64)BlockNum * Disk->BlockSize;
-	}
-	// Triple Indirect Blocks
-	VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);
-	VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(256*256)]*Disk->BlockSize, Disk->BlockSize, iBlocks);
-	VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/256)%256]*Disk->BlockSize, Disk->BlockSize, iBlocks);
-	BlockNum = iBlocks[BlockNum%256];
-	free(iBlocks);
-	return (Uint64)BlockNum * Disk->BlockSize;
-}
-
-/**
- * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)
- * \brief Allocate an inode (from the current group preferably)
- * \param Disk	EXT2 Disk Information Structure
- * \param Parent	Inode ID of the parent (used to locate the child nearby)
- */
-Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)
-{
-//	Uint	block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;
-	return 0;
-}
-
-/**
- * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
- * \brief Allocate a block from the best possible location
- * \param Disk	EXT2 Disk Information Structure
- * \param PrevBlock	Previous block ID in the file
- */
-Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
-{
-	 int	bpg = Disk->SuperBlock.s_blocks_per_group;
-	Uint	blockgroup = PrevBlock / bpg;
-	Uint	bitmap[Disk->BlockSize/sizeof(Uint)];
-	Uint	bitsperblock = 8*Disk->BlockSize;
-	 int	i, j = 0;
-	Uint	block;
-	
-	// Are there any free blocks?
-	if(Disk->SuperBlock.s_free_blocks_count == 0)	return 0;
-	
-	if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)
-	{
-		// Search block group's bitmap
-		for(i = 0; i < bpg; i++)
-		{
-			// Get the block in the bitmap block
-			j = i & (bitsperblock-1);
-			
-			// Read in if needed
-			if(j == 0) {
-				VFS_ReadAt(
-					Disk->FD,
-					(Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
-					Disk->BlockSize,
-					bitmap
-					);
-			}
-			
-			// Fast Check
-			if( bitmap[j/32] == -1 ) {
-				j = (j + 31) & ~31;
-				continue;
-			}
-			
-			// Is the bit set?
-			if( bitmap[j/32] & (1 << (j%32)) )
-				continue;
-			
-			// Ooh! We found one
-			break;
-		}
-		if( i < bpg ) {
-			Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist");
-			goto	checkAll;	// Search the entire filesystem for a free block
-			// Goto needed for neatness
-		}
-		
-		// Mark as used
-		bitmap[j/32] |= (1 << (j%32));
-		VFS_WriteAt(
-			Disk->FD,
-			(Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
-			Disk->BlockSize,
-			bitmap
-			);
-		block = i;
-		Disk->Groups[blockgroup].bg_free_blocks_count --;
-	}
-	else
-	{
-	checkAll:
-		Warning("[EXT2 ] TODO - Implement using blocks outside the current block group");
-		return 0;
-	}
-	
-	// Reduce global count
-	Disk->SuperBlock.s_free_blocks_count --;
-	#if EXT2_UPDATE_WRITEBACK
-	Ext2_int_UpdateSuperblock(Disk);
-	#endif
-	
-	return block;
-}
-
-/**
- * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)
- */
-void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)
-{
-	VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);
-}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..392ab5dec1b53000b31131b28fe868fc1a21df60
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,39 @@
+#
+# Acess2 Core Makefile
+#
+
+.PHONY: all clean
+
+all:
+	@echo === Kernel
+	@$(MAKE) all --no-print-directory -C Kernel
+	@echo === ld-acess.so
+	@$(MAKE) all --no-print-directory -C Usermode/Libraries/ld-acess.so_src
+	@echo === libacess.so
+	@$(MAKE) all --no-print-directory -C Usermode/Libraries/libacess.so_src
+	@echo === libgcc.so
+	@$(MAKE) all --no-print-directory -C Usermode/Libraries/libgcc.so_src
+	@echo === libc.so
+	@$(MAKE) all --no-print-directory -C Usermode/Libraries/libc.so_src
+	@echo === init
+	@$(MAKE) all --no-print-directory -C Usermode/Applications/init_src
+	@echo === login
+	@$(MAKE) all --no-print-directory -C Usermode/Applications/login_src
+	@echo === CLIShell
+	@$(MAKE) all --no-print-directory -C Usermode/Applications/CLIShell_src
+	@echo === cat
+	@$(MAKE) all --no-print-directory -C Usermode/Applications/cat_src
+	@echo === ls
+	@$(MAKE) all --no-print-directory -C Usermode/Applications/ls_src
+
+clean:
+	make clean --no-print-directory -C Kernel/
+	make clean --no-print-directory -C Usermode/Libraries/ld-acess.so_src
+	make clean --no-print-directory -C Usermode/Libraries/libacess.so_src
+	make clean --no-print-directory -C Usermode/Libraries/libc.so_src
+	make clean --no-print-directory -C Usermode/Libraries/libgcc.so_src
+	make clean --no-print-directory -C Usermode/Applications/init_src
+	make clean --no-print-directory -C Usermode/Applications/login_src
+	make clean --no-print-directory -C Usermode/Applications/CLIShell_src
+	make clean --no-print-directory -C Usermode/Applications/cat_src
+	make clean --no-print-directory -C Usermode/Applications/ls_src
diff --git a/Makefile.cfg b/Makefile.cfg
index a8d3b200b91b196304e51de5d6ac450786d659b5..18181a919ba6cdb15e1a7602567cc6396d4d15f4 100644
--- a/Makefile.cfg
+++ b/Makefile.cfg
@@ -14,9 +14,9 @@ RMDIR = rm -rf
 ARCH = i386
 ARCHDIR = x86
 
-FILESYSTEMS = fat ext2
+FILESYSTEMS = fat
 DRIVERS = ata_x86
-MODULES = FDD NE2000 BochsVBE
+MODULES = FS_Ext2 FDD NE2000 BochsGA
 
 DISTROOT = /mnt/AcessHDD/Acess2
 ACESSDIR = /home/hodgeja/Projects/Acess2
diff --git a/Modules/BochsVBE/Makefile b/Modules/BochsVBE/Makefile
deleted file mode 100644
index add4018187e979808f389e2ec777590b4a521452..0000000000000000000000000000000000000000
--- a/Modules/BochsVBE/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-#
-
-OBJ = bochsvbe.o
-NAME = BochsVBE
-
--include ../Makefile.tpl
diff --git a/Modules/BochsVBE/bochsvbe.c b/Modules/BochsVBE/bochsvbe.c
deleted file mode 100644
index 2fe01100aa5ae2ab2679f95f2df7c9c996aef9b3..0000000000000000000000000000000000000000
--- a/Modules/BochsVBE/bochsvbe.c
+++ /dev/null
@@ -1,451 +0,0 @@
-/**
- * \file drv_bochsvbe.c
- * \brief BGA (Bochs Graphic Adapter) Driver
- * \note for Acess2
- * \warning This driver does NOT support the Bochs PCI VGA driver
-*/
-#define DEBUG	0
-#include <common.h>
-#include <errno.h>
-#include <modules.h>
-#include <vfs.h>
-#include <fs_devfs.h>
-#include <drv_pci.h>
-#include <tpl_drv_video.h>
-
-//#define INT	static
-#define INT
-
-// === TYPEDEFS ===
-typedef struct {
-	Uint16	width;
-	Uint16	height;
-	Uint16	bpp;
-	Uint16	flags;
-	Uint32	fbSize;
-} t_bga_mode;
-
-// === CONSTANTS ===
-enum eMode_Flags {
-	MODEFLAG_TEXT = 1
-};
-#define	BGA_LFB_MAXSIZE	(1024*768*4)
-#define	VBE_DISPI_BANK_ADDRESS	0xA0000
-#define VBE_DISPI_LFB_PHYSICAL_ADDRESS	0xE0000000
-#define VBE_DISPI_IOPORT_INDEX	0x01CE
-#define	VBE_DISPI_IOPORT_DATA	0x01CF
-#define	VBE_DISPI_DISABLED	0x00
-#define VBE_DISPI_ENABLED	0x01
-#define	VBE_DISPI_LFB_ENABLED	0x40
-#define	VBE_DISPI_NOCLEARMEM	0x80
-enum {
-	VBE_DISPI_INDEX_ID,
-	VBE_DISPI_INDEX_XRES,
-	VBE_DISPI_INDEX_YRES,
-	VBE_DISPI_INDEX_BPP,
-	VBE_DISPI_INDEX_ENABLE,
-	VBE_DISPI_INDEX_BANK,
-	VBE_DISPI_INDEX_VIRT_WIDTH,
-	VBE_DISPI_INDEX_VIRT_HEIGHT,
-	VBE_DISPI_INDEX_X_OFFSET,
-	VBE_DISPI_INDEX_Y_OFFSET
-};
-
-
-// === PROTOTYPES ===
-// Driver
- int	BGA_Install(char **Arguments);
-void	BGA_Uninstall();
-// Internal
-void	BGA_int_WriteRegister(Uint16 reg, Uint16 value);
-Uint16	BGA_int_ReadRegister(Uint16 reg);
-void	BGA_int_SetBank(Uint16 bank);
-void	BGA_int_SetMode(Uint16 width, Uint16 height);
- int	BGA_int_UpdateMode(int id);
- int	BGA_int_FindMode(tVideo_IOCtl_Mode *info);
- int	BGA_int_ModeInfo(tVideo_IOCtl_Mode *info);
- int	BGA_int_MapFB(void *Dest);
-// Filesystem
-Uint64	BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
-Uint64	BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
- int	BGA_Ioctl(tVFS_Node *node, int id, void *data);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, 0x0032, BochsVBE, BGA_Install, NULL, NULL);
-tDevFS_Driver	gBGA_DriverStruct = {
-	NULL, "BochsGA",
-	{
-	.Read = BGA_Read,
-	.Write = BGA_Write,
-	.IOCtl = BGA_Ioctl
-	}
-};
- int	giBGA_CurrentMode = -1;
- int	giBGA_DriverId = -1;
-Uint	*gBGA_Framebuffer;
-t_bga_mode	gBGA_Modes[] = {
-	{},
-	{ 80,25, 12, MODEFLAG_TEXT, 80*25*8},	// 640 x 480
-	{100,37, 12, MODEFLAG_TEXT, 100*37*8},	// 800 x 600
-	{640,480,8, 0, 640*480},
-	{640,480,32, 0, 640*480*4},
-	{800,600,8, 0, 800*600},
-	{800,600,32, 0, 800*600*4},
-};
-#define	BGA_MODE_COUNT	(sizeof(gBGA_Modes)/sizeof(gBGA_Modes[0]))
-
-// === CODE ===
-/**
- * \fn int BGA_Install(char **Arguments)
- */
-int BGA_Install(char **Arguments)
-{
-	 int	bga_version = 0;
-	
-	// Check BGA Version
-	bga_version = BGA_int_ReadRegister(VBE_DISPI_INDEX_ID);
-	// NOTE: This driver was written for 0xB0C4, but they seem to be backwards compatable
-	if(bga_version < 0xB0C4 || bga_version > 0xB0C5) {
-		Warning("[BGA ] Bochs Adapter Version is not 0xB0C4 or 0xB0C5, instead 0x%x", bga_version);
-		return 0;
-	}
-	
-	// Install Device
-	giBGA_DriverId = DevFS_AddDevice( &gBGA_DriverStruct );
-	if(giBGA_DriverId == -1) {
-		Warning("[BGA ] Unable to register with DevFS, maybe already loaded?");
-		return 0;
-	}
-	
-	// Map Framebuffer to hardware address
-	gBGA_Framebuffer = (void *) MM_MapHWPage(VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768);	// 768 pages (3Mb)
-	
-	return 1;
-}
-
-/**
- * \fn void BGA_Uninstall()
- */
-void BGA_Uninstall()
-{
-	//DevFS_DelDevice( giBGA_DriverId );
-	MM_UnmapHWPage( VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768 );
-}
-
-/**
- * \fn Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
- * \brief Read from the framebuffer
- */
-Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
-{
-	// Check Mode
-	if(giBGA_CurrentMode == -1)	return -1;
-	
-	// Check Offset and Length against Framebuffer Size
-	if(off+len > gBGA_Modes[giBGA_CurrentMode].fbSize)
-		return -1;
-	
-	// Copy from Framebuffer
-	memcpy(buffer, (void*)((Uint)gBGA_Framebuffer + (Uint)off), len);
-	return len;
-}
-
-/**
- * \fn Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
- * \brief Write to the framebuffer
- */
-Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
-{	
-	ENTER("xoff xlen", off, len);
-	
-	// Check Mode
-	if(giBGA_CurrentMode == -1) {
-		LEAVE('i', -1);
-		return -1;
-	}
-	
-	// Check Input against Frambuffer Size
-	if(off+len > gBGA_Modes[giBGA_CurrentMode].fbSize) {
-		LEAVE('i', -1);
-		return -1;
-	}
-	
-	// Text Mode
-	if( gBGA_Modes[giBGA_CurrentMode].flags & MODEFLAG_TEXT )
-	{
-		tVT_Char	*chars = buffer;
-		 int	pitch = gBGA_Modes[giBGA_CurrentMode].width * giVT_CharWidth;
-		Uint32	*dest;
-		off /= sizeof(tVT_Char);
-		dest = (void*)gBGA_Framebuffer;
-		dest += (off / gBGA_Modes[giBGA_CurrentMode].width) * giVT_CharHeight * pitch;
-		dest += (off % gBGA_Modes[giBGA_CurrentMode].width) * giVT_CharWidth;
-		len /= sizeof(tVT_Char);
-		while(len--)
-		{
-			VT_Font_Render(
-				chars->Ch,
-				dest, pitch,
-				VT_Colour12to24(chars->BGCol),
-				VT_Colour12to24(chars->FGCol)
-				);
-			dest += giVT_CharWidth;
-			chars++;
-		}
-	}
-	else
-	{
-		Uint8	*destBuf = (Uint8*) ((Uint)gBGA_Framebuffer + (Uint)off);
-		
-		LOG("buffer = %p\n", buffer);
-		LOG("Updating Framebuffer (%p to %p)\n", 
-			destBuf, destBuf + (Uint)len);
-		
-		
-		// Copy to Frambuffer
-		memcpy(destBuf, buffer, len);
-		
-		LOG("BGA Framebuffer updated\n");
-	}
-	
-	LEAVE('i', len);
-	return len;
-}
-
-/**
- * \fn INT int BGA_Ioctl(tVFS_Node *node, int id, void *data)
- * \brief Handle messages to the device
- */
-INT int BGA_Ioctl(tVFS_Node *node, int id, void *data)
-{
-	 int	ret = -2;
-	ENTER("pNode iId pData", node, id, data);
-	
-	switch(id)
-	{
-	case DRV_IOCTL_TYPE:
-		ret = DRV_TYPE_VIDEO;
-		break;
-	case DRV_IOCTL_IDENT:
-		memcpy(data, "BGA1", 4);
-		ret = 1;
-		break;
-	case DRV_IOCTL_VERSION:
-		ret = 0x100;
-		break;
-	case DRV_IOCTL_LOOKUP:	// TODO: Implement
-		ret = 0;
-		break;
-		
-	case VIDEO_IOCTL_SETMODE:
-		ret = BGA_int_UpdateMode(*(int*)(data));
-		break;
-		
-	case VIDEO_IOCTL_GETMODE:
-		ret = giBGA_CurrentMode;
-		break;
-	
-	case VIDEO_IOCTL_FINDMODE:
-		ret = BGA_int_FindMode((tVideo_IOCtl_Mode*)data);
-		break;
-	
-	case VIDEO_IOCTL_MODEINFO:
-		ret = BGA_int_ModeInfo((tVideo_IOCtl_Mode*)data);
-		break;
-	
-	// Request Access to LFB
-	case VIDEO_IOCTL_REQLFB:
-		ret = BGA_int_MapFB( *(void**)data );
-		break;
-	
-	default:
-		LEAVE('i', -2);
-		return -2;
-	}
-	
-	LEAVE('i', ret);
-	return ret;
-}
-
-//== Internal Functions ==
-/**
- * \fn void BGA_int_WriteRegister(Uint16 reg, Uint16 value)
- * \brief Writes to a BGA register
- */
-void BGA_int_WriteRegister(Uint16 reg, Uint16 value)
-{
-	outw(VBE_DISPI_IOPORT_INDEX, reg);
-	outw(VBE_DISPI_IOPORT_DATA, value);
-}
-
-INT Uint16 BGA_int_ReadRegister(Uint16 reg)
-{
-	outw(VBE_DISPI_IOPORT_INDEX, reg);
-	return inw(VBE_DISPI_IOPORT_DATA);
-}
-
-#if 0
-INT void BGA_int_SetBank(Uint16 bank)
-{
-	BGA_int_WriteRegister(VBE_DISPI_INDEX_BANK, bank);
-}
-#endif
-
-/**
- * \fn void BGA_int_SetMode(Uint16 width, Uint16 height, Uint16 bpp)
- * \brief Sets the video mode from the dimensions and bpp given
- */
-void BGA_int_SetMode(Uint16 width, Uint16 height)
-{
-	ENTER("iwidth iheight ibpp", width, height, bpp);
-	BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
-    BGA_int_WriteRegister(VBE_DISPI_INDEX_XRES,	width);
-    BGA_int_WriteRegister(VBE_DISPI_INDEX_YRES,	height);
-    BGA_int_WriteRegister(VBE_DISPI_INDEX_BPP,	32);
-    BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM | VBE_DISPI_LFB_ENABLED);
-    //BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM);
-	LEAVE('-');
-}
-
-/**
- * \fn int BGA_int_UpdateMode(int id)
- * \brief Set current vide mode given a mode id
- */
-int BGA_int_UpdateMode(int id)
-{
-	// Sanity Check
-	if(id < 0 || id >= BGA_MODE_COUNT)	return -1;
-	
-	// Check if it is a text mode
-	if( gBGA_Modes[id].flags & MODEFLAG_TEXT )
-		BGA_int_SetMode(
-			gBGA_Modes[id].width*giVT_CharWidth,
-			gBGA_Modes[id].height*giVT_CharHeight);
-	else	// Graphics?
-		BGA_int_SetMode(
-			gBGA_Modes[id].width,
-			gBGA_Modes[id].height);
-	
-	giBGA_CurrentMode = id;
-	return id;
-}
-
-/**
- * \fn int BGA_int_FindMode(tVideo_IOCtl_Mode *info)
- * \brief Find a mode matching the given options
- */
-int BGA_int_FindMode(tVideo_IOCtl_Mode *info)
-{
-	 int	i;
-	 int	best = 0, bestFactor = 1000;
-	 int	factor, tmp;
-	 int	rqdProduct = info->width * info->height * info->bpp;
-	
-	ENTER("pinfo", info);
-	LOG("info = {width:%i,height:%i,bpp:%i})\n", info->width, info->height, info->bpp);
-	
-	for(i = 0; i < BGA_MODE_COUNT; i++)
-	{
-		#if DEBUG >= 2
-		LogF("Mode %i (%ix%ix%i), ", i, gBGA_Modes[i].width, gBGA_Modes[i].height, gBGA_Modes[i].bpp);
-		#endif
-		
-		// Check if this mode is the same type as what we want
-		if( !(gBGA_Modes[i].flags & MODEFLAG_TEXT) != !(info->flags & VIDEO_FLAG_TEXT) )
-			continue;
-		
-		// Ooh! A perfect match
-		if(gBGA_Modes[i].width == info->width
-		&& gBGA_Modes[i].height == info->height
-		&& gBGA_Modes[i].bpp == info->bpp)
-		{
-			#if DEBUG >= 2
-			LogF("Perfect!\n");
-			#endif
-			best = i;
-			break;
-		}
-		
-		// If not, how close are we?
-		tmp = gBGA_Modes[i].width * gBGA_Modes[i].height * gBGA_Modes[i].bpp;
-		tmp -= rqdProduct;
-		tmp = tmp < 0 ? -tmp : tmp;	// tmp = ABS(tmp)
-		factor = tmp * 100 / rqdProduct;
-		
-		#if DEBUG >= 2
-		LogF("factor = %i\n", factor);
-		#endif
-		
-		if(factor < bestFactor)
-		{
-			bestFactor = factor;
-			best = i;
-		}
-	}
-	
-	info->id = best;
-	info->width = gBGA_Modes[best].width;
-	info->height = gBGA_Modes[best].height;
-	info->bpp = gBGA_Modes[best].bpp;
-	
-	info->flags = 0;
-	if(gBGA_Modes[best].flags & MODEFLAG_TEXT)
-		info->flags |= VIDEO_FLAG_TEXT;
-	
-	return best;
-}
-
-/**
- * \fn int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)
- * \brief Get mode information
- */
-int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)
-{
-	// Sanity Check
-	//if( !MM_IsUser( (Uint)info, sizeof(tVideo_IOCtl_Mode) ) ) {
-	//	return -EINVAL;
-	//}
-	
-	if(info->id < 0 || info->id >= BGA_MODE_COUNT)	return -1;
-	
-	info->width = gBGA_Modes[info->id].width;
-	info->height = gBGA_Modes[info->id].height;
-	info->bpp = gBGA_Modes[info->id].bpp;
-	
-	info->flags = 0;
-	if(gBGA_Modes[info->id].flags & MODEFLAG_TEXT)
-		info->flags |= VIDEO_FLAG_TEXT;
-	
-	return 1;
-}
-
-/**
- * \fn int BGA_int_MapFB(void *Dest)
- * \brief Map the framebuffer into a process's space
- * \param Dest	User address to load to
- */
-int BGA_int_MapFB(void *Dest)
-{
-	Uint	i;
-	Uint	pages;
-	
-	// Sanity Check
-	if((Uint)Dest > 0xC0000000)	return 0;
-	if(gBGA_Modes[giBGA_CurrentMode].bpp < 15)	return 0;	// Only non-pallete modes are supported
-	
-	// Count required pages
-	pages = (gBGA_Modes[giBGA_CurrentMode].fbSize + 0xFFF) >> 12;
-	
-	// Check if there is space
-	for( i = 0; i < pages; i++ )
-	{
-		if(MM_GetPhysAddr( (Uint)Dest + (i << 12) ))
-			return 0;
-	}
-	
-	// Map
-	for( i = 0; i < pages; i++ )
-		MM_Map( (Uint)Dest + (i<<12), VBE_DISPI_LFB_PHYSICAL_ADDRESS + (i<<12) );
-	
-	return 1;
-}
diff --git a/Modules/FS_Ext2/Makefile b/Modules/FS_Ext2/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..806e638c4356f045020fc85b4d1b4677c5e5aaf9
--- /dev/null
+++ b/Modules/FS_Ext2/Makefile
@@ -0,0 +1,7 @@
+#
+#
+
+OBJ = ext2.o read.o dir.o write.o
+NAME = FS_Ext2
+
+-include ../Makefile.tpl
diff --git a/Modules/FS_Ext2/dir.c b/Modules/FS_Ext2/dir.c
new file mode 100644
index 0000000000000000000000000000000000000000..5fe893369e2ea79a9324e383940d9c58e4d73b11
--- /dev/null
+++ b/Modules/FS_Ext2/dir.c
@@ -0,0 +1,228 @@
+/*
+ * Acess OS
+ * Ext2 Driver Version 1
+ */
+/**
+ * \file dir.c
+ * \brief Second Extended Filesystem Driver
+ * \todo Implement file full write support
+ */
+#define DEBUG	1
+#define VERBOSE	0
+#include "ext2_common.h"
+
+
+// === PROTOTYPES ===
+char		*Ext2_ReadDir(tVFS_Node *Node, int Pos);
+tVFS_Node	*Ext2_FindDir(tVFS_Node *Node, char *FileName);
+ int		Ext2_MkNod(tVFS_Node *Node, char *Name, Uint Flags);
+tVFS_Node	*Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId, char *Name);
+
+// === CODE ===
+/**
+ \fn char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
+ \brief Reads a directory entry
+*/
+char *Ext2_ReadDir(tVFS_Node *Node, int Pos)
+{
+	tExt2_Inode	inode;
+	char	namebuf[EXT2_NAME_LEN+1];
+	tExt2_DirEnt	dirent;
+	Uint64	Base;	// Block's Base Address
+	 int	block = 0, ofs = 0;
+	 int	entNum = 0;
+	tExt2_Disk	*disk = Node->ImplPtr;
+	Uint	size;
+	
+	ENTER("pNode iPos", Node, Pos);
+	
+	// Read directory's inode
+	Ext2_int_GetInode(Node, &inode);
+	size = inode.i_size;
+	
+	LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
+	
+	// Find Entry
+	// Get First Block
+	// - Do this ourselves as it is a simple operation
+	Base = inode.i_block[0] * disk->BlockSize;
+	while(Pos -- && size > 0)
+	{
+		VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
+		ofs += dirent.rec_len;
+		size -= dirent.rec_len;
+		entNum ++;
+		
+		if(ofs >= disk->BlockSize) {
+			block ++;
+			if( ofs > disk->BlockSize ) {
+				Warning("[EXT2] Directory Entry %i of inode %i extends over a block boundary, ignoring",
+					entNum-1, Node->Inode);
+			}
+			ofs = 0;
+			Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
+		}
+	}
+	
+	// Check for the end of the list
+	if(size <= 0) {
+		LEAVE('n');
+		return NULL;
+	}
+	
+	// Read Entry
+	VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent );
+	//LOG("dirent.inode = %i", dirent.inode);
+	//LOG("dirent.rec_len = %i", dirent.rec_len);
+	//LOG("dirent.name_len = %i", dirent.name_len);
+	VFS_ReadAt( disk->FD, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );
+	namebuf[ dirent.name_len ] = '\0';	// Cap off string
+	
+	
+	// Ignore . and .. (these are done in the VFS)
+	if( (namebuf[0] == '.' && namebuf[1] == '\0')
+	||  (namebuf[0] == '.' && namebuf[1] == '.' && namebuf[2]=='\0')) {
+		LEAVE('p', VFS_SKIP);
+		return VFS_SKIP;	// Skip
+	}
+	
+	LEAVE('s', namebuf);
+	// Create new node
+	return strdup(namebuf);
+}
+
+/**
+ \fn tVFS_Node *Ext2_FindDir(tVFS_Node *node, char *filename)
+ \brief Gets information about a file
+ \param node	vfs node - Parent Node
+ \param filename	String - Name of file
+ \return VFS Node of file
+*/
+tVFS_Node *Ext2_FindDir(tVFS_Node *Node, char *Filename)
+{
+	tExt2_Disk	*disk = Node->ImplPtr;
+	tExt2_Inode	inode;
+	char	namebuf[EXT2_NAME_LEN+1];
+	tExt2_DirEnt	dirent;
+	Uint64	Base;	// Block's Base Address
+	 int	block = 0, ofs = 0;
+	 int	entNum = 0;
+	Uint	size;
+	
+	// Read directory's inode
+	Ext2_int_GetInode(Node, &inode);
+	size = inode.i_size;
+	
+	// Get First Block
+	// - Do this ourselves as it is a simple operation
+	Base = inode.i_block[0] * disk->BlockSize;
+	// Find File
+	while(size > 0)
+	{
+		VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent);
+		VFS_ReadAt( disk->FD, Base+ofs+sizeof(tExt2_DirEnt), dirent.name_len, namebuf );
+		namebuf[ dirent.name_len ] = '\0';	// Cap off string
+		// If it matches, create a node and return it
+		if(strcmp(namebuf, Filename) == 0)
+			return Ext2_int_CreateNode( disk, dirent.inode, namebuf );
+		// Increment pointers
+		ofs += dirent.rec_len;
+		size -= dirent.rec_len;
+		entNum ++;
+		
+		// Check for end of block
+		if(ofs >= disk->BlockSize) {
+			block ++;
+			if( ofs > disk->BlockSize ) {
+				Warning("[EXT2 ] Directory Entry %i of inode %i extends over a block boundary, ignoring",
+					entNum-1, Node->Inode);
+			}
+			ofs = 0;
+			Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block );
+		}
+	}
+	
+	return NULL;
+}
+
+/**
+ * \fn int Ext2_MkNod(tVFS_Node *Parent, char *Name, Uint Flags)
+ * \brief Create a new node
+ */
+int Ext2_MkNod(tVFS_Node *Parent, char *Name, Uint Flags)
+{
+	return 0;
+}
+
+// ---- INTERNAL FUNCTIONS ----
+/**
+ * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)
+ * \brief Create a new VFS Node
+ */
+tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID, char *Name)
+{
+	tExt2_Inode	inode;
+	tVFS_Node	retNode;
+	tVFS_Node	*tmpNode;
+	
+	if( !Ext2_int_ReadInode(Disk, InodeID, &inode) )
+		return NULL;
+	
+	if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) )
+		return tmpNode;
+	
+	
+	// Set identifiers
+	retNode.Inode = InodeID;
+	retNode.ImplPtr = Disk;
+	
+	// Set file length
+	retNode.Size = inode.i_size;
+	
+	// Set Access Permissions
+	retNode.UID = inode.i_uid;
+	retNode.GID = inode.i_gid;
+	retNode.NumACLs = 3;
+	retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid);
+	
+	//  Set Function Pointers
+	retNode.Read = Ext2_Read;
+	retNode.Write = Ext2_Write;
+	retNode.Close = Ext2_CloseFile;
+	
+	switch(inode.i_mode & EXT2_S_IFMT)
+	{
+	// Symbolic Link
+	case EXT2_S_IFLNK:
+		retNode.Flags = VFS_FFLAG_SYMLINK;
+		break;
+	// Regular File
+	case EXT2_S_IFREG:
+		retNode.Flags = 0;
+		retNode.Size |= (Uint64)inode.i_dir_acl << 32;
+		break;
+	// Directory
+	case EXT2_S_IFDIR:
+		retNode.ReadDir = Ext2_ReadDir;
+		retNode.FindDir = Ext2_FindDir;
+		retNode.MkNod = Ext2_MkNod;
+		//retNode.Relink = Ext2_Relink;
+		retNode.Flags = VFS_FFLAG_DIRECTORY;
+		break;
+	// Unknown, Write protect and hide it to be safe 
+	default:
+		retNode.Flags = VFS_FFLAG_READONLY;//|VFS_FFLAG_HIDDEN;
+		break;
+	}
+	
+	// Check if the file should be hidden
+	//if(Name[0] == '.')	retNode.Flags |= VFS_FFLAG_HIDDEN;
+	
+	// Set Timestamps
+	retNode.ATime = now();
+	retNode.MTime = inode.i_mtime * 1000;
+	retNode.CTime = inode.i_ctime * 1000;
+	
+	// Save in node cache and return saved node
+	return Inode_CacheNode(Disk->CacheID, &retNode);
+}
diff --git a/Modules/FS_Ext2/ext2.c b/Modules/FS_Ext2/ext2.c
new file mode 100644
index 0000000000000000000000000000000000000000..81dcef0b189d90e062754094c655918832ea74c7
--- /dev/null
+++ b/Modules/FS_Ext2/ext2.c
@@ -0,0 +1,309 @@
+/*
+ * Acess OS
+ * Ext2 Driver Version 1
+ */
+/**
+ * \file fs/ext2.c
+ * \brief Second Extended Filesystem Driver
+ * \todo Implement file full write support
+ */
+#define DEBUG	1
+#define VERBOSE	0
+#include "ext2_common.h"
+#include <modules.h>
+
+// === PROTOTYPES ===
+ int	Ext2_Install(char **Arguments);
+// Interface Functions
+tVFS_Node	*Ext2_InitDevice(char *Device, char **Options);
+void		Ext2_Unmount(tVFS_Node *Node);
+void		Ext2_CloseFile(tVFS_Node *Node);
+// Internal Helpers
+ int		Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode);
+Uint64		Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);
+Uint32		Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent);
+void		Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);
+
+// === SEMI-GLOBALS ===
+MODULE_DEFINE(0, 0x5B /*v0.90*/, FS_Ext2, Ext2_Install, NULL);
+tExt2_Disk	gExt2_disks[6];
+ int	giExt2_count = 0;
+tVFS_Driver	gExt2_FSInfo = {
+	"ext2", 0, Ext2_InitDevice, Ext2_Unmount, NULL
+	};
+
+// === CODE ===
+
+/**
+ * \fn int Ext2_Install(char **Arguments)
+ * \brief Install the Ext2 Filesystem Driver
+ */
+int Ext2_Install(char **Arguments)
+{
+	VFS_AddDriver( &gExt2_FSInfo );
+	return 1;
+}
+
+/**
+ \fn tVFS_Node *Ext2_InitDevice(char *Device, char **Options)
+ \brief Initializes a device to be read by by the driver
+ \param Device	String - Device to read from
+ \param Options	NULL Terminated array of option strings
+ \return Root Node
+*/
+tVFS_Node *Ext2_InitDevice(char *Device, char **Options)
+{
+	tExt2_Disk	*disk;
+	 int	fd;
+	 int	groupCount;
+	tExt2_SuperBlock	sb;
+	tExt2_Inode	inode;
+	
+	ENTER("sDevice pOptions", Device, Options);
+	
+	// Open Disk
+	fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);		//Open Device
+	if(fd == -1) {
+		Warning("[EXT2 ] Unable to open '%s'", Device);
+		LEAVE('n');
+		return NULL;
+	}
+	
+	// Read Superblock at offset 1024
+	VFS_ReadAt(fd, 1024, 1024, &sb);	// Read Superblock
+	
+	// Sanity Check Magic value
+	if(sb.s_magic != 0xEF53) {
+		Warning("[EXT2 ] Volume '%s' is not an EXT2 volume", Device);
+		VFS_Close(fd);
+		LEAVE('n');
+		return NULL;
+	}
+	
+	// Get Group count
+	groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);
+	LOG("groupCount = %i", groupCount);
+	
+	// Allocate Disk Information
+	disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount);
+	if(!disk) {
+		Warning("[EXT2 ] Unable to allocate disk structure");
+		VFS_Close(fd);
+		LEAVE('n');
+		return NULL;
+	}
+	disk->FD = fd;
+	memcpy(&disk->SuperBlock, &sb, 1024);
+	disk->GroupCount = groupCount;
+	
+	// Get an inode cache handle
+	disk->CacheID = Inode_GetHandle();
+	
+	// Get Block Size
+	LOG("s_log_block_size = 0x%x", sb.s_log_block_size);
+	disk->BlockSize = 1024 << sb.s_log_block_size;
+	
+	// Read Group Information
+	VFS_ReadAt(
+		disk->FD,
+		sb.s_first_data_block * disk->BlockSize + 1024,
+		sizeof(tExt2_Group)*groupCount,
+		disk->Groups
+		);
+	
+	#if VERBOSE
+	LOG("Block Group 0");
+	LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap);
+	LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap);
+	LOG(".bg_inode_table = 0x%x", disk->Groups[0].bg_inode_table);
+	LOG("Block Group 1");
+	LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap);
+	LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap);
+	LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table);
+	#endif
+	
+	// Get root Inode
+	Ext2_int_ReadInode(disk, 2, &inode);
+	
+	// Create Root Node
+	memset(&disk->RootNode, 0, sizeof(tVFS_Node));
+	disk->RootNode.Inode = 2;	// Root inode ID
+	disk->RootNode.ImplPtr = disk;	// Save disk pointer
+	disk->RootNode.Size = -1;	// Fill in later (on readdir)
+	disk->RootNode.Flags = VFS_FFLAG_DIRECTORY;
+	
+	disk->RootNode.ReadDir = Ext2_ReadDir;
+	disk->RootNode.FindDir = Ext2_FindDir;
+	//disk->RootNode.Relink = Ext2_Relink;
+	
+	// Complete root node
+	disk->RootNode.UID = inode.i_uid;
+	disk->RootNode.GID = inode.i_gid;
+	disk->RootNode.NumACLs = 1;
+	disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW;
+	
+	#if DEBUG
+	LOG("inode.i_size = 0x%x", inode.i_size);
+	LOG("inode.i_block[0] = 0x%x", inode.i_block[0]);
+	#endif
+	
+	LEAVE('p', &disk->RootNode);
+	return &disk->RootNode;
+}
+
+/**
+ * \fn void Ext2_Unmount(tVFS_Node *Node)
+ * \brief Close a mounted device
+ */
+void Ext2_Unmount(tVFS_Node *Node)
+{
+	tExt2_Disk	*disk = Node->ImplPtr;
+	
+	VFS_Close( disk->FD );
+	Inode_ClearCache( disk->CacheID );
+	memset(disk, 0, sizeof(tExt2_Disk)+disk->GroupCount*sizeof(tExt2_Group));
+	free(disk);
+}
+
+/**
+ * \fn void Ext2_CloseFile(tVFS_Node *Node)
+ * \brief Close a file (Remove it from the cache)
+ */
+void Ext2_CloseFile(tVFS_Node *Node)
+{
+	tExt2_Disk	*disk = Node->ImplPtr;
+	Inode_UncacheNode(disk->CacheID, Node->Inode);
+	return ;
+}
+
+//==================================
+//=       INTERNAL FUNCTIONS       =
+//==================================
+
+
+/**
+ * \fn int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode)
+ * \brief Gets the inode descriptor for a node
+ * \param Node	node to get the Inode of
+ * \param Inode	Destination
+ */
+int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode)
+{
+	return Ext2_int_ReadInode(Node->ImplPtr, Node->Inode, Inode);
+}
+
+/**
+ * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)
+ * \brief Read an inode into memory
+ */
+int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode)
+{
+	 int	group, subId;
+	
+	//LogF("Ext2_int_ReadInode: (Disk=%p, InodeId=%i, Inode=%p)", Disk, InodeId, Inode);
+	//ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode);
+	
+	if(InodeId == 0)	return 0;
+	
+	InodeId --;	// Inodes are numbered starting at 1
+	
+	group = InodeId / Disk->SuperBlock.s_inodes_per_group;
+	subId = InodeId % Disk->SuperBlock.s_inodes_per_group;
+	
+	//LOG("group=%i, subId = %i", group, subId);
+	
+	// Read Inode
+	VFS_ReadAt(Disk->FD,
+		Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId,
+		sizeof(tExt2_Inode),
+		Inode);
+	return 1;
+}
+
+/**
+ * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)
+ * \brief Get the address of a block from an inode's list
+ * \param Disk	Disk information structure
+ * \param Blocks	Pointer to an inode's block list
+ * \param BlockNum	Block index in list
+ */
+Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum)
+{
+	Uint32	*iBlocks;
+	// Direct Blocks
+	if(BlockNum < 12)
+		return (Uint64)Blocks[BlockNum] * Disk->BlockSize;
+	
+	// Single Indirect Blocks
+	iBlocks = malloc( Disk->BlockSize );
+	VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks);
+	
+	BlockNum -= 12;
+	if(BlockNum < 256) {
+		BlockNum = iBlocks[BlockNum];
+		free(iBlocks);
+		return (Uint64)BlockNum * Disk->BlockSize;
+	}
+	
+	// Double Indirect Blocks
+	if(BlockNum < 256*256)
+	{
+		VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks);
+		VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/256]*Disk->BlockSize, Disk->BlockSize, iBlocks);
+		BlockNum = iBlocks[BlockNum%256];
+		free(iBlocks);
+		return (Uint64)BlockNum * Disk->BlockSize;
+	}
+	// Triple Indirect Blocks
+	VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks);
+	VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(256*256)]*Disk->BlockSize, Disk->BlockSize, iBlocks);
+	VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/256)%256]*Disk->BlockSize, Disk->BlockSize, iBlocks);
+	BlockNum = iBlocks[BlockNum%256];
+	free(iBlocks);
+	return (Uint64)BlockNum * Disk->BlockSize;
+}
+
+/**
+ * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)
+ * \brief Allocate an inode (from the current group preferably)
+ * \param Disk	EXT2 Disk Information Structure
+ * \param Parent	Inode ID of the parent (used to locate the child nearby)
+ */
+Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)
+{
+//	Uint	block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;
+	return 0;
+}
+
+/**
+ * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)
+ * \brief Updates the superblock
+ */
+void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk)
+{
+	 int	bpg = Disk->SuperBlock.s_blocks_per_group;
+	 int	ngrp = Disk->SuperBlock.s_blocks_count / bpg;
+	 int	i;
+	 
+	// Update Primary
+	VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock);
+	
+	// Secondaries
+	// at Block Group 1, 3^n, 5^n, 7^n
+	
+	// 1
+	if(ngrp <= 1)	return;
+	VFS_WriteAt(Disk->FD, 1*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);
+	
+	// Powers of 3
+	for( i = 3; i < ngrp; i *= 3 )
+		VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);
+	
+	// Powers of 5
+	for( i = 5; i < ngrp; i *= 5 )
+		VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);
+	
+	// Powers of 7
+	for( i = 7; i < ngrp; i *= 7 )
+		VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock);
+}
diff --git a/Modules/FS_Ext2/ext2_common.h b/Modules/FS_Ext2/ext2_common.h
new file mode 100644
index 0000000000000000000000000000000000000000..07e838e51796ddf1d898e7f9fd27007de5ab4006
--- /dev/null
+++ b/Modules/FS_Ext2/ext2_common.h
@@ -0,0 +1,46 @@
+/*
+ * Acess OS
+ * Ext2 Driver Version 1
+ */
+/**
+ * \file ext2_common.h
+ * \brief Second Extended Filesystem Driver
+ */
+#ifndef _EXT2_COMMON_H
+#define _EXT2_COMMON_H
+#include <common.h>
+#include <vfs.h>
+#include "ext2fs.h"
+
+#define EXT2_UPDATE_WRITEBACK	1
+
+// === STRUCTURES ===
+typedef struct {
+	 int	FD;
+	 int	CacheID;
+	tVFS_Node	RootNode;
+	
+	tExt2_SuperBlock	SuperBlock;
+	 int	BlockSize;
+	 
+	 int	GroupCount;
+	tExt2_Group		Groups[];
+} tExt2_Disk;
+
+// === FUNCTIONS ===
+// --- Common ---
+extern void	Ext2_CloseFile(tVFS_Node *Node);
+extern int	Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode);
+extern Uint64	Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum);
+extern void	Ext2_int_UpdateSuperblock(tExt2_Disk *Disk);
+// --- Dir ---
+extern char	*Ext2_ReadDir(tVFS_Node *Node, int Pos);
+extern tVFS_Node	*Ext2_FindDir(tVFS_Node *Node, char *FileName);
+extern int	Ext2_MkNod(tVFS_Node *Node, char *Name, Uint Flags);
+// --- Read ---
+extern Uint64	Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
+extern int	Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode);
+// --- Write ---
+extern Uint64	Ext2_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
+
+#endif
diff --git a/Kernel/vfs/fs/fs_ext2.h b/Modules/FS_Ext2/ext2fs.h
similarity index 94%
rename from Kernel/vfs/fs/fs_ext2.h
rename to Modules/FS_Ext2/ext2fs.h
index f7c9ae15e2dfe57ee578b9555e17d9792061f8f2..8fd87ce415af3ae8830679d6e6362e2160b1dbf1 100644
--- a/Kernel/vfs/fs/fs_ext2.h
+++ b/Modules/FS_Ext2/ext2fs.h
@@ -1,8 +1,10 @@
 /**
  * Acess2
- * \file fs_ext2.h
+ * \file ext2fs.h
  * \brief EXT2 Filesystem Driver
  */
+#ifndef _EXT2FS_H_
+#define _EXT2FS_H_
 
 /**
  \name Inode Flag Values
@@ -88,7 +90,7 @@ struct ext2_inode_s {
 	Uint32 i_dtime; //!< Deletion Time
 	Uint16 i_gid;	//!< Group Id
 	Uint16 i_links_count;	//!< Links count
-	Uint32 i_blocks;	//!< Blocks count
+	Uint32 i_blocks;	//!< Number of blocks allocated for the file
 	Uint32 i_flags;	//!< File flags
 	union {
 		Uint32 linux_reserved1;	//!< Linux: Reserved
@@ -150,3 +152,5 @@ struct ext2_dir_entry_s {
 	Uint8	type;		//!< File Type
 	char	name[];		//!< File name
 };
+
+#endif
diff --git a/Modules/FS_Ext2/read.c b/Modules/FS_Ext2/read.c
new file mode 100644
index 0000000000000000000000000000000000000000..cb6649a6bef78904ae9283d9644e98bd72c264be
--- /dev/null
+++ b/Modules/FS_Ext2/read.c
@@ -0,0 +1,89 @@
+/*
+ * Acess OS
+ * Ext2 Driver Version 1
+ */
+/**
+ * \file read.c
+ * \brief Second Extended Filesystem Driver
+ * \todo Implement file full write support
+ */
+#define DEBUG	1
+#define VERBOSE	0
+#include "ext2_common.h"
+
+// === PROTOTYPES ===
+Uint64		Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
+ int		Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode);
+
+// === CODE ===
+/**
+ * \fn Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Read from a file
+ */
+Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+	tExt2_Disk	*disk = Node->ImplPtr;
+	tExt2_Inode	inode;
+	Uint64	base;
+	Uint	block;
+	Uint64	remLen;
+	
+	ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+	
+	// Get Inode
+	Ext2_int_GetInode(Node, &inode);
+	
+	// Sanity Checks
+	if(Offset >= inode.i_size) {
+		LEAVE('i', 0);
+		return 0;
+	}
+	if(Offset + Length > inode.i_size)
+		Length = inode.i_size - Offset;
+	
+	block = Offset / disk->BlockSize;
+	Offset = Offset / disk->BlockSize;
+	base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+	if(base == 0) {
+		Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode);
+		LEAVE('i', 0);
+		return 0;
+	}
+	
+	// Read only block
+	if(Length <= disk->BlockSize - Offset)
+	{
+		VFS_ReadAt( disk->FD, base+Offset, Length, Buffer);
+		LEAVE('X', Length);
+		return Length;
+	}
+	
+	// Read first block
+	remLen = Length;
+	VFS_ReadAt( disk->FD, base + Offset, disk->BlockSize - Offset, Buffer);
+	remLen -= disk->BlockSize - Offset;
+	Buffer += disk->BlockSize - Offset;
+	block ++;
+	
+	// Read middle blocks
+	while(remLen > disk->BlockSize)
+	{
+		base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+		if(base == 0) {
+			Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode);
+			LEAVE('i', 0);
+			return 0;
+		}
+		VFS_ReadAt( disk->FD, base, disk->BlockSize, Buffer);
+		Buffer += disk->BlockSize;
+		remLen -= disk->BlockSize;
+		block ++;
+	}
+	
+	// Read last block
+	base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+	VFS_ReadAt( disk->FD, base, remLen, Buffer);
+	
+	LEAVE('X', Length);
+	return Length;
+}
diff --git a/Modules/FS_Ext2/write.c b/Modules/FS_Ext2/write.c
new file mode 100644
index 0000000000000000000000000000000000000000..f3fdc6b372ff6cece9bc4d7faeb6a8c0f40df6f5
--- /dev/null
+++ b/Modules/FS_Ext2/write.c
@@ -0,0 +1,176 @@
+/*
+ * Acess OS
+ * Ext2 Driver Version 1
+ */
+/**
+ * \file write.c
+ * \brief Second Extended Filesystem Driver
+ * \todo Implement file full write support
+ */
+#define DEBUG	1
+#define VERBOSE	0
+#include "ext2_common.h"
+
+// === PROTOYPES ===
+Uint64		Ext2_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
+Uint32		Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock);
+
+// === CODE ===
+/**
+ * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ * \brief Write to a file
+ */
+Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+{
+	tExt2_Disk	*disk = Node->ImplPtr;
+	tExt2_Inode	inode;
+	Uint64	base;
+	Uint64	retLen;
+	Uint	block;
+	Uint64	allocSize;
+	 int	bNewBlocks = 0;
+	
+	Debug_HexDump("Ext2_Write", Buffer, Length);
+	
+	Ext2_int_GetInode(Node, &inode);
+	
+	// Get the ammount of space already allocated
+	// - Round size up to block size
+	// - block size is a power of two, so this will work
+	allocSize = (inode.i_size + disk->BlockSize) & ~(disk->BlockSize-1);
+	
+	// Are we writing to inside the allocated space?
+	if( Offset < allocSize )
+	{
+		// Will we go out of it?
+		if(Offset + Length > allocSize) {
+			bNewBlocks = 1;
+			retLen = allocSize - Offset;
+		} else
+			retLen = Length;
+		
+		// Within the allocated space
+		block = Offset / disk->BlockSize;
+		Offset %= disk->BlockSize;
+		base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+		
+		// Write only block (if only one)
+		if(Offset + retLen <= disk->BlockSize) {
+			VFS_WriteAt(disk->FD, base+Offset, retLen, Buffer);
+			if(!bNewBlocks)	return Length;
+			goto addBlocks;	// Ugh! A goto, but it seems unavoidable
+		}
+		
+		// Write First Block
+		VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer);
+		Buffer += disk->BlockSize-Offset;
+		retLen -= disk->BlockSize-Offset;
+		block ++;
+		
+		// Write middle blocks
+		while(retLen > disk->BlockSize)
+		{
+			base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+			VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
+			Buffer += disk->BlockSize;
+			retLen -= disk->BlockSize;
+			block ++;
+		}
+		
+		// Write last block
+		base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
+		VFS_WriteAt(disk->FD, base, retLen, Buffer);
+		if(!bNewBlocks)	return Length;	// Writing in only allocated space
+	}
+	
+addBlocks:
+	///\todo Implement block allocation
+	Warning("[EXT2] File extending is not yet supported");
+	
+	return 0;
+}
+
+/**
+ * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
+ * \brief Allocate a block from the best possible location
+ * \param Disk	EXT2 Disk Information Structure
+ * \param PrevBlock	Previous block ID in the file
+ */
+Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
+{
+	 int	bpg = Disk->SuperBlock.s_blocks_per_group;
+	Uint	blockgroup = PrevBlock / bpg;
+	Uint	bitmap[Disk->BlockSize/sizeof(Uint)];
+	Uint	bitsperblock = 8*Disk->BlockSize;
+	 int	i, j = 0;
+	Uint	block;
+	
+	// Are there any free blocks?
+	if(Disk->SuperBlock.s_free_blocks_count == 0)	return 0;
+	
+	if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)
+	{
+		// Search block group's bitmap
+		for(i = 0; i < bpg; i++)
+		{
+			// Get the block in the bitmap block
+			j = i & (bitsperblock-1);
+			
+			// Read in if needed
+			if(j == 0) {
+				VFS_ReadAt(
+					Disk->FD,
+					(Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
+					Disk->BlockSize,
+					bitmap
+					);
+			}
+			
+			// Fast Check
+			if( bitmap[j/32] == -1 ) {
+				j = (j + 31) & ~31;
+				continue;
+			}
+			
+			// Is the bit set?
+			if( bitmap[j/32] & (1 << (j%32)) )
+				continue;
+			
+			// Ooh! We found one
+			break;
+		}
+		if( i < bpg ) {
+			Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist");
+			goto	checkAll;	// Search the entire filesystem for a free block
+			// Goto needed for neatness
+		}
+		
+		// Mark as used
+		bitmap[j/32] |= (1 << (j%32));
+		VFS_WriteAt(
+			Disk->FD,
+			(Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
+			Disk->BlockSize,
+			bitmap
+			);
+		block = i;
+		Disk->Groups[blockgroup].bg_free_blocks_count --;
+		#if EXT2_UPDATE_WRITEBACK
+		//Ext2_int_UpdateBlockGroup(blockgroup);
+		#endif
+	}
+	else
+	{
+	checkAll:
+		Warning("[EXT2 ] TODO - Implement using blocks outside the current block group");
+		return 0;
+	}
+	
+	// Reduce global count
+	Disk->SuperBlock.s_free_blocks_count --;
+	#if EXT2_UPDATE_WRITEBACK
+	Ext2_int_UpdateSuperblock(Disk);
+	#endif
+	
+	return block;
+}
diff --git a/Modules/USB/uhci.c b/Modules/USB/uhci.c
index 65037ef0b4ef7372e03797590a6d25300d3b54de..2f1950a6630ae17395ec8d817968ad27c2e723d6 100644
--- a/Modules/USB/uhci.c
+++ b/Modules/USB/uhci.c
@@ -60,8 +60,8 @@ int UHCI_Initialise()
 	if(i == MAX_CONTROLLERS) {
 		Warning("[UHCI ] Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest");
 	}
-	LEAVE('i', 0);
-	return 0;
+	LEAVE('i', i);
+	return i;
 }
 
 /**
@@ -71,13 +71,13 @@ int UHCI_Initialise()
 void UHCI_Cleanup()
 {
 }
-
-/**
- * \fn int UHCI_IOCtl(tVFS_Node *Node, int ID, void *Data)
- */
-int UHCI_IOCtl(tVFS_Node *Node, int ID, void *Data)
-{
-	return 0;
+
+/**
+ * \brief Sends a packet to a device endpoint
+ */
+int UHCI_SendPacket(int ControllerId, int Device, int Endpoint, void *Data, int Length)
+{
+	return 0;
 }
 
 // === INTERNAL FUNCTIONS ===
diff --git a/Modules/USB/usb.h b/Modules/USB/usb.h
index 3e503ec4b4b43c09cafb5a3e044e936758c947cb..1ccf98c7f621cd884251a1e7a0d32c0af9d1ff2e 100644
--- a/Modules/USB/usb.h
+++ b/Modules/USB/usb.h
@@ -9,6 +9,68 @@
 typedef struct sUSBHost	tUSBHost;
 typedef struct sUSBDevice	tUSBDevice;
 
+// === CONSTANTS ===
+enum eUSB_PIDs
+{
+	/**
+	 * \name Token
+	 * \{
+	 */
+	PID_OUT		= 0xE1,
+	PID_IN		= 0x69,
+	PID_SOF		= 0xA5,
+	PID_SETUP	= 0x2D,
+	/**
+	 * \}
+	 */
+	
+	/**
+	 * \name Data
+	 * \{
+	 */
+	PID_DATA0	= 0xC3,
+	PID_DATA1	= 0x4B,
+	PID_DATA2	= 0x87,	// USB2 only
+	PID_MDATA	= 0x0F,	// USB2 only
+	/**
+	 * \}
+	 */
+	
+	/**
+	 * \name Handshake
+	 * \{
+	 */
+	PID_ACK		= 0xD2,
+	PID_NAK		= 0x5A,
+	PID_STALL	= 0x1E,
+	PID_NYET	= 0x96,
+	/**
+	 * \}
+	 */
+	
+	/**
+	 * \name Special
+	 * \{
+	 */
+	PID_PRE		= 0x3C, PID_ERR		= 0x3C,
+	PID_SPLIT	= 0x78,
+	PID_PING	= 0xB4,
+	PID_RESVD	= 0xF0,
+	/**
+	 * \}
+	 */
+};
+
+// === FUNCTIONS ===
+/**
+ * \note 00101 - X^5+X^2+1
+ */
+Uint8	USB_TokenCRC(void *Data, int len);
+/**
+ * \note X^16 + X15 + X^2 + 1
+ */
+Uint16	USB_TokenCRC(void *Data, int len);
+
 // === STRUCTURES ===
 /**
  * \brief Defines a USB Host Controller
diff --git a/README b/README
index 61e69d24a68c47f610e137c7f44efd51ffd7de4b..6890ba3c1047d3e812a10c9a4bd3dca60038511b 100644
--- a/README
+++ b/README
@@ -40,5 +40,10 @@ DRIVERS defines what device drivers are to be included from the Kernel
 MODULES defines what modules should be statically linked with the kernel
   (see /Modules for a list)
 
-Finally set the source root directory (ACESSDIR) and the destination
-  directory (DISTROOT) then call make in the source root.
+Set the source root directory (ACESSDIR) and the destination  directory
+ (DISTROOT).
+
+Now you can compile the kernel and usermode applications by calling make
+ in the source root.
+
+
diff --git a/Usermode/Libraries/libc.so_src/lib.h b/Usermode/Libraries/libc.so_src/lib.h
index 49c5eb4c2ada9e1596093093d76fc0b8f1fed1a8..9f773dcec32030b72b9d7ae598f10e5c92193024 100644
--- a/Usermode/Libraries/libc.so_src/lib.h
+++ b/Usermode/Libraries/libc.so_src/lib.h
@@ -1,11 +1,13 @@
 /*
-AcessOS Basic Lib C
-
-lib.h
+ * Acess2 Basic Lib C
+ * lib.h
 */
 #ifndef _LIB_H
 #define _LIB_H
 
+#include <stddef.h>
+#include <stdint.h>
+
 #define BUILD_SO	1
 
 #if defined(BUILD_DLL)
@@ -18,4 +20,12 @@ lib.h
 
 extern void *memcpy(void *dest, const void *src, size_t n);
 
+typedef struct sCPUID	tCPUID;
+
+struct sCPUID
+{
+	uint8_t	SSE;
+	uint8_t	SSE2;
+};
+
 #endif
diff --git a/Usermode/Libraries/libc.so_src/stub.c b/Usermode/Libraries/libc.so_src/stub.c
index a10d9d1114472a8c2742d98e20cb12fb37b50336..0233b977d9e9674591f6c9eb38dc3b6a3e00aee2 100644
--- a/Usermode/Libraries/libc.so_src/stub.c
+++ b/Usermode/Libraries/libc.so_src/stub.c
@@ -2,13 +2,21 @@
  * AcessOS Basic C Library
  */
 #include "stdio_int.h"
+#include "lib.h"
 
+// === PROTOTYPES ===
+static void	cpuid(uint32_t Num, uint32_t *EAX, uint32_t *EBX, uint32_t *EDX, uint32_t *ECX);
+
+// === GLOBALS ===
 extern char **_envp;
 extern struct sFILE	_iob[];
 extern struct sFILE	*stdin;
 extern struct sFILE	*stdout;
 extern struct sFILE	*stderr;
+// --- CPU Features ---
+tCPUID	gCPU_Features;
 
+// === CODE ===
 /**
  * \fn int SoMain()
  * \brief Stub Entrypoint
@@ -30,5 +38,34 @@ int SoMain(unsigned int BaseAddress, int argc, char **argv, char **envp)
 	stderr = &_iob[2];
 	stderr->FD = 2;	stderr->Flags = FILE_FLAG_MODE_WRITE;
 	
+	/*
+	{
+		uint32_t	ecx, edx;
+		cpuid(1, NULL, NULL, &edx, &ecx);
+		gCPU_Features.SSE  = edx & (1 << 25);	// SSE
+		gCPU_Features.SSE2 = edx & (1 << 26);	// SSE2
+	}
+	*/
+	
 	return 1;
 }
+
+
+/**
+ * \brief Call the CPUID opcode
+ */
+static void cpuid(uint32_t Num, uint32_t *EAX, uint32_t *EBX, uint32_t *EDX, uint32_t *ECX)
+{
+	uint32_t	eax, ebx, edx, ecx;
+	
+	__asm__ __volatile__ (
+		"cpuid"
+		: "=a"(eax), "=c"(ecx), "=d"(edx)
+		: "a"(Num)
+		);
+	
+	if(EAX)	*EAX = eax;
+	if(EBX)	*EBX = ebx;
+	if(EDX)	*EDX = edx;
+	if(ECX)	*ECX = ecx;
+}
diff --git a/Usermode/include/acess/devices.h b/Usermode/include/acess/devices.h
index 1039430e9dace5f4caaf890630eeebb9bb10184b..bfbb625658cced74a048bd6feaecadb1c1363328 100644
--- a/Usermode/include/acess/devices.h
+++ b/Usermode/include/acess/devices.h
@@ -44,4 +44,7 @@ enum eDrv_Mouse {
 	MSE_IOCTL_MAX_Y
 };
 
+// === Terminal ===
+#include "devices/terminal.h"
+
 #endif
diff --git a/Usermode/include/acess/devices/terminal.h b/Usermode/include/acess/devices/terminal.h
new file mode 100644
index 0000000000000000000000000000000000000000..473271ca110db53f3a40aeabba666713942b7cb0
--- /dev/null
+++ b/Usermode/include/acess/devices/terminal.h
@@ -0,0 +1,58 @@
+/**
+ * \file devices/terminal.h
+ */
+#ifndef _SYS_DEVICES_TERMINAL_H
+#define _SYS_DEVICES_TERMINAL_H
+
+
+enum eDrv_Terminal {
+	TERM_IOCTL_MODETYPE = 4,
+	TERM_IOCTL_WIDTH,
+	TERM_IOCTL_HEIGHT,
+	TERM_IOCTL_QUERYMODE
+};
+
+
+struct sTerm_IOCtl_Mode
+{
+	short	ID;		//!< Zero Based index of mode
+	short	DriverID;	//!< Driver's ID number (from ::tVideo_IOCtl_Mode)
+	Uint16	Height;	//!< Height
+	Uint16	Width;	//!< Width
+	Uint8	Depth;	//!< Bits per cell
+	struct {
+		unsigned bText: 1;	//!< Text Mode marker
+		unsigned unused:	7;
+	};
+};
+
+/**
+ * \brief Terminal Modes
+ */
+enum eTplTerminal_Modes {
+	/**
+	 * \brief UTF-8 Text Mode
+	 * Any writes to the terminal file are treated as UTF-8 encoded
+	 * strings and reads will also return UTF-8 strings.
+	 */
+	TERM_MODE_TEXT,
+	
+	/**
+	 * \brief 32bpp Framebuffer
+	 * Writes to the terminal file will write to the framebuffer.
+	 * Reads will return UTF-32 characters
+	 */
+	TERM_MODE_FB,
+	
+	/**
+	 * \brief OpenGL 2D/3D
+	 * Writes to the terminal file will send 3D commands
+	 * Reads will return UTF-32 characters
+	 * \note May or may not stay in the spec
+	 */
+	TERM_MODE_OPENGL,
+	
+	NUM_TERM_MODES
+};
+
+#endif