diff --git a/.gitignore b/.gitignore
index 7ed9ebec100052cf4ced1bc16a6993df01e65523..365eb6c6c9d11cf0c010b9b06923629f13015e03 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@ serial.txt
 *.gz
 *.img
 *.vmdk
+*.iso
 SrcDoc/
 APIDoc/
 Usermode/*/*/UTEST/stage
diff --git a/.travis.yml b/.travis.yml
index d39dc5d2a1d77978cfe601b75985cdba389c5d2a..52dc0327297c08d4d44e918c17245fd0c1434e3b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,4 +6,6 @@ compiler: clang
 #env:
 # - ARCH=host HOST_ARCH=x86 USE_ACPICA=0
 # - ARCH=host HOST_ARCH=x86_64 CC="$CC -m64"
-script: "make utest mtest"
+script:
+ - "make utest-build mtest-build"
+ - "make -k utest-run mtest-run"
diff --git a/AcessNative/Makefile b/AcessNative/Makefile
index 7448f4fd9f87e85e506b4d5d1fc560d9475dcd4e..8d5d6c4caef0f24d66d28b5b036d56f101fea3e5 100644
--- a/AcessNative/Makefile
+++ b/AcessNative/Makefile
@@ -1,6 +1,10 @@
 
 .PHONY: all clean
 
+V ?= @
+
 all clean:
 	@$(MAKE) -C acesskernel_src $@
 	@$(MAKE) -C ld-acess_src $@
+	@$(MAKE) -C libacess-native.so_src $@	
+	@$(MAKE) -C .. $@-user ARCH=native V=$(V)
diff --git a/AcessNative/RunNative b/AcessNative/RunNative
new file mode 100755
index 0000000000000000000000000000000000000000..406573db0d20b68b544347d7071819907ad69e60
--- /dev/null
+++ b/AcessNative/RunNative
@@ -0,0 +1,42 @@
+#!/bin/bash
+#
+# Execute the specified root application using the ARCH=native build
+#
+_=$PWD; cd $(dirname $0); DIR=$PWD; cd $_
+DISTROOT=$(dirname $DIR)/Usermode/Output/native/
+VTERM=/Devices/pts/vt0
+echo $DISTROOT
+
+KERNEL_PID=$$
+function cleanup {
+	trap '' SIGINT
+	echo Cleaning up $KERNEL_PID
+	kill -INT $KERNEL_PID
+}
+
+trap cleanup SIGINT
+
+# 1. Start up AcessKernel
+# - Set DISTROOT to the output directory of ARCH=native
+# - Don't start a root application
+${DIR}/AcessKernel --distroot $DISTROOT > ${DIR}/log/native_AcessKernel.log 2>&1 &
+KERNEL_PID=$!
+echo Kernel is $KERNEL_PID
+sleep 1
+
+APP=gui4
+
+case $APP in
+gui4)
+	LD_LIBRARY_PATH=${DIR}:${DISTROOT}Libs AN_PREOPEN=$VTERM:$VTERM:$VTERM ${DBG} ${DISTROOT}Apps/AxWin/4.0/AxWinServer
+	;;
+gui3)
+	LD_LIBRARY_PATH=${DIR}:${DISTROOT}Libs AN_PREOPEN=$VTERM:$VTERM:$VTERM ${DBG} ${DISTROOT}Apps/AxWin/3.0/AxWinWM
+	;;
+*)
+	echo "Unknown application '$APP'"
+esac
+
+trap '' SIGINT
+cleanup
+
diff --git a/AcessNative/RunRootApp b/AcessNative/RunRootApp
index 39a30fac5638011843c422867c2ded7ed82a9fd7..a3d31abc16482475bb4625e7e6c0e236a45ffa20 100755
--- a/AcessNative/RunRootApp
+++ b/AcessNative/RunRootApp
@@ -1,3 +1,7 @@
 #!/bin/sh
 
+#
+# Spin up an application bound to VTerm 0 using ./ld-acess
+#
+
 $DBG ./ld-acess --open /Devices/VTerm/0 --open /Devices/VTerm/0 --open /Devices/VTerm/0 $*
diff --git a/AcessNative/RunTest b/AcessNative/RunTest
index 6ea88b673b2410b57d20c7247da2cde18394a643..069ea14f066b7ec6434dc3fe4c112df84df2bce9 100755
--- a/AcessNative/RunTest
+++ b/AcessNative/RunTest
@@ -1,4 +1,7 @@
 #!/bin/sh
+#
+# Runs the AcessNative kernel and spawns a native executable using ./ld-acess
+#
 trap '' 2
 #$1 ./AcessKernel --rootapp /Acess/SBin/login
 $1 ./AcessKernel --rootapp /Acess/Apps/AxWin/3.0/AxWinWM
diff --git a/AcessNative/acesskernel_src/Makefile b/AcessNative/acesskernel_src/Makefile
index 9e8ce35fd33ffb7c7d5397eec74e054ee6c59e58..11976045622715104c0af6c222de12ebcbb09751 100644
--- a/AcessNative/acesskernel_src/Makefile
+++ b/AcessNative/acesskernel_src/Makefile
@@ -15,7 +15,7 @@ KERNEL_SRC = ../../KernelLand/Kernel/
 LDACESS_SRC = ../../Usermode/Libraries/ld-acess.so_src/
 
 # - Kernel objects (from KernelLand/Kernel)
-KERNEL_OBJ := logging.o adt.o lib.o debug.o messages.o drvutil_disk.o drvutil_video.o
+KERNEL_OBJ := logging.o adt.o lib.o debug.o messages.o drvutil_disk.o drvutil_video.o memfs_helpers.o
 KERNEL_OBJ += mutex.o semaphore.o rwlock.o workqueue.o events.o
 #KERNEL_OBJ += libc.o
 KERNEL_OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/io.o vfs/dir.o
@@ -30,6 +30,7 @@ N_OBJ := main.o net_wrap.o
 # - Local objects (use the kernel includes)
 OBJ := helpers.o threads.o threads_glue.o server.o syscalls.o time.o
 OBJ += video.o keyboard.o mouse.o nativefs.o vfs_handle.o ui_sdl.o
+#OBJ += shm.o
 OBJ += net.o syscall_getpath.o
 
 BUILDINFO_OBJ := obj-$(PLATFORM)/buildinfo.o
@@ -46,6 +47,7 @@ KCPPFLAGS = -I include/ -I $(KERNEL_SRC)include/ -I$(LDACESS_SRC)include_exp/ -D
 CFLAGS += -Wall -g -std=gnu99
 CPPFLAGS += $(shell sdl-config --cflags) -I /usr/include/
 LDFLAGS += $(shell sdl-config --libs) -g -Wl,--defsym,__buildnum=$(BUILD_NUM)
+LDFLAGS += -Wl,-Map,obj-$(PLATFORM)/Map.txt
 
 ifeq ($(PLATFORM),win)
 	BIN := ../AcessKernel.exe
diff --git a/AcessNative/acesskernel_src/include/arch.h b/AcessNative/acesskernel_src/include/arch.h
index ab27de1f1ea0996448d468b0de7a75b123186e23..868a0569c424934ea761926eff33e6211291c17e 100644
--- a/AcessNative/acesskernel_src/include/arch.h
+++ b/AcessNative/acesskernel_src/include/arch.h
@@ -28,6 +28,7 @@ typedef intptr_t	tPAddr;
 
 typedef	int	BOOL;
 
+extern void	exit(int status) __attribute__((noreturn));
 #define HALT_CPU()	exit(1)
 
 #include <stddef.h>
diff --git a/AcessNative/acesskernel_src/main.c b/AcessNative/acesskernel_src/main.c
index ead4f33d9901c866db962c79b0f4f91d06f013b0..4f5de2399d7f501d8c61218224b251253ec25a28 100644
--- a/AcessNative/acesskernel_src/main.c
+++ b/AcessNative/acesskernel_src/main.c
@@ -13,6 +13,7 @@
 #endif
 #include <unistd.h>
 #include <string.h>
+#include <stdbool.h>
 #include "../../KernelLand/Kernel/include/logdebug.h"
 
 #define VALGRIND_CLIENT	0
@@ -115,7 +116,7 @@ int main(int argc, char *argv[])
 	VFS_MkDir("/Acess");	
 	VFS_Mount(gsAcessDir, "/Acess", "nativefs", "");
 
-	Debug_SetKTerminal("/Devices/pts/vt7c");
+	Debug_SetKTerminal("/Devices/pts/vt7");
 	
 	// Start syscall server
 	SyscallServer();
diff --git a/AcessNative/acesskernel_src/net_wrap.c b/AcessNative/acesskernel_src/net_wrap.c
index 730eda0232f4e72d19c88cbf4419d5da0c13eee9..33a5cf91d410a89885e8719152c9ff4afb62f9b5 100644
--- a/AcessNative/acesskernel_src/net_wrap.c
+++ b/AcessNative/acesskernel_src/net_wrap.c
@@ -8,6 +8,7 @@
 #define DEBUG	1
 #include <stdlib.h>
 #include <unistd.h>
+#include <stdbool.h>
 #include "../../KernelLand/Kernel/include/logdebug.h"
 #include "net_wrap.h"
 #include <string.h>
diff --git a/AcessNative/acesskernel_src/server.c b/AcessNative/acesskernel_src/server.c
index f3826db79f0425cda62c3e85d0aea51d83893c19..4dd964ca155166adc9483aedbce8f00be2ce7aa2 100644
--- a/AcessNative/acesskernel_src/server.c
+++ b/AcessNative/acesskernel_src/server.c
@@ -6,6 +6,7 @@
  */
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <string.h>
 #include <SDL/SDL.h>
 #ifdef __WIN32__
@@ -227,6 +228,13 @@ int SyscallServer(void)
 	server.sin_port = htons(SERVER_PORT);
 	server.sin_addr.s_addr = htonl(INADDR_ANY);
 	
+	#if USE_TCP
+	{
+		int val = 1;
+		setsockopt(gSocket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val);
+	}
+	#endif
+	
 	// Bind
 	if( bind(gSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1 )
 	{
diff --git a/AcessNative/acesskernel_src/threads.c b/AcessNative/acesskernel_src/threads.c
index 86b8a892780f2cfc5102542ed658e45e8c4635e6..74d85fc60c078c54bfdc59fae473d26be74fd0f5 100644
--- a/AcessNative/acesskernel_src/threads.c
+++ b/AcessNative/acesskernel_src/threads.c
@@ -199,9 +199,10 @@ int Threads_SetGID(tGID NewGID)
 }
 
 int *Threads_GetErrno(void) { return &gpCurrentThread->_errno; }
-char **Threads_GetCWD(void) { return &gpCurrentThread->Process->CWD; }
-char **Threads_GetChroot(void) { return &gpCurrentThread->Process->Chroot; }
-int *Threads_GetMaxFD(void) { return &gpCurrentThread->Process->MaxFD; };
+static tProcess *proc(tProcess *Proc) { return Proc ? Proc : gpCurrentThread->Process; }
+char **Threads_GetCWD   (tProcess *Proc) { return &proc(Proc)->CWD; }
+char **Threads_GetChroot(tProcess *Proc) { return &proc(Proc)->Chroot; }
+int *Threads_GetMaxFD   (tProcess *Proc) { return &proc(Proc)->MaxFD; };
 
 tTID Threads_WaitTID(int TID, int *Status)
 {
diff --git a/AcessNative/acesskernel_src/threads_glue.c b/AcessNative/acesskernel_src/threads_glue.c
index fa83980e38890f8f707b83f73b87c1ba30384a67..7c1378040763fd0565ae7a3d0decf4c15456175a 100644
--- a/AcessNative/acesskernel_src/threads_glue.c
+++ b/AcessNative/acesskernel_src/threads_glue.c
@@ -20,6 +20,7 @@ typedef void	**tShortSpinlock;
 #include <pthread.h>
 
 #define NORETURN	__attribute__((noreturn))
+#include <stdbool.h>
 #include <logdebug.h>	// Kernel land, but uses standards
 #include <errno.h>
 
diff --git a/AcessNative/acesskernel_src/vfs_handle.c b/AcessNative/acesskernel_src/vfs_handle.c
index 6c3992f8cb39e2ff898a1bc17b011cfae1d64a2b..93b6a9d674d7354fbb65b8cce57566f1fa7eda6c 100644
--- a/AcessNative/acesskernel_src/vfs_handle.c
+++ b/AcessNative/acesskernel_src/vfs_handle.c
@@ -79,7 +79,7 @@ void VFS_CloneHandleList(int PID)
 	
 	ent = VFS_int_GetUserHandles(PID, 1);
 	
-	maxhandles = *Threads_GetMaxFD();
+	maxhandles = *Threads_GetMaxFD(NULL);
 	memcpy(ent->Handles, cur->Handles, maxhandles*sizeof(tVFS_Handle));
 	
 	// Reference all
@@ -105,7 +105,7 @@ void VFS_CloneHandlesFromList(int PID, int nFD, int FDs[])
 
 	LOG("Copying %i FDs from %i", nFD, PID);
 
-	maxhandles = *Threads_GetMaxFD();
+	maxhandles = *Threads_GetMaxFD(NULL);
 	if( nFD > maxhandles )
 		nFD = maxhandles;
 	for( int i = 0; i < nFD; i ++ )
@@ -156,7 +156,7 @@ tVFS_Handle *VFS_GetHandle(int FD)
 	else
 	{
 		 int	pid = Threads_GetPID();
-		 int	maxhandles = *Threads_GetMaxFD();
+		 int	maxhandles = *Threads_GetMaxFD(NULL);
 		
 		tUserHandles *ent = VFS_int_GetUserHandles(pid, 0);
 		if(!ent) {
@@ -193,7 +193,7 @@ int VFS_SetHandle(int FD, tVFS_Node *Node, int Mode)
 	else {
 		tUserHandles	*ent;
 		 int	pid = Threads_GetPID();
-		 int	maxhandles = *Threads_GetMaxFD();
+		 int	maxhandles = *Threads_GetMaxFD(NULL);
 		
 		ent = VFS_int_GetUserHandles(pid, 0);
 		if(!ent) {
@@ -219,7 +219,7 @@ int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode)
 	if(bIsUser)
 	{
 		tUserHandles	*ent;
-		 int	maxhandles = *Threads_GetMaxFD();
+		 int	maxhandles = *Threads_GetMaxFD(NULL);
 		// Find the PID's handle list
 		ent = VFS_int_GetUserHandles(Threads_GetPID(), 1);
 		// Get a handle
@@ -254,7 +254,7 @@ void VFS_ClearHandles(int PID)
 	tUserHandles *ent = VFS_int_GetUserHandles(PID, 0);
 	if( !ent )	return;
 	// Get a handle
-	 int	maxhandles = *Threads_GetMaxFD();
+	 int	maxhandles = *Threads_GetMaxFD(NULL);
 	for( int i = 0; i < maxhandles; i ++ )
 	{
 		if(ent->Handles[i].Node)	continue;
diff --git a/AcessNative/acesskernel_src/video.c b/AcessNative/acesskernel_src/video.c
index b1b65b01e6fdc90ce99cdd98c78f0be5459ef2fa..d78f6dadbf3ce7aceb309d1f9d48130cb8f8742f 100644
--- a/AcessNative/acesskernel_src/video.c
+++ b/AcessNative/acesskernel_src/video.c
@@ -30,7 +30,9 @@ tVFS_NodeType	gVideo_NodeType = {
 };
 tDevFS_Driver	gVideo_DriverStruct = {
 	NULL, "NativeVideo",
-	{.Type = &gVideo_NodeType}
+	{
+		.Type = &gVideo_NodeType
+	}
 };
  int	giVideo_DriverID;
  int	giVideo_CurrentFormat;
@@ -255,6 +257,8 @@ int Video_IOCtl(tVFS_Node *Node, int ID, void *Data)
 	// Video mode control
 	// - We cheat, and only have one mode
 	case VIDEO_IOCTL_GETSETMODE:
+		// - Abuse GETSETMODE to update size
+		Node->Size = giUI_Pitch * giUI_Height;
 		return 0;
 	case VIDEO_IOCTL_FINDMODE:
 	case VIDEO_IOCTL_MODEINFO:
diff --git a/AcessNative/ld-acess_src/binary.c b/AcessNative/ld-acess_src/binary.c
index 59be8295b63de629475a262683053247629ccb20..738c6e819c77966fd36e33647af012883c3ab399 100644
--- a/AcessNative/ld-acess_src/binary.c
+++ b/AcessNative/ld-acess_src/binary.c
@@ -6,6 +6,7 @@
  * - Provides binary loading and type abstraction
  */
 #define DEBUG	0
+#define _POSIX_C_SOURCE	200809L	// needed for strdup
 #include "common.h"
 #include <stdint.h>
 #include <stdio.h>
@@ -228,7 +229,7 @@ void Binary_SetReadyToUse(void *Base)
 	}
 }
 
-int Binary_GetSymbol(const char *SymbolName, uintptr_t *Value, size_t *Size)
+int Binary_GetSymbol(const char *SymbolName, uintptr_t *Value, size_t *Size, void *IgnoreBase)
 {
 	 int	i;
 	tBinary	*bin;
@@ -251,6 +252,7 @@ int Binary_GetSymbol(const char *SymbolName, uintptr_t *Value, size_t *Size)
 	// Search list of loaded binaries
 	for(bin = gLoadedBinaries; bin; bin = bin->Next)
 	{
+		if( bin->Base == IgnoreBase )	continue ;
 		if( !bin->Ready )	continue;
 		//printf(" Binary_GetSymbol: bin = %p{%p, %s}\n", bin, bin->Base, bin->Path);
 		if( bin->Format->GetSymbol(bin->Base, (char*)SymbolName, Value, Size) )
diff --git a/AcessNative/ld-acess_src/common.h b/AcessNative/ld-acess_src/common.h
index 9f67e9c63d67e3df582612ab9f363073f957109b..0bdc40f247af7cd30556986bda41fff22f2481d1 100644
--- a/AcessNative/ld-acess_src/common.h
+++ b/AcessNative/ld-acess_src/common.h
@@ -8,16 +8,16 @@
 #include <stdint.h>
 #include <string.h>
 
-extern int	Binary_GetSymbol(const char *SymbolName, uintptr_t *Value, size_t *Size);
+extern int	Binary_GetSymbol(const char *SymbolName, uintptr_t *Value, size_t *Size, void *IgnoreBase);
 extern void	*Binary_LoadLibrary(const char *Path);
 extern void	*Binary_Load(const char *Path, uintptr_t *EntryPoint);
 extern void	Binary_SetReadyToUse(void *Base);
 
 // HACKS - So this can share the usermode elf.c
-static inline int GetSymbol(const char *sym, void **val, size_t *sz)
+static inline int GetSymbol(const char *sym, void **val, size_t *sz, void *IgnoreBase)
 {
 	uintptr_t rv;
-	if( !Binary_GetSymbol(sym, &rv, sz) )
+	if( !Binary_GetSymbol(sym, &rv, sz, IgnoreBase) )
 		return 0;
 	*val = (void*)rv;
 	return 1;
diff --git a/AcessNative/ld-acess_src/elf_load.c b/AcessNative/ld-acess_src/elf_load.c
index 19a8c13fc945255552d3afdc5e93308fd666dbb7..76d92519a8b5ddf2f631d5228838e8c3c43fa718 100644
--- a/AcessNative/ld-acess_src/elf_load.c
+++ b/AcessNative/ld-acess_src/elf_load.c
@@ -64,17 +64,10 @@ void *Elf_Load(int FD)
 
 void *Elf32Load(int FD, Elf32_Ehdr *hdr)
 {
-	Elf32_Phdr	*phtab;
-	 int	i;
-	 int	iPageCount;
-	uint32_t	max, base;
-	uint32_t	addr;
-	uint32_t	baseDiff = 0;
-	
 	ENTER("iFD", FD);
 	
 	// Check for a program header
-	if(hdr->phoff == 0) {
+	if(hdr->e_phoff == 0) {
 		#if DEBUG_WARN
 		Warning("ELF File does not contain a program header\n");
 		#endif
@@ -83,25 +76,25 @@ void *Elf32Load(int FD, Elf32_Ehdr *hdr)
 	}
 	
 	// Read Program Header Table
-	phtab = malloc( sizeof(Elf32_Phdr) * hdr->phentcount );
+	Elf32_Phdr* phtab = malloc( sizeof(Elf32_Phdr) * hdr->e_phnum );
 	if( !phtab ) {
 		LEAVE('n');
 		return NULL;
 	}
-	LOG("hdr.phoff = 0x%08x\n", hdr->phoff);
-	acess__SysSeek(FD, hdr->phoff, ACESS_SEEK_SET);
-	acess__SysRead(FD, phtab, sizeof(Elf32_Phdr) * hdr->phentcount);
+	LOG("hdr.e_phoff = 0x%08x\n", hdr->e_phoff);
+	acess__SysSeek(FD, hdr->e_phoff, ACESS_SEEK_SET);
+	acess__SysRead(FD, phtab, sizeof(Elf32_Phdr) * hdr->e_phnum);
 	
 	// Count Pages
-	iPageCount = 0;
-	LOG("hdr.phentcount = %i\n", hdr->phentcount);
-	for( i = 0; i < hdr->phentcount; i++ )
+	unsigned int iPageCount = 0;
+	LOG("hdr.e_phnum = %i\n", hdr->e_phnum);
+	for( unsigned int i = 0; i < hdr->e_phnum; i++ )
 	{
 		// Ignore Non-LOAD types
-		if(phtab[i].Type != PT_LOAD)
+		if(phtab[i].p_type != PT_LOAD)
 			continue;
-		iPageCount += ((phtab[i].VAddr&0xFFF) + phtab[i].MemSize + 0xFFF) >> 12;
-		LOG("phtab[%i] = {VAddr:0x%x, MemSize:0x%x}\n", i, phtab[i].VAddr, phtab[i].MemSize);
+		iPageCount += ((phtab[i].p_vaddr&0xFFF) + phtab[i].p_memsz + 0xFFF) >> 12;
+		LOG("phtab[%i] = {p_vaddr:0x%x, p_memsz:0x%x}\n", i, phtab[i].p_vaddr, phtab[i].MemSize);
 	}
 	
 	LOG("iPageCount = %i\n", iPageCount);
@@ -115,20 +108,21 @@ void *Elf32Load(int FD, Elf32_Ehdr *hdr)
 	//ret->Interpreter = NULL;
 
 	// Prescan for base and size
-	max = 0;
-	base = 0xFFFFFFFF;
-	for( i = 0; i < hdr->phentcount; i ++)
+	uint32_t	max = 0;
+	uint32_t	base = UINT32_MAX;
+	for( unsigned int i = 0; i < hdr->e_phnum; i ++)
 	{
-		if( phtab[i].Type != PT_LOAD )
+		if( phtab[i].p_type != PT_LOAD )
 			continue;
-		if( phtab[i].VAddr < base )
-			base = phtab[i].VAddr;
-		if( phtab[i].VAddr + phtab[i].MemSize > max )
-			max = phtab[i].VAddr + phtab[i].MemSize;
+		if( phtab[i].p_vaddr < base )
+			base = phtab[i].p_vaddr;
+		if( phtab[i].p_vaddr + phtab[i].p_memsz > max )
+			max = phtab[i].p_vaddr + phtab[i].p_memsz;
 	}
 
 	LOG("base = %08x, max = %08x\n", base, max);
 
+	uint32_t	baseDiff = 0;
 	if( base == 0 ) {
 		// Find a nice space (47 address bits allowed)
 		base = FindFreeRange( max, 47 );
@@ -138,39 +132,39 @@ void *Elf32Load(int FD, Elf32_Ehdr *hdr)
 	}
 	
 	// Load Pages
-	for( i = 0; i < hdr->phentcount; i++ )
+	for( unsigned int i = 0; i < hdr->e_phnum; i++ )
 	{
 		// Get Interpreter Name
-		if( phtab[i].Type == PT_INTERP )
+		if( phtab[i].p_type == PT_INTERP )
 		{
 			char *tmp;
 			//if(ret->Interpreter)	continue;
-			tmp = malloc(phtab[i].FileSize);
-			acess__SysSeek(FD, phtab[i].Offset, ACESS_SEEK_SET);
-			acess__SysRead(FD, tmp, phtab[i].FileSize);
+			tmp = malloc(phtab[i].p_filesz);
+			acess__SysSeek(FD, phtab[i].p_offset, ACESS_SEEK_SET);
+			acess__SysRead(FD, tmp, phtab[i].p_filesz);
 			//ret->Interpreter = Binary_RegInterp(tmp);
 			LOG("Interpreter '%s'\n", tmp);
 			free(tmp);
 			continue;
 		}
 		// Ignore non-LOAD types
-		if(phtab[i].Type != PT_LOAD)	continue;
+		if(phtab[i].p_type != PT_LOAD)	continue;
 		
-		LOG("phtab[%i] = PT_LOAD {Adj VAddr:0x%x, Offset:0x%x, FileSize:0x%x, MemSize:0x%x}\n",
-			i, phtab[i].VAddr+baseDiff, phtab[i].Offset, phtab[i].FileSize, phtab[i].MemSize);
+		LOG("phtab[%i] = PT_LOAD {Adj p_vaddr:0x%x, p_offset:0x%x, p_filesz:0x%x, p_memsz:0x%x}\n",
+			i, phtab[i].p_vaddr+baseDiff, phtab[i].p_offset, phtab[i].p_filesz, phtab[i].p_memsz);
 		
-		addr = phtab[i].VAddr + baseDiff;
+		uint64_t addr = phtab[i].p_vaddr + baseDiff;
 
-		if( AllocateMemory( addr, phtab[i].MemSize ) ) {
-			fprintf(stderr, "Elf_Load: Unable to map memory at %x (0x%x bytes)\n",
-				addr, phtab[i].MemSize);
+		if( AllocateMemory( addr, phtab[i].p_memsz ) ) {
+			fprintf(stderr, "Elf_Load: Unable to map memory at 0x%"PRIx64" (0x%x bytes)\n",
+				addr, phtab[i].p_memsz);
 			free( phtab );
 			return NULL;
 		}
 		
-		acess__SysSeek(FD, phtab[i].Offset, ACESS_SEEK_SET);
-		acess__SysRead(FD, PTRMK(void, addr), phtab[i].FileSize);
-		memset( PTRMK(char, addr) + phtab[i].FileSize, 0, phtab[i].MemSize - phtab[i].FileSize );
+		acess__SysSeek(FD, phtab[i].p_offset, ACESS_SEEK_SET);
+		acess__SysRead(FD, PTRMK(void, addr), phtab[i].p_filesz);
+		memset( PTRMK(char, addr) + phtab[i].p_filesz, 0, phtab[i].p_memsz - phtab[i].p_filesz );
 	}
 	
 	// Clean Up
@@ -182,13 +176,6 @@ void *Elf32Load(int FD, Elf32_Ehdr *hdr)
 
 void *Elf64Load(int FD, Elf64_Ehdr *hdr)
 {
-	Elf64_Phdr	*phtab;
-	 int	i;
-	 int	iPageCount;
-	uint64_t	max, base;
-	uint64_t	addr;
-	uint64_t	baseDiff = 0;
-	
 	ENTER("iFD", FD);
 	
 	if( sizeof(void*) == 4) {
@@ -205,25 +192,25 @@ void *Elf64Load(int FD, Elf64_Ehdr *hdr)
 	}
 	
 	// Read Program Header Table
-	phtab = malloc( sizeof(Elf64_Phdr) * hdr->e_phnum );
+	Elf64_Phdr* phtab = malloc( sizeof(Elf64_Phdr) * hdr->e_phnum );
 	if( !phtab ) {
 		LEAVE('n');
 		return NULL;
 	}
-	LOG("hdr.phoff = 0x%08llx\n", (long long)hdr->e_phoff);
+	LOG("hdr.e_phoff = 0x%08llx\n", (long long)hdr->e_phoff);
 	acess__SysSeek(FD, hdr->e_phoff, ACESS_SEEK_SET);
 	acess__SysRead(FD, phtab, sizeof(Elf64_Phdr) * hdr->e_phnum);
 	
 	// Count Pages
-	iPageCount = 0;
-	LOG("hdr.phentcount = %i\n", hdr->e_phnum);
-	for( i = 0; i < hdr->e_phnum; i++ )
+	unsigned int iPageCount = 0;
+	LOG("hdr.e_phnum = %i\n", hdr->e_phnum);
+	for( unsigned int i = 0; i < hdr->e_phnum; i++ )
 	{
 		// Ignore Non-LOAD types
 		if(phtab[i].p_type != PT_LOAD)
 			continue;
 		iPageCount += ((phtab[i].p_vaddr&0xFFF) + phtab[i].p_memsz + 0xFFF) >> 12;
-		LOG("phtab[%i] = {VAddr:0x%llx, MemSize:0x%llx}\n",
+		LOG("phtab[%i] = {p_vaddr:0x%llx, p_memsz:0x%llx}\n",
 			i, (long long)phtab[i].p_vaddr, (long long)phtab[i].p_memsz);
 	}
 	
@@ -238,9 +225,9 @@ void *Elf64Load(int FD, Elf64_Ehdr *hdr)
 	//ret->Interpreter = NULL;
 
 	// Prescan for base and size
-	max = 0;
-	base = 0xFFFFFFFF;
-	for( i = 0; i < hdr->e_phnum; i ++)
+	uint64_t max = 0;
+	uint64_t base = UINT64_MAX;
+	for( unsigned int i = 0; i < hdr->e_phnum; i ++)
 	{
 		if( phtab[i].p_type != PT_LOAD )
 			continue;
@@ -252,16 +239,18 @@ void *Elf64Load(int FD, Elf64_Ehdr *hdr)
 
 	LOG("base = %08lx, max = %08lx\n", base, max);
 
+	uint64_t	baseDiff = 0;
 	if( base == 0 ) {
 		// Find a nice space (31 address bits allowed)
 		base = FindFreeRange( max, 31 );
 		LOG("new base = %08lx\n", base);
-		if( base == 0 )	return NULL;
+		if( base == 0 )
+			goto _err;
 		baseDiff = base;
 	}
 	
 	// Load Pages
-	for( i = 0; i < hdr->e_phnum; i++ )
+	for( unsigned int i = 0; i < hdr->e_phnum; i++ )
 	{
 		// Get Interpreter Name
 		if( phtab[i].p_type == PT_INTERP )
@@ -280,19 +269,18 @@ void *Elf64Load(int FD, Elf64_Ehdr *hdr)
 		// Ignore non-LOAD types
 		if(phtab[i].p_type != PT_LOAD)	continue;
 		
-		LOG("phtab[%i] = PT_LOAD {Adj VAddr:0x%llx, Offset:0x%llx, FileSize:0x%llx, MemSize:0x%llx}\n",
+		LOG("phtab[%i] = PT_LOAD {Adj p_vaddr:0x%llx, p_offset:0x%llx, p_filesz:0x%llx, p_memsz:0x%llx}\n",
 			i,
 			(long long)phtab[i].p_vaddr+baseDiff, (long long)phtab[i].p_offset,
 			(long long)phtab[i].p_filesz, (long long)phtab[i].p_memsz
 			);
 		
-		addr = phtab[i].p_vaddr + baseDiff;
+		uint64_t addr = phtab[i].p_vaddr + baseDiff;
 
 		if( AllocateMemory( addr, phtab[i].p_memsz ) ) {
 			fprintf(stderr, "Elf_Load: Unable to map memory at %"PRIx64" (0x%"PRIx64" bytes)\n",
 				(uint64_t)addr, (uint64_t)phtab[i].p_memsz);
-			free( phtab );
-			return NULL;
+			goto _err;
 		}
 		
 		acess__SysSeek(FD, phtab[i].p_offset, ACESS_SEEK_SET);
@@ -305,5 +293,9 @@ void *Elf64Load(int FD, Elf64_Ehdr *hdr)
 	// Return
 	LEAVE('p', base);
 	return PTRMK(void, base);
+_err:
+	free(phtab);
+	LEAVE('n');
+	return NULL;
 }
 
diff --git a/AcessNative/ld-acess_src/exports.c b/AcessNative/ld-acess_src/exports.c
index a898b5a3e24f9d06d32358585abfecad9a193143..8064bb8bb1f54ab13c33fed52ccd4ac1ef504a12 100644
--- a/AcessNative/ld-acess_src/exports.c
+++ b/AcessNative/ld-acess_src/exports.c
@@ -53,7 +53,13 @@ int acess__SysOpen(const char *Path, unsigned int Flags)
 {
 	if( strncmp(Path, "$$$$", 4) == 0 )
 	{
-		return native_open(Path, Flags) | NATIVE_FILE_MASK;
+		return native_open(Path+4, Flags) | NATIVE_FILE_MASK;
+	}
+	if( strncmp(Path, "/Devices/shm/", 13) == 0 )
+	{
+		const char* tag = Path + 13;
+		Warning("TODO: Handle open SHM \"%s\"", tag);
+		return native_shm(tag, Flags) | NATIVE_FILE_MASK;
 	}
 	SYSTRACE("open(\"%s\", 0x%x)", Path, Flags);
 	return _Syscall(SYS_OPEN, ">s >i", Path, Flags);
@@ -95,6 +101,10 @@ size_t acess__SysWrite(int FD, const void *Src, size_t Bytes) {
 	SYSTRACE("_SysWrite(0x%x, 0x%x, %p\"%.*s\")", FD, Bytes, Src, Bytes, (char*)Src);
 	return _Syscall(SYS_WRITE, ">i >i >d", FD, Bytes, Bytes, Src);
 }
+uint64_t acess__SysTruncate(int fd, uint64_t size) {
+	TODO();
+	return 0;
+}
 
 int acess__SysSeek(int FD, int64_t Ofs, int Dir)
 {
@@ -156,6 +166,26 @@ int acess__SysUnlink(const char *pathname)
 	TODO();
 	return 0;
 }
+void* acess__SysMMap(void *addr, size_t length, unsigned int _flags, int fd, uint64_t offset)
+{
+	TODO();
+	return NULL;
+}
+int acess__SysMUnMap(void *addr, size_t length)
+{
+	TODO();
+	return 0;
+}
+uint64_t acess__SysMarshalFD(int FD)
+{
+	TODO();
+	return 0;
+}
+int acess__SysUnMarshalFD(uint64_t Handle)
+{
+	TODO();
+	return -1;
+}
 
 int acess__SysOpenChild(int fd, char *name, int flags) {
 	SYSTRACE("_SysOpenChild(0x%x, '%s', 0x%x)", fd, name, flags);
@@ -224,10 +254,7 @@ int acess__SysLoadModule(const char *Path)
 // --- Timekeeping ---
 int64_t acess__SysTimestamp(void)
 {
-	// TODO: Better impl
-	TODO();
-//	return now()*1000;
-	return 0;
+	return native_timestamp();
 }
 
 // --- Memory Management ---
@@ -418,19 +445,69 @@ int acess__SysWaitEvent(int Mask)
 }
 
 // --- Logging
+static void int_dbgheader(void )
+{
+	printf("[_SysDebug %i] ", giSyscall_ClientID);
+}
 void acess__SysDebug(const char *Format, ...)
 {
 	va_list	args;
 	
 	va_start(args, Format);
-	
-	printf("[_SysDebug %i] ", giSyscall_ClientID);
+	int_dbgheader();	
 	vprintf(Format, args);
 	printf("\n");
 	
 	va_end(args);
 }
 
+void acess__SysDebugHex(const char *tag, const void *data, size_t size)
+{
+	int_dbgheader();	
+	printf("%s (Hexdump of %p+%zi)\r\n", tag, data, size);
+
+	#define	CH(n)	((' '<=cdat[(n)]&&cdat[(n)]<0x7F) ? cdat[(n)] : '.')
+
+	const uint8_t	*cdat = data;
+	unsigned int	pos = 0;
+
+	while(size >= 16)
+	{
+		int_dbgheader();
+		printf("%04x:"
+			" %02x %02x %02x %02x %02x %02x %02x %02x "
+			" %02x %02x %02x %02x %02x %02x %02x %02x "
+			" %c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c\r\n",
+			pos,
+			cdat[ 0], cdat[ 1], cdat[ 2], cdat[ 3], cdat[ 4], cdat[ 5], cdat[ 6], cdat[ 7],
+			cdat[ 8], cdat[ 9], cdat[10], cdat[11], cdat[12], cdat[13], cdat[14], cdat[15],
+			CH(0),	CH(1),	CH(2),	CH(3),	CH(4),	CH(5),	CH(6),	CH(7),
+			CH(8),	CH(9),	CH(10),	CH(11),	CH(12),	CH(13),	CH(14),	CH(15)
+			);
+		size -= 16;
+		cdat += 16;
+		pos += 16;
+	}
+
+	{
+		int_dbgheader();
+		printf("%04x: ", pos);
+		for(int i = 0; i < size; i ++)
+			printf("%02x ", cdat[i]);
+		for(int i = size; i < 16; i ++)
+			printf("   ");
+		printf(" ");
+		for(int i = 0; i < size; i ++)
+		{
+			if( i == 8 )
+				printf(" ");
+			printf("%c", CH(i));
+		}
+	
+		printf("\n");
+	}
+}
+
 void acess__exit(int Status)
 {
 	DEBUG("_exit(%i)", Status);
@@ -489,6 +566,9 @@ const tSym	caBuiltinSymbols[] = {
 	DEFSYM(_SysAllocate),
 	DEFSYM(_SysSetMemFlags),
 	DEFSYM(_SysDebug),
+	{"_ZN4_sys5debugEPKcz", &acess__SysDebug},
+	DEFSYM(_SysDebugHex),
+	{"_ZN4_sys7hexdumpEPKcPKvj", &acess__SysDebugHex},
 	DEFSYM(_SysSetFaultHandler),
 	DEFSYM(_SysWaitEvent),
 	
diff --git a/AcessNative/ld-acess_src/exports.h b/AcessNative/ld-acess_src/exports.h
index e8f2e4c8a71425890cb607ab7d989d19ba614b18..94987d0b49e4eaa3791ba7679ff240f02cebe310 100644
--- a/AcessNative/ld-acess_src/exports.h
+++ b/AcessNative/ld-acess_src/exports.h
@@ -17,6 +17,7 @@ extern uint64_t	_Syscall(int SyscallID, const char *ArgTypes, ...);
 extern int	acess__errno;
 
 extern int	native_open(const char *Path, int Flags);
+extern int	native_shm(const char *Tag, int Flags);
 extern void	native_close(int FD);
 extern size_t	native_read(int FD, void *Dest, size_t Bytes);
 extern size_t	native_write(int FD, const void *Src, size_t Bytes);
@@ -26,6 +27,8 @@ extern uint64_t	native_tell(int FD);
 extern int	native_execve(const char *filename, const char *const argv[], const char *const envp[]);
 extern int	native_spawn(const char *filename, const char *const argv[], const char *const envp[]);
 
+extern int64_t	native_timestamp(void);
+
 // Syscalls used by the linker
 extern int	acess__SysOpen(const char *Path, unsigned int Flags);
 extern void	acess__SysClose(int FD);
diff --git a/AcessNative/ld-acess_src/memory.c b/AcessNative/ld-acess_src/memory.c
index c8166af4fb9bc2bf7f5da71f3e8d88000960f759..99238a5222719485605347661dd33333820780da 100644
--- a/AcessNative/ld-acess_src/memory.c
+++ b/AcessNative/ld-acess_src/memory.c
@@ -1,5 +1,6 @@
 /*
  */
+#define _GNU_SOURCE	// needed for MAP_ANONYMOUS to be avaliable
 #include "common.h"
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/AcessNative/ld-acess_src/request.c b/AcessNative/ld-acess_src/request.c
index fdf57485e860e61e02aa3fe0a1d87a459245bc56..690c7f7ff35221542b3a1b8e5c4064d8adc9b6ce 100644
--- a/AcessNative/ld-acess_src/request.c
+++ b/AcessNative/ld-acess_src/request.c
@@ -25,6 +25,7 @@
 # include <unistd.h>
 # include <sys/socket.h>
 # include <netinet/in.h>
+# include <sys/select.h>
 #endif
 #include "request.h"
 #include "../syscalls.h"
@@ -228,12 +229,12 @@ int SendRequest(tRequestHeader *Request, int RequestSize, int ResponseSize)
 	// Send it off
 	SendData(Request, RequestSize);
 
-	if( Request->CallID == SYS_EXIT )	return 0;
-
 	// Wait for a response (no timeout)
 	ReadData(Request, sizeof(*Request), 0);
+	
+	size_t	recvbytes = sizeof(*Request);
 	// TODO: Sanity
-	size_t	recvbytes = sizeof(*Request), expbytes = Request->MessageLength;
+	size_t	expbytes = Request->MessageLength;
 	char	*ptr = (void*)Request->Params;
 	while( recvbytes < expbytes )
 	{
diff --git a/AcessNative/ld-acess_src/syscalls.c b/AcessNative/ld-acess_src/syscalls.c
index 60b7d952ed53fbae526efe418de13dc321017166..3341a9568f32745f49b4273ba6f0f79f39ca36fb 100644
--- a/AcessNative/ld-acess_src/syscalls.c
+++ b/AcessNative/ld-acess_src/syscalls.c
@@ -13,6 +13,7 @@
 # include <spawn.h>	// posix_spawn
 #endif
 #include "request.h"
+#include <sys/time.h>
 
 #define assert(cnd) do{ \
 	if( !(cnd) ) { \
@@ -171,7 +172,6 @@ uint64_t _Syscall(int SyscallID, const char *ArgTypes, ...)
 	tRequestHeader	*req;
 	void	*dataPtr;
 	uint64_t	retValue;
-	 int	i;
 	
 	// DEBUG!
 //	printf("&tRequestHeader->Params = %i\n", offsetof(tRequestHeader, Params));
@@ -244,8 +244,11 @@ uint64_t _Syscall(int SyscallID, const char *ArgTypes, ...)
 		exit(127);
 	}
 	
+	if( !(req->NParams >= 2) ) {
+		fprintf(stderr, "syscalls.c: Too few return params (%i)", req->NParams);
+		exit(127);
+	}
 	dataPtr = (void*)&req->Params[req->NParams];
-	assert(req->NParams >= 2);
 	// return
 	assert(req->Params[0].Type == ARG_TYPE_INT64);
 	assert(req->Params[0].Length == sizeof(uint64_t));
@@ -264,7 +267,7 @@ uint64_t _Syscall(int SyscallID, const char *ArgTypes, ...)
 		exit(127);
 	}
 	retCount = 0;
-	for( i = 2; i < req->NParams; i ++ )
+	for( unsigned int i = 2; i < req->NParams; i ++ )
 	{
 		#if 0
 		 int	 j;
@@ -288,18 +291,40 @@ uint64_t _Syscall(int SyscallID, const char *ArgTypes, ...)
 	return retValue;
 }
 
+int native_int_getfd(void)
+{
+	for(int ret = 0; ret < MAX_FPS; ret ++ )
+		if( gaSyscall_LocalFPs[ret] == NULL )
+			return ret;
+	return -1;
+}
 
 int native_open(const char *Path, int Flags)
 {
-	int	ret;
-       for(ret = 0; ret < MAX_FPS && gaSyscall_LocalFPs[ret]; ret ++ )	;
-       if(ret == MAX_FPS)	return -1;
+	int	ret = native_int_getfd();
+	if(ret == -1)	return -1;
        // TODO: Handle directories
-       gaSyscall_LocalFPs[ret] = fopen(&Path[4], "r+");
+       gaSyscall_LocalFPs[ret] = fopen(Path, "r+");
        if(!gaSyscall_LocalFPs[ret])	return -1;
        return ret;
 }
 
+int native_shm(const char *Tag, int Flags)
+{
+	// int ret = native_int_getfd();
+	//if(ret == -1)	return -1;
+	//if( strcmp(Tag, "anon") == 0 )
+	//	path = "/AcessNative/anon/RAND";
+	//	FD = shm_open(path, O_RDWD);
+	//shm_unlink(path);
+	//else
+	//	path = "/Acessnative/named/<TAG>";
+	// int FD = shm_open(path, O_RDWD);
+	//shm_unlink(path);
+	//gaSyscall_LocalFPs[ret] = 
+	return -1;
+}
+
 void native_close(int FD)
 {
 	fclose( gaSyscall_LocalFPs[FD] );
@@ -357,3 +382,10 @@ int native_spawn(const char *filename, const char *const argv[], const char *con
 	
 	return rv;
 }
+
+int64_t native_timestamp(void)
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	return tv.tv_sec*1000 + tv.tv_usec / 1000;
+}
diff --git a/AcessNative/libacess-native.so_src/Makefile b/AcessNative/libacess-native.so_src/Makefile
index 927831146e09a1b5da461e95e02d5d9464c84bd8..2a0f0c4c46079beb4a3cfa1c6c53ee7d210d0c01 100644
--- a/AcessNative/libacess-native.so_src/Makefile
+++ b/AcessNative/libacess-native.so_src/Makefile
@@ -20,7 +20,7 @@ $(warning $(BINLINK))
 
 CFLAGS   += -Wall
 CFLAGS   += -Werror
-CFLAGS   += -g -shared -fPIC
+CFLAGS   += -g -shared -fPIC -std=c99
 CPPFLAGS += -DARCHDIR_is_x86_64=1
 LDFLAGS  += -g -shared -Wl,--no-undefined -lc
 
diff --git a/AcessNative/libacess-native.so_src/exports.c b/AcessNative/libacess-native.so_src/exports.c
index 2f960c4fefae34f152502fbfe7f5b3164684a259..ad5aac4911b39a9316d86461efc4ee35ae758627 100644
--- a/AcessNative/libacess-native.so_src/exports.c
+++ b/AcessNative/libacess-native.so_src/exports.c
@@ -10,6 +10,9 @@ int *libc_geterrno(void)
 	return &acess__errno;
 }
 
+void _ZN4_sys5debugEPKcz(const char *fmt, ...)	__attribute__((alias("acess__SysDebug")));
+void _ZN4_sys7hexdumpEPKcPKvj(const char *tag, const void *ptr, size_t size) __attribute__((alias("acess__SysDebugHex")));
+
 #undef acess__SysSpawn
 
 int acess__SysSpawn(const char *binary, const char **argv, const char **envp, int nfd, int fds[], struct s_sys_spawninfo *info)
diff --git a/AcessNative/libacess-native.so_src/main.c b/AcessNative/libacess-native.so_src/main.c
index 74d9d3c62d9baa7a62da054e7cb8dd67e08d5ecd..b5f066f70809e5bc4868aec4c2affc7413b8e7bc 100644
--- a/AcessNative/libacess-native.so_src/main.c
+++ b/AcessNative/libacess-native.so_src/main.c
@@ -10,6 +10,8 @@
 #include <stdint.h>
 #include "../ld-acess_src/exports.h"
 
+extern int	gbSyscallDebugEnabled;
+
 #ifdef __WINDOWS__
 int DllMain(void)
 {
@@ -40,10 +42,13 @@ int libacessnative_init(int argc, char *argv[], char **envp)
 {
 	Request_Preinit();
 	
+	//gbSyscallDebugEnabled = 1;
+	
 	const char *preopens = getenv_p(envp, ENV_VAR_PREOPENS);
 	printf("preopens = %s\n", preopens);
 	if( preopens )
 	{
+		 int	exp_fd = 0;
 		while( *preopens )
 		{
 			const char *splitter = strchr(preopens, ':');
@@ -59,8 +64,16 @@ int libacessnative_init(int argc, char *argv[], char **envp)
 			path[len] = 0;
 			int fd = acess__SysOpen(path, 6);	// WRITE,READ,no EXEC
 			if( fd == -1 ) {
-				fprintf(stderr, "Unable to preopen '%s'\n", path);
+				fprintf(stderr, "Unable to preopen '%s' errno=%i\n", path, acess__errno);
+				exit(1);
+			}
+			if( fd != exp_fd ) {
+				//  Oh... this is bad
+				fprintf(stderr, "Pre-opening '%s' resulted in an incorrect FD (expected %i, got %i)",
+					path, exp_fd, fd);
+				exit(1);
 			}
+			exp_fd += 1;
 			
 			if( !splitter )
 				break;
@@ -77,7 +90,7 @@ int libacessnative_init(int argc, char *argv[], char **envp)
 int acessnative_spawn(const char *Binary, int SyscallID, const char * const * argv, const char * const * envp)
 {
 	 int	envc = 0;
-	while( envp[envc++] )
+	while( envp && envp[envc++] )
 		envc ++;
 
 	// Set environment variables for libacess-native
@@ -89,7 +102,7 @@ int acessnative_spawn(const char *Binary, int SyscallID, const char * const * ar
 	
 	const char *newenv[envc+2+1];
 	 int	i = 0;
-	for( ; envp[i]; i ++ )
+	for( ; envp && envp[i]; i ++ )
 	{
 		const char	*ev = envp[i];
 		if( strncmp(ev, ENV_VAR_KEY"=", sizeof(ENV_VAR_KEY"=")) == 0 ) {
@@ -100,7 +113,7 @@ int acessnative_spawn(const char *Binary, int SyscallID, const char * const * ar
 	}
 	if( !bKeyHit )
 		newenv[i++] = keystr;
-	newenv[i++] = "LD_LIBRARY_PATH=Libs/";	// HACK
+	newenv[i++] = getenv("LD_LIBRARY_PATH") - (sizeof("LD_LIBRARY_PATH=")-1);	// VERY hacky
 	newenv[i] = NULL;
 	
 	// TODO: Detect native_spawn failing
@@ -127,17 +140,3 @@ void Warning(const char *format, ...)
 	printf("\n");
 }
 
-void __libc_csu_fini()
-{
-}
-
-void __libc_csu_init()
-{
-}
-
-void __stack_chk_fail(void)
-{
-	fprintf(stderr, "__stack_chk_fail");
-	exit(1);
-}
-
diff --git a/BuildConf/armv7/Makefile.cfg b/BuildConf/armv7/Makefile.cfg
index d2f0a0f74371b1ff84668f71076dcd384c3d3374..3180234a859e2f9cbc78b2252dd8261ca3d1e68d 100644
--- a/BuildConf/armv7/Makefile.cfg
+++ b/BuildConf/armv7/Makefile.cfg
@@ -1,12 +1,9 @@
 
+TRIPLET = arm-pc-acess2
 ARM_CPUNAME = gerneric-armv7
-CC = arm-eabi-gcc -mcpu=$(ARM_CPUNAME)
-AS = arm-eabi-gcc -mcpu=$(ARM_CPUNAME) -c
-LD = arm-eabi-ld
-OBJDUMP = arm-eabi-objdump
+#AS = $(TRIPLET)-gcc -mcpu=$(ARM_CPUNAME) -c
 DISASM := $(OBJDUMP) -d -S
 ARCHDIR = armv7
-STRIP = arm-eabi-strip
 
 ASSUFFIX = S
 
diff --git a/BuildConf/x86/Makefile.cfg b/BuildConf/x86/Makefile.cfg
index 6559146ec7483570f69b5bfecb22300c08b1cff9..271059c88b5a48e5d4c948f01f31ccf7a4cb67ff 100644
--- a/BuildConf/x86/Makefile.cfg
+++ b/BuildConf/x86/Makefile.cfg
@@ -2,15 +2,8 @@
 # Acess2 Build Configuration
 #
 
-CC = i586-elf-gcc
-CXX = i586-elf-g++
-#CC = clang -m32
-LD = i586-elf-ld
-#CC = gcc
-#LD = ld
+TRIPLET = i686-pc-acess2
 AS = nasm
-#OBJDUMP = i586-elf-objdump
-OBJDUMP = objdump
 RM = @rm -f
 STRIP = strip
 
diff --git a/BuildConf/x86_64/Makefile.cfg b/BuildConf/x86_64/Makefile.cfg
index cc252bc57426e1bec38c3532b8f586ea7ff88833..20dcbb2025032c96110b8e5d8e928b923d41de61 100644
--- a/BuildConf/x86_64/Makefile.cfg
+++ b/BuildConf/x86_64/Makefile.cfg
@@ -1,10 +1,6 @@
 
-#PREFIX := x86_64-pc-elf
-PREFIX := x86_64-none-elf
-
-CC := $(PREFIX)-gcc
-LD := $(PREFIX)-ld
-DISASM = $(PREFIX)-objdump -d -M x86-64 -S
+TRIPLET = x86_64-pc-acess2
+AS = nasm
 
 KERNEL_CFLAGS := -mcmodel=kernel -nostdlib -mno-red-zone -Wall -mno-sse
 DYNMOD_CFLAGS := -mcmodel=small -fPIC -mno-red-zone -mno-sse
diff --git a/Externals/common_automake.mk b/Externals/common_automake.mk
index 70b1978eb7e619b033b6179f5b66e2e4f6b0cbe4..c7e67c3cec50a43190ef5ca4e3040052f86155ca 100644
--- a/Externals/common_automake.mk
+++ b/Externals/common_automake.mk
@@ -24,9 +24,9 @@ endif
 
 $(BDIR)/Makefile: _patch $(CONFIGSCRIPT) ../common_automake.mk Makefile 
 	mkdir -p $(BDIR)
-	cd $(BDIR) && $(CONFIGURE_ENV) PATH=$(PATH) $(CONFIGURE_LINE)
+	cd $(BDIR) && $(CONFIGURE_ENV) PATH="$(PATH)" $(CONFIGURE_LINE)
 
 _build: $(BDIR)/Makefile
-	PATH=$(PATH) make $(BTARGETS) -C $(BDIR)
-	PATH=$(PATH) make DESTDIR=$(OUTDIR) $(ITARGETS) -C $(BDIR)
+	PATH="$(PATH)" make $(BTARGETS) -C $(BDIR)
+	PATH="$(PATH)" make DESTDIR=$(OUTDIR) $(ITARGETS) -C $(BDIR)
 
diff --git a/Externals/config.mk b/Externals/config.mk
index 3d7913b5f00d70f927d9a35ec56c743f50d4ab9f..9db8f0dedfec4c3d3773ed9258b91ce25e094163 100644
--- a/Externals/config.mk
+++ b/Externals/config.mk
@@ -4,9 +4,11 @@
 -include ../../Makefile.cfg
 
 ifeq ($(ARCH),x86)
- BFD := i586
+ BFD := i686
 else ifeq ($(ARCH),x86_64)
  BFD := x86_64
+else ifeq ($(ARCH),armv7)
+ BFD := arm
 else
  $(error No BFD translation for $(ARCH) in Externals/config.mk)
 endif
diff --git a/Externals/core.mk b/Externals/core.mk
index bc7a5b80184cb3f17389129e5379c86b8f781ac9..6c23a33ba520f59eafe1aa0c09d9b88dba87feef 100644
--- a/Externals/core.mk
+++ b/Externals/core.mk
@@ -73,7 +73,14 @@ $(DIR)/%: patches/%
 
 PATCHED_FILES := $(addprefix $(DIR)/,$(PATCHES))
 
-_patch: $(DIR) $(PATCHED_FILES) $(wildcard patches/UNIFIED.patch)
 ifneq ($(wildcard patches/UNIFIED.patch),)
+$(DIR)/_unified_applied: $(wildcard patches/UNIFIED.patch)
 	cd $(DIR) && patch -p1 < ../patches/UNIFIED.patch
+	touch $@
+UNIFIED_TARGET=$(DIR)/_unified_applied
+else
+UNIFIED_TARGET=
 endif
+
+_patch: $(DIR) $(PATCHED_FILES) $(UNIFIED_TARGET)
+
diff --git a/Externals/cross-compiler/Makefile b/Externals/cross-compiler/Makefile
index 9dde5d8859c50bfd438b82f239a6204ca30fbe98..ae2df5ae1c5cd6819319a0e9dc2452d213df7e0d 100644
--- a/Externals/cross-compiler/Makefile
+++ b/Externals/cross-compiler/Makefile
@@ -1,67 +1,28 @@
+#
+#
+#
+include Makefile.common.mk
 
+GCC_TARGETS := gcc
 
--include ../config.mk
+PREFIX := $(OUTDIR)
+BDIR := build-n-$(ARCH)/
+BDIR_GCC := $(BDIR)gcc
+BDIR_BINUTILS := $(BDIR)binutils
 
-GCC_ARCHIVE:=$(lastword $(sort $(wildcard gcc-*.tar.bz2)))
-GCC_DIR:=$(GCC_ARCHIVE:%.tar.bz2=%)
-BINUTILS_ARCHIVE:=$(lastword $(sort $(wildcard binutils-*.tar.bz2)))
-BINUTILS_DIR:=$(BINUTILS_ARCHIVE:%.tar.bz2=%)
+ENVVARS := PATH=$(OUTDIR)-BUILD/bin:$$PATH
 
-BINUTILS_CHANGES := config.sub bfd/config.bfd gas/configure.tgt ld/configure.tgt ld/emulparams/acess2_i386.sh ld/emulparams/acess2_amd64.sh ld/Makefile.in
-GCC_CHANGES := config.sub gcc/config.gcc gcc/config/acess2.h libgcc/config.host
-# libstdc++-v3/crossconfig.m4 config/override.m4
-
-TARGET=$(HOST)
-GCC_TARGETS := gcc target-libgcc
-# target-libstdc++-v3 
-
-PREFIX := $(OUTDIR)-BUILD
-BDIR_GCC := build-$(ARCH)/gcc
-BDIR_BINUTILS := build-$(ARCH)/binutils
-
-.PHONY: all clean binutils gcc include
-
-all: include binutils gcc
+include Makefile.rules.mk
 
 include:
 	mkdir -p $(PREFIX)
-	mkdir -p $(SYSROOT)/usr
-	ln -sf $(ACESSDIR)/Usermode/include $(SYSROOT)/usr/include
-	ln -sf $(ACESSDIR)/Usermode/Output/$(ARCH)/Libs $(SYSROOT)/usr/lib
-
-gcc: $(GCC_DIR) $(PREFIX)/bin/$(TARGET)-gcc
-
-binutils: $(BINUTILS_DIR) $(PREFIX)/bin/$(TARGET)-ld
-
-clean:
-	$(RM) -rf $(BINUTILS_DIR) $(GCC_DIR) build-$(ARCH)
-
-$(BINUTILS_DIR) $(GCC_DIR): %: %.tar.bz2
-	tar -xf $<
-$(warning $(BINUTILS_DIR) $(GCC_DIR))
-
-$(GCC_DIR)/%: patches/gcc/%.patch
-	@echo [PATCH] $@
-	#@tar -xf $(GCC_ARCHIVE) $@
-	@patch $@ $<
-$(GCC_DIR)/%: patches/gcc/%
-	@echo [CP] $@
-	@cp $< $@
-
-$(BINUTILS_DIR)/%: patches/binutils/%.patch
-	@echo [PATCH] $@
-	#@tar -xf $(BINUTILS_ARCHIVE) $@
-	@patch $@ $<
-$(BINUTILS_DIR)/%: patches/binutils/%
-	@echo [CP] $@
-	@cp $< $@
 
 $(GCC_DIR)/libstdc++-v3/configure: $(GCC_DIR)/libstdc++-v3/crossconfig.m4
 	cd $(GCC_DIR)/libstdc++-v3/ && autoconf
 
 $(BDIR_BINUTILS)/Makefile: $(addprefix $(BINUTILS_DIR)/,$(BINUTILS_CHANGES))
 	@mkdir -p $(BDIR_BINUTILS)
-	@cd $(BDIR_BINUTILS) && ../../$(BINUTILS_DIR)/configure --target=$(TARGET) --prefix=$(PREFIX) --disable-nls "--with-sysroot=$(SYSROOT)" --enable-shared
+	@cd $(BDIR_BINUTILS) && ../../$(BINUTILS_DIR)/configure --target=$(TARGET) --prefix=$(PREFIX) --disable-nls --enable-shared --without-docdir
 
 $(PREFIX)/bin/$(TARGET)-ld: $(BDIR_BINUTILS)/Makefile
 	@make -C $(BDIR_BINUTILS) all -j $(PARLEVEL)
@@ -69,10 +30,12 @@ $(PREFIX)/bin/$(TARGET)-ld: $(BDIR_BINUTILS)/Makefile
 
 $(BDIR_GCC)/Makefile: Makefile $(addprefix $(GCC_DIR)/,$(GCC_CHANGES)) $(GCC_DIR)/libstdc++-v3/configure
 	@mkdir -p $(BDIR_GCC)
-	@cd $(BDIR_GCC) && PATH=$(PREFIX)/bin:$$PATH ../../$(GCC_DIR)/configure --target=$(TARGET) --prefix=$(PREFIX) --disable-nls --enable-langs=c,c++ --includedir=$(ACESSDIR)/Usermode/include "--with-sysroot=$(SYSROOT)"
+	@cd $(BDIR_GCC) && $(ENVVARS) ../../$(GCC_DIR)/configure --target=$(TARGET) --prefix=$(PREFIX) --disable-nls --enable-langs=c,c++ --includedir=$(ACESSDIR)/Usermode/include --without-docdir --enable-threads=posix
+	@echo "MAKEINFO = :" >> $(BDIR_GCC)/Makefile
 
 $(PREFIX)/bin/$(TARGET)-gcc: $(BDIR_GCC)/Makefile
-	@PATH=$(PREFIX)/bin:$$PATH make -C $(BDIR_GCC) $(GCC_TARGETS:%=all-%) -j $(PARLEVEL)
-	@PATH=$(PREFIX)/bin:$$PATH make -C $(BDIR_GCC) $(GCC_TARGETS:%=install-%)
+	@$(ENVVARS) make -C $(BDIR_GCC) $(GCC_TARGETS:%=all-%) -j $(PARLEVEL)
+	@$(ENVVARS) make -C $(BDIR_GCC)libstdc++-v3/ all-target-libsupc++ -j $(PARLEVEL)
+	@$(ENVVARS) make -C $(BDIR_GCC) $(GCC_TARGETS:%=install-%)
 
 
diff --git a/Externals/cross-compiler/Makefile.common.mk b/Externals/cross-compiler/Makefile.common.mk
new file mode 100644
index 0000000000000000000000000000000000000000..b986044d2ac6e45580b0cc0af8cd89f1e765a64b
--- /dev/null
+++ b/Externals/cross-compiler/Makefile.common.mk
@@ -0,0 +1,26 @@
+#
+#
+#
+-include ../config.mk
+
+GCC_ARCHIVE:=$(lastword $(sort $(wildcard gcc-*.tar.bz2)))
+GCC_DIR:=$(GCC_ARCHIVE:%.tar.bz2=%)
+BINUTILS_ARCHIVE:=$(lastword $(sort $(wildcard binutils-*.tar.bz2)))
+BINUTILS_DIR:=$(BINUTILS_ARCHIVE:%.tar.bz2=%)
+
+ifeq ($(GCC_ARCHIVE),)
+ $(warning Unable to find a GCC archive matching gcc-*.tar.bz2)
+ $(error No archive found)
+endif
+ifeq ($(BINUTILS_ARCHIVE),)
+ $(warning Unable to find a binutils archive matching binutils-*.tar.bz2)
+ $(error No archive found)
+endif
+
+BINUTILS_CHANGES := config.sub bfd/config.bfd gas/configure.tgt ld/configure.tgt ld/emulparams/acess2_i386.sh ld/emulparams/acess2_amd64.sh ld/emulparams/acess2_arm.sh ld/Makefile.in
+GCC_CHANGES := config.sub gcc/config.gcc gcc/config/acess2.h libgcc/config.host gcc/config/acess2.opt
+# libstdc++-v3/crossconfig.m4 config/override.m4
+
+TARGET=$(HOST)
+GCC_TARGETS := gcc target-libgcc
+# target-libstdc++-v3 
diff --git a/Externals/cross-compiler/Makefile.cross b/Externals/cross-compiler/Makefile.cross
new file mode 100644
index 0000000000000000000000000000000000000000..c6a4edd258ebeb7488c43cd2a3893d55de226271
--- /dev/null
+++ b/Externals/cross-compiler/Makefile.cross
@@ -0,0 +1,39 @@
+#
+#
+#
+include Makefile.common.mk
+
+PREFIX := $(OUTDIR)-BUILD
+BDIR := build-$(ARCH)/
+BDIR_GCC := $(BDIR)gcc
+BDIR_BINUTILS := $(BDIR)binutils
+
+include Makefile.rules.mk
+
+include:
+	mkdir -p $(PREFIX)
+	mkdir -p $(SYSROOT)/usr
+	ln -sf $(ACESSDIR)/Usermode/include $(SYSROOT)/usr/include
+	ln -sf $(ACESSDIR)/Usermode/Output/$(ARCH)/Libs $(SYSROOT)/usr/lib
+
+$(GCC_DIR)/libstdc++-v3/configure: $(GCC_DIR)/libstdc++-v3/crossconfig.m4
+	cd $(GCC_DIR)/libstdc++-v3/ && autoconf
+
+$(BDIR_BINUTILS)/Makefile: $(addprefix $(BINUTILS_DIR)/,$(BINUTILS_CHANGES))
+	@mkdir -p $(BDIR_BINUTILS)
+	@cd $(BDIR_BINUTILS) && ../../$(BINUTILS_DIR)/configure --target=$(TARGET) --prefix=$(PREFIX) --disable-nls "--with-sysroot=$(SYSROOT)" --enable-shared --without-docdir
+
+$(PREFIX)/bin/$(TARGET)-ld: $(BDIR_BINUTILS)/Makefile
+	@make -C $(BDIR_BINUTILS) all -j $(PARLEVEL)
+	@make -C $(BDIR_BINUTILS) install
+
+$(BDIR_GCC)/Makefile: Makefile $(addprefix $(GCC_DIR)/,$(GCC_CHANGES)) $(GCC_DIR)/libstdc++-v3/configure
+	@mkdir -p $(BDIR_GCC)
+	@cd $(BDIR_GCC) && PATH=$(PREFIX)/bin:$$PATH ../../$(GCC_DIR)/configure --target=$(TARGET) --prefix=$(PREFIX) --disable-nls --enable-langs=c,c++ --includedir=$(ACESSDIR)/Usermode/include "--with-sysroot=$(SYSROOT)" --without-docdir --enable-threads=posix
+	@echo "MAKEINFO = :" >> $(BDIR_GCC)/Makefile
+
+$(PREFIX)/bin/$(TARGET)-gcc: $(BDIR_GCC)/Makefile
+	@PATH=$(PREFIX)/bin:$$PATH make -C $(BDIR_GCC) $(GCC_TARGETS:%=all-%) -j $(PARLEVEL)
+	@PATH=$(PREFIX)/bin:$$PATH make -C $(BDIR_GCC) $(GCC_TARGETS:%=install-%)
+
+
diff --git a/Externals/cross-compiler/Makefile.rules.mk b/Externals/cross-compiler/Makefile.rules.mk
new file mode 100644
index 0000000000000000000000000000000000000000..48bf43de144f91a544ca715dd3041db3bd9cd808
--- /dev/null
+++ b/Externals/cross-compiler/Makefile.rules.mk
@@ -0,0 +1,32 @@
+
+.PHONY: all clean binutils gcc include
+
+all: include binutils gcc
+
+clean:
+	$(RM) -rf $(BINUTILS_DIR) $(GCC_DIR) build-$(ARCH)
+
+gcc: $(GCC_DIR) $(PREFIX)/bin/$(TARGET)-gcc
+
+binutils: $(BINUTILS_DIR) $(PREFIX)/bin/$(TARGET)-ld
+
+$(BINUTILS_DIR) $(GCC_DIR): %: %.tar.bz2
+	tar -xf $<
+
+$(GCC_DIR)/%: patches/gcc/%.patch
+	@echo [PATCH] $@
+	@tar -xf $(GCC_ARCHIVE) $@
+	@patch $@ $<
+$(GCC_DIR)/%: patches/gcc/%
+	@echo [CP] $@
+	@cp $< $@
+
+$(BINUTILS_DIR)/%: patches/binutils/%.patch
+	@echo [PATCH] $@
+	@tar -xf $(BINUTILS_ARCHIVE) $@
+	@patch $@ $<
+$(BINUTILS_DIR)/%: patches/binutils/%
+	@echo [CP] $@
+	@cp $< $@
+
+
diff --git a/Externals/cross-compiler/patches/binutils/bfd/config.bfd.patch b/Externals/cross-compiler/patches/binutils/bfd/config.bfd.patch
index 6b61642485013e25d47e91d24fb2aebcea3e0126..0c0183101e6135c310aadfd2fba0b4b92a73e4b1 100644
--- a/Externals/cross-compiler/patches/binutils/bfd/config.bfd.patch
+++ b/Externals/cross-compiler/patches/binutils/bfd/config.bfd.patch
@@ -12,7 +12,7 @@
 +    want64=true
 +    ;;
 +  arm-*-acess2)
-+    targ_defvec=bfd_elf32_arm_vec
-+    targ_selvecs="bfd_elf32_arm_vec"
++    targ_defvec=bfd_elf32_littlearm_vec
++    targ_selvecs="bfd_elf32_bigarm_vec"
 +    ;;
  # END OF targmatch.h
diff --git a/Externals/cross-compiler/patches/binutils/gas/configure.tgt.patch b/Externals/cross-compiler/patches/binutils/gas/configure.tgt.patch
index a024c0805ee535b43e4b73821eb0bf87cf03e301..6148df197182ced812f85879b7742cbf63b80073 100644
--- a/Externals/cross-compiler/patches/binutils/gas/configure.tgt.patch
+++ b/Externals/cross-compiler/patches/binutils/gas/configure.tgt.patch
@@ -1,6 +1,7 @@
 --- gas/configure.tgt 2011-07-29 00:00:00.000000 +0000
 +++ gas/configure.tgt 2013-03-01 10:45:00.000000 +0800
-@@ -173,2 +173,3 @@
+@@ -173,2 +173,4 @@
    i386-sequent-bsd*)			fmt=aout em=dynix ;;
 +  i386-*-acess2*)    fmt=elf ;;
++  arm-*-acess2*)    fmt=elf ;;
    i386-*-beospe*)			fmt=coff em=pe ;;
diff --git a/Externals/cross-compiler/patches/binutils/ld/Makefile.in.patch b/Externals/cross-compiler/patches/binutils/ld/Makefile.in.patch
index 76439619c5cbf79f0515b40ad4ef22a2681cbfb5..becadff7eca3e7385d676604ed1db05ce320ab3b 100644
--- a/Externals/cross-compiler/patches/binutils/ld/Makefile.in.patch
+++ b/Externals/cross-compiler/patches/binutils/ld/Makefile.in.patch
@@ -1,9 +1,11 @@
 --- ld/Makefile.in
 +++ ld/Makefile.in
-@@ -2627,2 +2627,6 @@
+@@ -2627,2 +2627,8 @@
  	${GENSCRIPTS} elf32xtensa "$(tdir_elf32xtensa)"
 +eacess2_i386.c: $(srcdir)/emulparams/acess2_i386.sh $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
 +	${GENSCRIPTS} acess2_i386 "$(tdir_acess2_i386)"
 +eacess2_amd64.c: $(srcdir)/emulparams/acess2_amd64.sh $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
 +	${GENSCRIPTS} acess2_amd64 "$(tdir_acess2_amd64)"
++eacess2_arm.c: $(srcdir)/emulparams/acess2_arm.sh $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
++	${GENSCRIPTS} acess2_arm "$(tdir_acess2_arm)"
  eelf_i386.c: $(srcdir)/emulparams/elf_i386.sh \
diff --git a/Externals/cross-compiler/patches/binutils/ld/configure.tgt.patch b/Externals/cross-compiler/patches/binutils/ld/configure.tgt.patch
index 8d6a302ac928710d510aa86becc1ec861d4eb353..1d5a4853b67268c5735114e26be6d3a61db1bd86 100644
--- a/Externals/cross-compiler/patches/binutils/ld/configure.tgt.patch
+++ b/Externals/cross-compiler/patches/binutils/ld/configure.tgt.patch
@@ -1,7 +1,8 @@
 --- ld/configure.tgt
 +++ ld/configure.tgt
-@@ -167,1 +167,3 @@
+@@ -167,1 +167,4 @@
  i[3-7]86-*-nto-qnx*)    targ_emul=i386nto ;;
 +i[3-7]86-*-acess2*)    targ_emul=acess2_i386 ;;
-+x86_64-*-acess2*)    targ_emul=acess2_amd64 ;;
++x86_64-*-acess2*)      targ_emul=acess2_amd64 ;;
++arm-*-acess2*)         targ_emul=acess2_arm ;;
 
diff --git a/Externals/cross-compiler/patches/binutils/ld/emulparams/acess2_amd64.sh b/Externals/cross-compiler/patches/binutils/ld/emulparams/acess2_amd64.sh
index d31dae98e1cc72466b11a52a065411b701ade2df..3d027850897961e4960b3aac4a2fd9d84dff0b36 100644
--- a/Externals/cross-compiler/patches/binutils/ld/emulparams/acess2_amd64.sh
+++ b/Externals/cross-compiler/patches/binutils/ld/emulparams/acess2_amd64.sh
@@ -1,11 +1,12 @@
 SCRIPT_NAME=elf
-OUTPUT_FORMAT=elf64-x86_64
+OUTPUT_FORMAT=elf64-x86-64
 TEXT_START_ADDR=0x00400000
 MAXPAGESIZE="CONSTANT (MAXPAGESIZE)"
 COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)"
-TEMPLATE_NAME=elf64
+ELFSIZE=64
+TEMPLATE_NAME=elf32
 
-ARCH=x86_64
+ARCH="i386:x86-64"
 MACHINE=
 NOP=0x90909090
 GENERATE_SHLIB_SCRIPT=yes
diff --git a/Externals/cross-compiler/patches/binutils/ld/emulparams/acess2_arm.sh b/Externals/cross-compiler/patches/binutils/ld/emulparams/acess2_arm.sh
new file mode 100644
index 0000000000000000000000000000000000000000..87ef5df09814887fca8e978f2833f2c3251d402d
--- /dev/null
+++ b/Externals/cross-compiler/patches/binutils/ld/emulparams/acess2_arm.sh
@@ -0,0 +1,19 @@
+SCRIPT_NAME=elf
+OUTPUT_FORMAT="elf32-littlearm"
+BIG_OUTPUT_FORMAT="elf32-bigarm"
+LITTLE_OUTPUT_FORMAT="elf32-littlearm"
+TEXT_START_ADDR=0x8000
+MAXPAGESIZE="CONSTANT (MAXPAGESIZE)"
+COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)"
+TEMPLATE_NAME=elf32
+
+ARCH=arm
+MACHINE=
+GENERATE_SHLIB_SCRIPT=yes
+GENERATE_PIE_SCRIPT=yes
+
+NO_SMALL_DATA=yes
+SEPARATE_GOTPLT=12
+
+ELF_INTERPRETER_NAME=\"/Acess/Libs/ld-acess.so\"
+
diff --git a/Externals/cross-compiler/patches/gcc/gcc/config.gcc.patch b/Externals/cross-compiler/patches/gcc/gcc/config.gcc.patch
index 4a1525f215329f185899fa278708858e9fcb703e..c6f085ec7e81ef700e0e12bad4a7e227ab738ac7 100644
--- a/Externals/cross-compiler/patches/gcc/gcc/config.gcc.patch
+++ b/Externals/cross-compiler/patches/gcc/gcc/config.gcc.patch
@@ -1,22 +1,34 @@
 --- gcc/config.gcc
 +++ gcc/config.gcc
-@@ -519,3 +519,10 @@
+@@ -519,3 +519,12 @@
  # Common parts for widely ported systems.
  case ${target} in
 +*-*-acess2*)
-+  extra_parts="crtbegin.o crtend.o"
++  extra_options="${extra_options} acess2.opt"
++  extra_parts="crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o crtendT.o"
 +  gas=yes
 +  gnu_ld=yes
 +  default_use_cxa_atexit=yes
 +  use_gcc_stdint=provide
++  thread_file=posix
 +  ;;
  *-*-darwin*)
 
-@@ -1192,2 +1196,7 @@
+@@ -1192,2 +1196,17 @@
  	;;
 +i[3-7]86-*-acess2*)
 +	tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h i386/i386elf.h newlib-stdint.h acess2.h"
-+	tmake_file="i386/t-i386elf t-svr4"
++	tmake_file="i386/t-i386elf i386/t-crtstuff t-svr4"
++	use_fixproto=yes
++	;;
++x86_64-*-acess2*)
++	tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h newlib-stdint.h i386/i386elf.h i386/x86-64.h acess2.h"
++	tmake_file="i386/t-i386elf i386/t-crtstuff t-svr4"
++	use_fixproto=yes
++	;;
++arm-*-acess2*)
++	tm_file="dbxelf.h elfos.h arm/unknown-elf.h arm/elf.h arm/bpabi.h newlib-stdint.h acess2.h arm/aout.h arm/arm.h"
++	tmake_file="arm/t-arm arm/t-arm-elf arm/t-bpabi"
 +	use_fixproto=yes
 +	;;
  i[34567]86-*-elf*)
diff --git a/Externals/cross-compiler/patches/gcc/gcc/config/acess2.h b/Externals/cross-compiler/patches/gcc/gcc/config/acess2.h
index ec3c1131d48f4c8cc9e5bc118718fd997381ba26..a22c5356bd5bf1d4817367e1a4ad605e10dbc068 100644
--- a/Externals/cross-compiler/patches/gcc/gcc/config/acess2.h
+++ b/Externals/cross-compiler/patches/gcc/gcc/config/acess2.h
@@ -5,10 +5,17 @@
     builtin_define_std ("unix");      \
     builtin_assert ("system=acess2");   \
     builtin_assert ("system=unix");   \
+    builtin_assert ("system=posix");   \
   } while(0);
 
-#define LIB_SPEC	"-lc -lld-acess -lposix"
+#define LIB_SPEC	"-lc -lld-acess -lposix %{pthread:-lpthread}"
 #define LIBSTDCXX "c++"
+#undef STARTFILE_SPEC
+#undef ENDFILE_SPEC
+#define STARTFILE_SPEC	"crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s} %{shared:crt0S.o%s;:crt0.o%s}"
+#define ENDFILE_SPEC	"%{static:crtendT.o%s;shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s"
+#undef LINK_SPEC
+#define LINK_SPEC	"%{shared:-shared} %{!shared:%{!static:%{rdynamic:-export-dynamic}%{!dynamic-linker:-dynamic-linker /Acess/Libs/ld-acess.so}}}"
 
 /*
 #undef TARGET_VERSION                                     // note that adding these two lines cause an error in gcc-4.7.0
diff --git a/Externals/cross-compiler/patches/gcc/gcc/config/acess2.opt b/Externals/cross-compiler/patches/gcc/gcc/config/acess2.opt
new file mode 100644
index 0000000000000000000000000000000000000000..e9db7e5ce596163ca54bd74ef5aaf58e0c016fb7
--- /dev/null
+++ b/Externals/cross-compiler/patches/gcc/gcc/config/acess2.opt
@@ -0,0 +1,6 @@
+; Options for acess2
+
+pthread
+Driver
+
+;
diff --git a/Externals/cross-compiler/patches/gcc/libgcc/config.host.patch b/Externals/cross-compiler/patches/gcc/libgcc/config.host.patch
index bcf7a4af560caed25ada6ac3a546a984f65c48ef..52725e33b9bb35504dca5efc53707263bc1ffbfb 100644
--- a/Externals/cross-compiler/patches/gcc/libgcc/config.host.patch
+++ b/Externals/cross-compiler/patches/gcc/libgcc/config.host.patch
@@ -1,16 +1,22 @@
 --- libgcc/config.host
 +++ libgcc/config.host
-@@ -523,4 +523,12 @@
+@@ -523,4 +523,18 @@
  x86_64-*-elf*)
  	tmake_file="$tmake_file i386/t-crtstuff t-crtstuff-pic t-libgcc-pic"
  	;;
 +i[3-7]86-*-acess2*)
-+	extra_parts="crtbegin.o crtend.o"
++	extra_parts="crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o crtendT.o"
 +	tmake_file="$tmake_file i386/t-crtstuff"
 +	;;
 +x86_64-*-acess2*)
-+	extra_parts="crtbegin.o crtend.o"
++	extra_parts="crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o crtendT.o"
 +	tmake_file="$tmake_file i386/t-crtstuff t-crtstuff-pic t-libgcc-pic"
++	;;
++arm-*-acess2*)
++	extra_parts="crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o crtendT.o"
++	tmake_file="${tmake_file} arm/t-arm arm/t-elf t-fixedpoint-gnu-prefix arm/t-bpabi t-softfp-sfdf t-softfp-excl arm/t-softfp t-softfp"
++	tm_file="$tm_file arm/bpabi-lib.h"
++	unwind_header=config/arm/unwind-arm.h
 +	;;
  i[34567]86-*-freebsd*)
 
diff --git a/Externals/curl/Makefile b/Externals/curl/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..e0c5eefac9aa27f2969b7e14888c2d964ac76e3a
--- /dev/null
+++ b/Externals/curl/Makefile
@@ -0,0 +1,14 @@
+#
+# Acess2 External: libcurl
+# - Makefile and patches by John Hodge (thePowersGang)
+#
+
+DEPS := 
+TARBALL_PATTERN := curl-*.tar.bz2
+TARBALL_TO_DIR_L := %.tar.bz2
+TARBALL_TO_DIR_R := %
+PATCHES := config.sub
+CONFIGURE_ARGS = LIBS=-lpsocket
+
+include ../common_automake.mk
+
diff --git a/Externals/curl/patches/config.sub.patch b/Externals/curl/patches/config.sub.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8380154724151d1a9523055512b4e3acfad1bce4
--- /dev/null
+++ b/Externals/curl/patches/config.sub.patch
@@ -0,0 +1,7 @@
+--- bochs-2.6.2_orig/config.sub	2013-06-17 11:39:39.670720710 +0800
++++ bochs-2.6.2/config.sub	2013-06-17 11:48:09.149384231 +0800
+@@ -1356,2 +1356,3 @@
+ 	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
++	      | -acess2 \
+ 	      | -aos* | -aros* \
+
diff --git a/Externals/glib/Makefile b/Externals/glib/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..953ef038da4bdd37888d3de358d15a7feb798e8d
--- /dev/null
+++ b/Externals/glib/Makefile
@@ -0,0 +1,17 @@
+#
+# Acess2 Externals - glib
+# - Patches and Makefile by John Hodge (thePowersGang)
+#
+
+DEPS := libffi
+_NAME := glib
+TARBALL_PATTERN := $(_NAME)-*.tar.xz
+TARBALL_TO_DIR_L := $(_NAME)-%.tar.xz
+TARBALL_TO_DIR_R := $(_NAME)-%
+PATCHES := config.sub
+CONFIGURE_ARGS := glib_cv_stack_grows=no ac_cv_func_posix_getpwuid_r=no ac_cv_func_posix_getgrgid_r=no
+CONFIGURE_ARGS += LDFLAGS=-lpsocket
+
+include ../common_automake.mk
+
+
diff --git a/Externals/glib/patches/config.sub.patch b/Externals/glib/patches/config.sub.patch
new file mode 100644
index 0000000000000000000000000000000000000000..f600ad4b374acc30a81e55937316788e8affe64d
--- /dev/null
+++ b/Externals/glib/patches/config.sub.patch
@@ -0,0 +1,8 @@
+--- glib/config.sub
++++ glib/config.sub
+@@ -1335,2 +1335,5 @@
+		;;
++	-acess2)
++		os=-acess2
++		;;
+	-solaris)
diff --git a/Externals/libspiderscript/source b/Externals/libspiderscript/source
index a5d190a89ca3f3a78c8b44a855b044ff4e713bb3..9f2d7faf34c16ceaee2f1bffe3d5558c41382523 160000
--- a/Externals/libspiderscript/source
+++ b/Externals/libspiderscript/source
@@ -1 +1 @@
-Subproject commit a5d190a89ca3f3a78c8b44a855b044ff4e713bb3
+Subproject commit 9f2d7faf34c16ceaee2f1bffe3d5558c41382523
diff --git a/Externals/netsurf/Makefile b/Externals/netsurf/Makefile
index d4140c3f3efe5426a202da1c868f674fb69b4a9a..5b69f5d8ae1d7ec92ba183b79cad4179bd8ec042 100644
--- a/Externals/netsurf/Makefile
+++ b/Externals/netsurf/Makefile
@@ -12,7 +12,12 @@ NOBDIR = yes
 
 include ../core.mk
 
-_build:
-	cd $(BDIR) && CC=$(HOST)-gcc TARGET=sdl make
+.PHONY: _check_local_deps
+
+_build: _check_local_deps
+	cd $(BDIR) && CC=$(HOST)-gcc TARGET=framebuffer make
+
+_check_local_deps:
+	@gperf --help >/dev/null || (echo "ERROR: netsurf's build system requires gperf"; false)
 
 
diff --git a/Externals/netsurf/patches/UNIFIED.patch b/Externals/netsurf/patches/UNIFIED.patch
index e94563138732104d4b498334fd83aba584bd50dd..f59d7c172da4d8d8dc499d047af334cf6ab945a7 100644
--- a/Externals/netsurf/patches/UNIFIED.patch
+++ b/Externals/netsurf/patches/UNIFIED.patch
@@ -108,3 +108,15 @@ diff -ru .orig/netsurf-full-3.0//src/libparserutils-0.1.2/src/utils/vector.c net
  {
  	if (vector == NULL || vector->current_item < 0)
  		return NULL;
+diff -ru .orig/netsurf-full-3.0//src/libnsfb-0.1.2/Makefile netsurf-full-3.0//src/libnsfb-0.1.0/Makefile
+--- .orig/netsurf-full-3.0//src/libnsfb-0.1.0/Makefile	2013-04-20 02:06:57.000000000 +0800
++++ netsurf-full-3.0//src/libnsfb-0.1.0/Makefile	2013-07-01 22:22:45.246689992 +0800
+@@ -31,5 +31,5 @@
+ # surfaces not detectable via pkg-config 
+ NSFB_ABLE_AVAILABLE := no
+-NSFB_LINUX_AVAILABLE := yes
++NSFB_LINUX_AVAILABLE := no
+ 
+ # Flags and setup for each support library
+ NSFB_ABLE_AVAILABLE := no
+ 
diff --git a/Externals/pkgconfig/Makefile b/Externals/pkgconfig/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..bcd63052eaa9e21ac5d7aae09f906e557989b035
--- /dev/null
+++ b/Externals/pkgconfig/Makefile
@@ -0,0 +1,17 @@
+#
+# Acess2 Externals - pkgconfig
+# - Patches and Makefile by John Hodge (thePowersGang)
+#
+
+DEPS := 
+TARBALL_PATTERN := pkg-config-*.tar.gz
+TARBALL_TO_DIR_L := pkg-config-%.tar.gz
+TARBALL_TO_DIR_R := pkg-config-%
+PATCHES := 
+CONFIGURE_LINE = $(SDIR)/configure --prefix=$(BUILD_OUTDIR) --with-internal-glib
+
+include ../common_automake.mk
+
+
+
+
diff --git a/Externals/pkgconfig/patches/config.sub.patch b/Externals/pkgconfig/patches/config.sub.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b2d73891f75db2ec5941170f972abc91da6ce25f
--- /dev/null
+++ b/Externals/pkgconfig/patches/config.sub.patch
@@ -0,0 +1,7 @@
+--- bochs-2.6.2_orig/config.sub	2013-06-17 11:39:39.670720710 +0800
++++ bochs-2.6.2/config.sub	2013-06-17 11:48:09.149384231 +0800
+@@ -1344,2 +1344,3 @@
+ 	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
++	      | -acess2 \
+ 	      | -aos* | -aros* \
+
diff --git a/Externals/pkgconfig/patches/glib/config.sub.patch b/Externals/pkgconfig/patches/glib/config.sub.patch
new file mode 100644
index 0000000000000000000000000000000000000000..f600ad4b374acc30a81e55937316788e8affe64d
--- /dev/null
+++ b/Externals/pkgconfig/patches/glib/config.sub.patch
@@ -0,0 +1,8 @@
+--- glib/config.sub
++++ glib/config.sub
+@@ -1335,2 +1335,5 @@
+		;;
++	-acess2)
++		os=-acess2
++		;;
+	-solaris)
diff --git a/KernelLand/Kernel/GenSyscalls.pl b/KernelLand/Kernel/GenSyscalls.pl
index fdf33e91611b074e3cd252abc09fc516a97d2584..e7e3cbe14ed95e7818ef978d90410431b708de9d 100755
--- a/KernelLand/Kernel/GenSyscalls.pl
+++ b/KernelLand/Kernel/GenSyscalls.pl
@@ -46,7 +46,9 @@ foreach my $call (@calls)
 }
 print HEADER "
 #define NUM_SYSCALLS	",$i,"
-#define SYS_DEBUG	0x100
+#define SYS_DEBUGS	0x100
+#define SYS_DEBUGF	0x101
+#define SYS_DEBUGHEX	0x102
 
 #if !defined(__ASSEMBLER__) && !defined(NO_SYSCALL_STRS)
 static const char *cSYSCALL_NAMES[] = {
diff --git a/KernelLand/Kernel/Makefile b/KernelLand/Kernel/Makefile
index cc4f230524b8f28ff7b7968ff3798e5ce52d74e3..895d90f9878e99f141b739b8e3ca368551e94e55 100644
--- a/KernelLand/Kernel/Makefile
+++ b/KernelLand/Kernel/Makefile
@@ -56,13 +56,13 @@ BUILDINFO_SRC := $(OBJDIR)buildinfo.c$(OBJSUFFIX)
 OBJ := $(addprefix arch/$(ARCHDIR)/,$(A_OBJ))
 OBJ += pmemmap.o
 OBJ += heap.o logging.o debug.o lib.o libc.o adt.o time.o utf16.o debug_hooks.o
-OBJ += drvutil_video.o drvutil_disk.o
-OBJ += messages.o modules.o syscalls.o system.o
+OBJ += drvutil_video.o drvutil_disk.o memfs_helpers.o
+OBJ += messages.o modules.o syscalls.o system.o emergency_console.o
 OBJ += threads.o mutex.o semaphore.o workqueue.o events.o rwlock.o
 OBJ += drv/zero-one.o drv/proc.o drv/fifo.o drv/dgram_pipe.o drv/iocache.o drv/pci.o drv/vpci.o
 OBJ += drv/vterm.o drv/vterm_font.o drv/vterm_vt100.o drv/vterm_output.o drv/vterm_input.o drv/vterm_termbuf.o
 OBJ += drv/vterm_2d.o
-OBJ += drv/pty.o drv/serial.o
+OBJ += drv/pty.o drv/serial.o drv/shm.o
 OBJ += binary.o bin/elf.o bin/pe.o
 OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/dir.o vfs/io.o vfs/mount.o
 OBJ += vfs/memfile.o vfs/nodecache.o vfs/handle.o vfs/select.o vfs/mmap.o
@@ -153,7 +153,7 @@ $(BUILDINFO_SRC): $(filter-out $(BUILDINFO_OBJ), $(OBJ)) $(MODS) arch/$(ARCHDIR)
 	@echo "const char gsGitHash[] = \"$(_GITHASH)\";" >> $@
 	@echo "const int giBuildNumber = $(BUILD_NUM);" >> $@
 	@echo "const char gsBuildInfo[] = \"Acess2 v$(KERNEL_VERSION) $(ARCH)-$(PLATFORM)\\\\r\\\\n\"" >> $@
-	@echo "                           \"Build $(shell hostname --fqdn):$(BUILD_NUM) Git $(_GITHASH) - $(_GITCHANGED) modified\";" >> $@
+	@echo "                           \"Build $(shell hostname --fqdn):$(BUILD_NUM) $(shell date +%FT%T%z) Git $(_GITHASH) - $(_GITCHANGED) modified\";" >> $@
 # Compile rule for buildinfo (needs a special one because it's not a general source file)
 $(BUILDINFO_OBJ): $(BUILDINFO_SRC)
 	@echo --- CC -o $@
diff --git a/KernelLand/Kernel/arch/helpers.h b/KernelLand/Kernel/arch/helpers.h
index 4d85fcb4d5f31aeaae7df3f1b9e89143c910bd94..b478031003456503481e7d860da45e2d4dc6cb65 100644
--- a/KernelLand/Kernel/arch/helpers.h
+++ b/KernelLand/Kernel/arch/helpers.h
@@ -14,10 +14,10 @@
 //  > If the `N` value is greater than `D`, we can set this bit
 #define DEF_DIVMOD(s) Uint##s __divmod##s(Uint##s N, Uint##s D, Uint##s*Rem){\
 	Uint##s ret=0,add=1;\
-	while(N>=D&&add) {D<<=1;add<<=1;}\
-	while(add>1){\
-		add>>=1;D>>=1;\
+	while(N/2>=D&&add) {D<<=1;add<<=1;}\
+	while(add>0){\
 		if(N>=D){ret+=add;N-=D;}\
+		add>>=1;D>>=1;\
 	}\
 	if(Rem)*Rem = N;\
 	return ret;\
diff --git a/KernelLand/Kernel/arch/x86/common.inc.asm b/KernelLand/Kernel/arch/x86/common.inc.asm
new file mode 100644
index 0000000000000000000000000000000000000000..ec7f8441028186cf503ae8e8447d495d0dee3971
--- /dev/null
+++ b/KernelLand/Kernel/arch/x86/common.inc.asm
@@ -0,0 +1,24 @@
+%macro PUSH_CC	0
+	; Don't bother being too picky, just take the time
+	pusha
+%endmacro
+%macro PUSH_SEG	0
+	push ds
+	push es
+	push fs
+	push gs
+	mov ax, 0x10
+	mov ds, ax
+	mov es, ax
+	mov fs, ax
+	mov gs, ax
+%endmacro
+%macro POP_CC	0
+	popa
+%endmacro
+%macro POP_SEG	0
+	pop gs
+	pop fs
+	pop es
+	pop ds
+%endmacro
diff --git a/KernelLand/Kernel/arch/x86/desctab.asm b/KernelLand/Kernel/arch/x86/desctab.asm
index 354a001eb375b3a1e747d7930aa3f18694f3a02c..338253474392d1efa09152c76122b9b3a017cf6e 100644
--- a/KernelLand/Kernel/arch/x86/desctab.asm
+++ b/KernelLand/Kernel/arch/x86/desctab.asm
@@ -1,6 +1,7 @@
 ; AcessOS Microkernel Version
 ;
 ; desctab.asm
+%include "arch/x86/common.inc.asm"
 [BITS 32]
 
 
@@ -182,14 +183,14 @@ Isr0xED:
 	jmp .jmp
 
 [global Isr0xEE]
-[extern SchedulerBase]
+[extern Proc_EventTimer_LAPIC]
 ; AP's Timer Interrupt
 Isr0xEE:
 	push eax	; Line up with interrupt number
 	mov eax, dr1	; CPU Number
 	push eax
 	mov eax, [esp+4]	; Load EAX back
-	jmp SchedulerBase
+	jmp Proc_EventTimer_LAPIC
 ; Spurious Interrupt
 [global Isr0xEF]
 Isr0xEF:
@@ -201,7 +202,7 @@ Isr0xEF:
 ; - Timer
 [global Isr240]
 [global Isr240.jmp]
-[extern SchedulerBase]
+[extern Proc_EventTimer_PIT]
 [extern SetAPICTimerCount]
 Isr240:
 	push 0	; Line up with Argument in errors
@@ -211,7 +212,7 @@ Isr240.jmp:
 	%if USE_MP
 	jmp SetAPICTimerCount	; This is reset once the bus speed has been calculated
 	%else
-	jmp SchedulerBase
+	jmp Proc_EventTimer_PIT
 	%endif
 ; - Assignable
 %assign i	0xF1
@@ -254,11 +255,8 @@ ErrorCommon:
 ; --------------------------
 [extern SyscallHandler]
 SyscallCommon:
-	pusha
-	push ds
-	push es
-	push fs
-	push gs
+	PUSH_CC	; Actually a pusha
+	PUSH_SEG
 	
 	push esp
 	call SyscallHandler
@@ -286,17 +284,8 @@ SyscallCommon:
 [global IRQCommon_handled]
 IRQCommon_handled equ IRQCommon.handled
 IRQCommon:
-	pusha
-	push ds
-	push es
-	push fs
-	push gs
-	
-	mov ax, 0x10
-	mov ds, ax
-	mov es, ax
-	mov fs, ax
-	mov gs, ax
+	PUSH_CC
+	PUSH_SEG
 	
 	push esp
 	call IRQ_Handler
diff --git a/KernelLand/Kernel/arch/x86/mm_virt.c b/KernelLand/Kernel/arch/x86/mm_virt.c
index aec6c60651bc7fb913358cbebb50b2c2eafba339..80875185405089d833980c6d6623d0a5b2b68907 100644
--- a/KernelLand/Kernel/arch/x86/mm_virt.c
+++ b/KernelLand/Kernel/arch/x86/mm_virt.c
@@ -74,6 +74,7 @@ void	MM_InstallVirtual(void);
 void	MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
 void	MM_DumpTables_Print(tVAddr Start, Uint32 Orig, size_t Size, void *Node);
 //void	MM_DumpTables(tVAddr Start, tVAddr End);
+tPAddr	MM_GetPageFromAS(tProcess *Process, volatile const void *Addr);
 //void	MM_ClearUser(void);
 tPAddr	MM_DuplicatePage(tVAddr VAddr);
 
@@ -538,6 +539,23 @@ tPAddr MM_GetPhysAddr(volatile const void *Addr)
 	return (gaPageTable[addr >> 12] & ~0xFFF) | (addr & 0xFFF);
 }
 
+/**
+ * \brief Get the address of a page from another addres space
+ * \return Refenced physical address (or 0 on error)
+ */
+tPAddr MM_GetPageFromAS(tProcess *Process, volatile const void *Addr)
+{
+	tPAddr	ret = 0;
+	GET_TEMP_MAPPING(Process->MemState.CR3);
+	tVAddr	addr = (tVAddr)Addr;
+	if( (gaTmpDir[addr >> 22] & 1) && (gaTmpTable[addr >> 12] & 1) ) {
+		ret = (gaTmpTable[addr >> 12] & ~0xFFF) | (addr & 0xFFF);
+		MM_RefPhys( ret );
+	}
+	REL_TEMP_MAPPING();
+	return ret;
+}
+
 /**
  * \fn void MM_SetCR3(Uint CR3)
  * \brief Sets the current process space
@@ -552,18 +570,17 @@ void MM_SetCR3(Uint CR3)
  */
 void MM_ClearUser(void)
 {
-	Uint	i, j;
-	
-	for( i = 0; i < (MM_USER_MAX>>22); i ++ )
+	ASSERTC(MM_PPD_MIN, ==, MM_USER_MAX);
+	for( unsigned int i = 0; i < (MM_USER_MAX>>22); i ++ )
 	{
 		// Check if directory is not allocated
 		if( !(gaPageDir[i] & PF_PRESENT) ) {
 			gaPageDir[i] = 0;
 			continue;
 		}
-		
+	
 		// Deallocate tables
-		for( j = 0; j < 1024; j ++ )
+		for( unsigned int j = 0; j < 1024; j ++ )
 		{
 			if( gaPageTable[i*1024+j] & 1 )
 				MM_DerefPhys( gaPageTable[i*1024+j] & ~0xFFF );
@@ -583,8 +600,6 @@ void MM_ClearUser(void)
  */
 void MM_ClearSpace(Uint32 CR3)
 {
-	 int	i, j;
-	
 	if(CR3 == (*gpPageCR3 & ~0xFFF)) {
 		Log_Error("MMVirt", "Can't clear current address space");
 		return ;
@@ -601,7 +616,7 @@ void MM_ClearSpace(Uint32 CR3)
 	GET_TEMP_MAPPING(CR3);
 	INVLPG( gaTmpDir );
 
-	for( i = 0; i < 1024; i ++ )
+	for( int i = 0; i < 1024; i ++ )
 	{
 		Uint32	*table = &gaTmpTable[i*1024];
 		if( !(gaTmpDir[i] & PF_PRESENT) )
@@ -611,7 +626,7 @@ void MM_ClearSpace(Uint32 CR3)
 
 		if( i < 768 || (i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) )
 		{
-			for( j = 0; j < 1024; j ++ )
+			for( int j = 0; j < 1024; j ++ )
 			{
 				if( !(table[j] & 1) )
 					continue;
@@ -1052,6 +1067,8 @@ void *MM_MapTemp(tPAddr PAddr)
 		LOG("%i: %x", i, *pte);
 		// Check if page used
 		if(*pte & 1)	continue;
+		MM_RefPhys( PAddr );
+		
 		// Mark as used
 		*pte = PAddr | 3;
 		INVLPG( TEMP_MAP_ADDR + (i << 12) );
@@ -1064,6 +1081,15 @@ void *MM_MapTemp(tPAddr PAddr)
 	return NULL;
 }
 
+void *MM_MapTempFromProc(tProcess *Process, const void *VAddr)
+{
+	// Get paddr
+	tPAddr	paddr = MM_GetPageFromAS(Process, VAddr);
+	if( paddr == 0 )
+		return NULL;
+	return MM_MapTemp(paddr);
+}
+
 /**
  * \fn void MM_FreeTemp(tVAddr PAddr)
  * \brief Free's a temp mapping
@@ -1073,7 +1099,9 @@ void MM_FreeTemp(void *VAddr)
 	 int	i = (tVAddr)VAddr >> 12;
 	//ENTER("xVAddr", VAddr);
 	
-	if(i >= (TEMP_MAP_ADDR >> 12)) {
+	if(i >= (TEMP_MAP_ADDR >> 12))
+	{
+		MM_DerefPhys( gaPageTable[i] & ~0xFFF );
 		gaPageTable[ i ] = 0;
 		Semaphore_Signal(&gTempMappingsSem, 1);
 	}
diff --git a/KernelLand/Kernel/arch/x86/proc.asm b/KernelLand/Kernel/arch/x86/proc.asm
index b8c01bcf451fe4e520a3b81ddd145e6530627cfa..1987166d6c2ff1d6e5a8a120eaea6b62c7e89288 100644
--- a/KernelLand/Kernel/arch/x86/proc.asm
+++ b/KernelLand/Kernel/arch/x86/proc.asm
@@ -1,5 +1,6 @@
 ; AcessOS Microkernel Version
 ; Start.asm
+%include "arch/x86/common.inc.asm"
 
 [bits 32]
 
@@ -11,19 +12,6 @@ KSTACK_USERSTATE_SIZE	equ	(4+8+1+5)*4	; SRegs, GPRegs, CPU, IRET
 
 [section .text]
 
-[global NewTaskHeader]
-NewTaskHeader:
-	mov eax, [esp]
-	mov dr0, eax
-
-	mov eax, [esp+4]
-	add esp, 12	; Thread, Function, Arg Count
-	call eax
-	
-	push eax	; Ret val
-	push 0  	; 0 = This Thread
-	call Threads_Exit
-
 [extern MM_Clone]
 [global Proc_CloneInt]
 Proc_CloneInt:
@@ -38,7 +26,7 @@ Proc_CloneInt:
 	mov esi, [esp+0x20+8]
 	mov [esi], eax
 	; Undo the pusha
-	add esp, 0x20
+	popa
 	mov eax, .newTask
 	ret
 .newTask:
@@ -53,7 +41,7 @@ Proc_CloneInt:
 ; +16 = Old RIP save loc
 ; +20 = CR3
 SwitchTasks:
-	pusha
+	PUSH_CC
 	
 	; Old IP
 	mov eax, [esp+0x20+16]
@@ -78,7 +66,7 @@ SwitchTasks:
 	jmp ecx
 
 .restore:
-	popa
+	POP_CC
 	xor eax, eax
 	ret
 
@@ -122,17 +110,8 @@ Proc_RestoreSSE:
 [extern Isr240.jmp]
 [global SetAPICTimerCount]
 SetAPICTimerCount:
-	pusha
-	push ds
-	push es
-	push fs
-	push gs
-	
-	mov ax, 0x10
-	mov ds, ax
-	mov es, ax
-	mov fs, ax
-	mov gs, ax
+	PUSH_CC
+	PUSH_SEG
 	
 	mov eax, [gpMP_LocalAPIC]
 	mov ecx, [eax+0x320]
@@ -152,7 +131,7 @@ SetAPICTimerCount:
 	mov DWORD [eax+0x380], 0
 
 	; Update Timer IRQ to the IRQ code
-	mov eax, SchedulerBase
+	mov eax, Proc_EventTimer_PIT
 	sub eax, Isr240.jmp+5
 	mov DWORD [Isr240.jmp+1], eax
 
@@ -160,72 +139,55 @@ SetAPICTimerCount:
 .ret:
 	mov dx, 0x20
 	mov al, 0x20
-	out dx, al		; ACK IRQ
-	pop gs
-	pop fs
-	pop es
-	pop ds
-	popa
+	out 0x20, al		; ACK IRQ
+	POP_SEG
+	POP_CC
 	add esp, 8	; CPU ID / Error Code
 	iret
 %endif
-; --------------
-; Task Scheduler
-; --------------
-[extern Proc_Scheduler]
-[global SchedulerBase]
-SchedulerBase:
-	pusha
-	push ds
-	push es
-	push fs
-	push gs
+
+%if USE_MP
+[global Proc_EventTimer_LAPIC]
+Proc_EventTimer_LAPIC:
+	push eax
+	mov eax, SS:[gpMP_LocalAPIC]
+	mov DWORD SS:[eax + 0xB0], 0
+	pop eax
+	jmp Proc_EventTimer_Common
+%endif
+[global Proc_EventTimer_PIT]
+Proc_EventTimer_PIT:
+	push eax
+	mov al, 0x20
+	out 0x20, al		; ACK IRQ
+	pop eax
+	jmp Proc_EventTimer_Common
+[extern Proc_HandleEventTimer]
+[global Proc_EventTimer_Common]
+Proc_EventTimer_Common:
+	PUSH_CC
+	PUSH_SEG
 	
+	; Clear the Trace/Trap flag
 	pushf
 	and BYTE [esp+1], 0xFE	; Clear Trap Flag
 	popf
-	
-	mov eax, dr0
-	push eax	; Debug Register 0, Current Thread
-	
-	mov ax, 0x10
-	mov ds, ax
-	mov es, ax
-	mov fs, ax
-	mov gs, ax
+	; Re-enable interrupts
+	; - TODO: This is quite likely racy, if we get an interrupt flood
+	sti
 	
 	%if USE_MP
 	call GetCPUNum
-	mov ebx, eax
 	push eax	; Push as argument
 	%else
 	push 0
 	%endif
 	
-	call Proc_Scheduler
+	call Proc_HandleEventTimer
 [global scheduler_return]
 scheduler_return:	; Used by some hackery in Proc_DumpThreadCPUState
-	
 	add esp, 4	; Remove CPU Number (thread is poped later)
 
-	%if USE_MP
-	test ebx, ebx
-	jnz .sendEOI
-	%endif
-	
-	mov al, 0x20
-	out 0x20, al		; ACK IRQ
-
-	%if USE_MP
-	jmp .ret
-.sendEOI:
-	mov eax, DWORD [gpMP_LocalAPIC]
-	mov DWORD [eax+0x0B0], 0
-	%endif
-.ret:
-	pop eax	; Debug Register 0, Current Thread
-	mov dr0, eax
-	
 	jmp ReturnFromInterrupt
 
 ;
diff --git a/KernelLand/Kernel/arch/x86/proc.c b/KernelLand/Kernel/arch/x86/proc.c
index 5637985fbbfb318638a021eb40c876c1c199ba60..b1dcb99dc1180ae1f993d2d438c92452d9ca0458 100644
--- a/KernelLand/Kernel/arch/x86/proc.c
+++ b/KernelLand/Kernel/arch/x86/proc.c
@@ -44,7 +44,6 @@ extern tGDT	gGDT[];
 extern tIDT	gIDT[];
 extern void	APWait(void);	// 16-bit AP pause code
 extern void	APStartup(void);	// 16-bit AP startup code
-extern void	NewTaskHeader(tThread *Thread, void *Fcn, int nArgs, ...);	// Actually takes cdecl args
 extern Uint	Proc_CloneInt(Uint *ESP, Uint32 *CR3, int bNoUserClone);
 extern Uint32	gaInitPageDir[1024];	// start.asm
 extern char	Kernel_Stack_Top[];
@@ -71,6 +70,7 @@ void	Proc_IdleThread(void *Ptr);
 //tThread	*Proc_GetCurThread(void);
 void	Proc_ChangeStack(void);
 // int	Proc_NewKThread(void (*Fcn)(void*), void *Data);
+void	NewTaskHeader(tThread *Thread, void (*Fcn)(void*), void *Data);	// Actually takes cdecl args
 // int	Proc_Clone(Uint *Err, Uint Flags);
 Uint	Proc_MakeUserStack(void);
 //void	Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize);
@@ -78,7 +78,7 @@ void	Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) NO
 void	Proc_CallUser(Uint32 UserIP, Uint32 UserSP, const void *StackData, size_t StackDataLen);
 //void	Proc_CallFaultHandler(tThread *Thread);
 //void	Proc_DumpThreadCPUState(tThread *Thread);
-void	Proc_Scheduler(int CPU);
+void	Proc_HandleEventTimer(int CPU);
 
 // === GLOBALS ===
 // --- Multiprocessing ---
@@ -307,10 +307,12 @@ void Proc_IdleThread(void *Ptr)
 	cpu->Current->ThreadName = strdup("Idle Thread");
 	Threads_SetPriority( cpu->Current, -1 );	// Never called randomly
 	cpu->Current->Quantum = 1;	// 1 slice quantum
-	for(;;) {
+	LOG("Idle thread for CPU %i ready", GetCPUNum());
+	for(;;)
+	{
 		__asm__ __volatile__ ("sti");	// Make sure interrupts are enabled
-		__asm__ __volatile__ ("hlt");
-		Proc_Reschedule();
+		Proc_Reschedule();	// Reshedule
+		__asm__ __volatile__ ("hlt");	// And wait for an interrupt if we get scheduled again
 	}
 }
 
@@ -432,10 +434,7 @@ void Proc_ClearThread(tThread *Thread)
 
 tTID Proc_NewKThread(void (*Fcn)(void*), void *Data)
 {
-	Uint	esp;
-	tThread	*newThread;
-	
-	newThread = Threads_CloneTCB(0);
+	tThread *newThread = Threads_CloneTCB(0);
 	if(!newThread)	return -1;
 	
 	// Create new KStack
@@ -445,12 +444,14 @@ tTID Proc_NewKThread(void (*Fcn)(void*), void *Data)
 		free(newThread);
 		return -1;
 	}
+	
+	LOG("%p(%i %s) SP=%p", newThread, newThread->TID, newThread->ThreadName, newThread->KernelStack);
 
-	esp = newThread->KernelStack;
+	Uint esp = newThread->KernelStack;
 	*(Uint*)(esp-=4) = (Uint)Data;	// Data (shadowed)
-	*(Uint*)(esp-=4) = 1;	// Number of params
 	*(Uint*)(esp-=4) = (Uint)Fcn;	// Function to call
 	*(Uint*)(esp-=4) = (Uint)newThread;	// Thread ID
+	*(Uint*)(esp-=4) = (Uint)0;	// Empty return address
 	
 	newThread->SavedState.ESP = esp;
 	newThread->SavedState.EIP = (Uint)&NewTaskHeader;
@@ -463,15 +464,24 @@ tTID Proc_NewKThread(void (*Fcn)(void*), void *Data)
 	return newThread->TID;
 }
 
+void NewTaskHeader(tThread *NewThread, void (*Fcn)(void*), void *Data)
+{
+	LOG("NewThread=%p, Fcn=%p, Data=%p", NewThread, Fcn, Data);
+	__asm__ __volatile__ ("mov %0, %%dr0" : : "r"(NewThread));
+	SHORTREL(&glThreadListLock);
+	Fcn(Data);
+	
+	Threads_Exit(0, 0);
+	for(;;);
+}
+
 /**
  * \fn int Proc_Clone(Uint *Err, Uint Flags)
  * \brief Clone the current process
  */
 tPID Proc_Clone(Uint Flags)
 {
-	tThread	*newThread;
 	tThread	*cur = Proc_GetCurThread();
-	Uint	eip;
 
 	// Sanity, please
 	if( !(Flags & CLONE_VM) ) {
@@ -480,16 +490,22 @@ tPID Proc_Clone(Uint Flags)
 	}
 	
 	// New thread
-	newThread = Threads_CloneTCB(Flags);
+	tThread *newThread = Threads_CloneTCB(Flags);
 	if(!newThread)	return -1;
+	ASSERT(newThread->Process);
 
 	newThread->KernelStack = cur->KernelStack;
 
 	// Clone state
-	eip = Proc_CloneInt(&newThread->SavedState.ESP, &newThread->Process->MemState.CR3, Flags & CLONE_NOUSER);
+	Uint eip = Proc_CloneInt(&newThread->SavedState.ESP, &newThread->Process->MemState.CR3, Flags & CLONE_NOUSER);
 	if( eip == 0 ) {
+		SHORTREL( &glThreadListLock );
+		LOG("In new thread");
 		return 0;
 	}
+	//ASSERT(newThread->Process);
+	//ASSERT(CheckMem(newThread->Process, sizeof(tProcess)));
+	//LOG("newThread->Process = %p", newThread->Process);
 	newThread->SavedState.EIP = eip;
 	newThread->SavedState.SSE = NULL;
 	newThread->SavedState.bSSEModified = 0;
@@ -522,12 +538,13 @@ tThread *Proc_SpawnWorker(void (*Fcn)(void*), void *Data)
 		Warning("Proc_SpawnWorker - Out of heap space!\n");
 		return NULL;
 	}
+	LOG("new = (%i %s)", new->TID, new->ThreadName);
 
 	// Create the stack contents
 	stack_contents[3] = (Uint)Data;
-	stack_contents[2] = 1;
-	stack_contents[1] = (Uint)Fcn;
-	stack_contents[0] = (Uint)new;
+	stack_contents[2] = (Uint)Fcn;
+	stack_contents[1] = (Uint)new;
+	stack_contents[0] = 0;
 	
 	// Create a new worker stack (in PID0's address space)
 	new->KernelStack = MM_NewWorkerStack(stack_contents, sizeof(stack_contents));
@@ -760,82 +777,82 @@ void Proc_DumpThreadCPUState(tThread *Thread)
 
 void Proc_Reschedule(void)
 {
-	tThread	*nextthread, *curthread;
 	 int	cpu = GetCPUNum();
 
 	// TODO: Wait for the lock?
-	if(IS_LOCKED(&glThreadListLock))        return;
+	if(IS_LOCKED(&glThreadListLock)) {
+		LOG("Thread list locked, not rescheduling");
+		return;
+	}
 	
-	curthread = Proc_GetCurThread();
-
-	nextthread = Threads_GetNextToRun(cpu, curthread);
-
-	if(!nextthread || nextthread == curthread)
-		return ;
-
-	#if DEBUG_TRACE_SWITCH
-	// HACK: Ignores switches to the idle threads
-	if( nextthread->TID == 0 || nextthread->TID > giNumCPUs )
+	SHORTLOCK(&glThreadListLock);
+	
+	tThread *curthread = Proc_GetCurThread();
+	tThread *nextthread = Threads_GetNextToRun(cpu, curthread);
+	
+	if(nextthread && nextthread != curthread)
 	{
-		LogF("\nSwitching CPU %i to %p (%i %s) - CR3 = 0x%x, EIP = %p, ESP = %p\n",
-			GetCPUNum(),
-			nextthread, nextthread->TID, nextthread->ThreadName,
-			nextthread->Process->MemState.CR3,
-			nextthread->SavedState.EIP,
-			nextthread->SavedState.ESP
-			);
-		LogF("OldCR3 = %P\n", curthread->Process->MemState.CR3);
-	}
-	#endif
+		#if DEBUG_TRACE_SWITCH
+		// HACK: Ignores switches to the idle threads
+		//if( nextthread->TID == 0 || nextthread->TID > giNumCPUs )
+		{
+			LogF("\nSwitching CPU %i to %p (%i %s) - CR3 = 0x%x, EIP = %p, ESP = %p\n",
+				GetCPUNum(),
+				nextthread, nextthread->TID, nextthread->ThreadName,
+				nextthread->Process->MemState.CR3,
+				nextthread->SavedState.EIP,
+				nextthread->SavedState.ESP
+				);
+			LogF(" from %p (%i %s) - CR3 = 0x%x, EIP = %p, ESP = %p\n",
+				curthread, curthread->TID, curthread->ThreadName,
+				curthread->Process->MemState.CR3,
+				curthread->SavedState.EIP,
+				curthread->SavedState.ESP
+				);
+		}
+		#endif
 
-	// Update CPU state
-	gaCPUs[cpu].Current = nextthread;
-	gaCPUs[cpu].LastTimerThread = NULL;
-	gTSSs[cpu].ESP0 = nextthread->KernelStack-4;
-	__asm__ __volatile__("mov %0, %%db0\n\t" : : "r"(nextthread) );
+		// Update CPU state
+		gaCPUs[cpu].Current = nextthread;
+		gaCPUs[cpu].LastTimerThread = NULL;
+		gTSSs[cpu].ESP0 = nextthread->KernelStack-4;
+		__asm__ __volatile__("mov %0, %%db0\n\t" : : "r"(nextthread) );
 
-	// Save FPU/MMX/XMM/SSE state
-	if( curthread && curthread->SavedState.SSE )
-	{
-		Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF );
-		curthread->SavedState.bSSEModified = 0;
-		Proc_DisableSSE();
-	}
+		// Save FPU/MMX/XMM/SSE state
+		if( curthread && curthread->SavedState.SSE )
+		{
+			Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF );
+			curthread->SavedState.bSSEModified = 0;
+			Proc_DisableSSE();
+		}
 
-	if( curthread )
-	{
-		SwitchTasks(
-			nextthread->SavedState.ESP, &curthread->SavedState.ESP,
-			nextthread->SavedState.EIP, &curthread->SavedState.EIP,
-			nextthread->Process->MemState.CR3
-			);
-	}
-	else
-	{
-		SwitchTasks(
-			nextthread->SavedState.ESP, 0,
-			nextthread->SavedState.EIP, 0,
-			nextthread->Process->MemState.CR3
-			);
+		if( curthread )
+		{
+			SwitchTasks(
+				nextthread->SavedState.ESP, &curthread->SavedState.ESP,
+				nextthread->SavedState.EIP, &curthread->SavedState.EIP,
+				nextthread->Process->MemState.CR3
+				);
+		}
+		else
+		{
+			SwitchTasks(
+				nextthread->SavedState.ESP, 0,
+				nextthread->SavedState.EIP, 0,
+				nextthread->Process->MemState.CR3
+				);
+		}
 	}
-
-	return ;
+	
+	SHORTREL(&glThreadListLock);
 }
 
 /**
- * \fn void Proc_Scheduler(int CPU)
- * \brief Swap current thread and clears dead threads
+ * \brief Handle the per-CPU timer ticking
+ 
  */
-void Proc_Scheduler(int CPU)
+void Proc_HandleEventTimer(int CPU)
 {
-	#if USE_MP
-	if( GetCPUNum() )
-		gpMP_LocalAPIC->EOI.Val = 0;
-	else
-	#endif
-		outb(0x20, 0x20);
-	__asm__ __volatile__ ("sti");	
-
 	// Call the timer update code
 	Timer_CallTimers();
 
@@ -843,6 +860,8 @@ void Proc_Scheduler(int CPU)
 	// If two ticks happen within the same task, and it's not an idle task, swap
 	if( gaCPUs[CPU].Current->TID > giNumCPUs && gaCPUs[CPU].Current == gaCPUs[CPU].LastTimerThread )
 	{
+		const tThread* const t = gaCPUs[CPU].Current;
+		LOG("Preempting thread %p(%i %s)", t, t->TID, t->ThreadName);
 		Proc_Reschedule();
 	}
 	
diff --git a/KernelLand/Kernel/arch/x86/time.c b/KernelLand/Kernel/arch/x86/time.c
index 54e59a5d2d9ac520ebaa8dedbddda239304db8ae..b367c50ae2facdd8851ca09e6fb080fa642800ff 100644
--- a/KernelLand/Kernel/arch/x86/time.c
+++ b/KernelLand/Kernel/arch/x86/time.c
@@ -103,6 +103,7 @@ void Time_Interrupt(int IRQ, void *Ptr)
 	if( giTime_TSCAtLastTick )
 	{
 		giTime_TSCPerTick = curTSC - giTime_TSCAtLastTick;
+		//Debug("TSC Frequency is %llu-%llu = %llu Hz", curTSC, giTime_TSCAtLastTick, giTime_TSCPerTick*2);
 	}
 	giTime_TSCAtLastTick = curTSC;
 	
diff --git a/KernelLand/Kernel/arch/x86/vm8086.c b/KernelLand/Kernel/arch/x86/vm8086.c
index 93ea63ae5b6022fdb6feff0a31d489fc6439b61b..f1b50059989a0590d0871a35b296d512f5912be1 100644
--- a/KernelLand/Kernel/arch/x86/vm8086.c
+++ b/KernelLand/Kernel/arch/x86/vm8086.c
@@ -68,16 +68,14 @@ Uint32	gaVM8086_MemBitmap[VM8086_BLOCKCOUNT/32];
 // === FUNCTIONS ===
 int VM8086_Install(char **Arguments)
 {
-	tPID	pid;	
-
 	Semaphore_Init(&gVM8086_TasksToDo, 0, 10, "VM8086", "TasksToDo");
 	
 	// Lock to avoid race conditions
 	Mutex_Acquire( &glVM8086_Process );
 	
 	// Create BIOS Call process
-	pid = Proc_Clone(CLONE_VM);
-	//Log_Debug("VM8086", "pid = %i", pid);
+	tPID pid = Proc_Clone(CLONE_VM);
+	LOG("pid = %i", pid);
 	if(pid == -1)
 	{
 		Log_Error("VM8086", "Unable to clone kernel into VM8086 worker");
@@ -87,15 +85,14 @@ int VM8086_Install(char **Arguments)
 	{
 		Uint	* volatile stacksetup;	// Initialising Stack
 		Uint16	* volatile rmstack;	// Real Mode Stack
-		 int	i;
 
-		//Log_Debug("VM8086", "Initialising worker");	
+		LOG("Initialising worker");
 	
 		// Set Image Name
 		Threads_SetName("VM8086");
 
 		// Map ROM Area
-		for(i=0xA0;i<0x100;i++) {
+		for(unsigned int i = 0xA0;i<0x100;i++) {
 			MM_RefPhys(i * 0x1000);
 			MM_Map( (void*)(i * 0x1000), i * 0x1000 );
 		}
@@ -150,6 +147,7 @@ int VM8086_Install(char **Arguments)
 		stacksetup--;	*stacksetup = 0x20|3;	// ES - Kernel
 		stacksetup--;	*stacksetup = 0x20|3;	// FS
 		stacksetup--;	*stacksetup = 0x20|3;	// GS
+		LOG("stacksetup = %p, entering vm8086");
 		__asm__ __volatile__ (
 		"mov %%eax,%%esp;\n\t"	// Set stack pointer
 		"pop %%gs;\n\t"
@@ -164,6 +162,7 @@ int VM8086_Install(char **Arguments)
 	gVM8086_WorkerPID = pid;
 
 	// It's released when the GPF fires
+	LOG("Waiting for worker %i to start", gVM8086_WorkerPID);
 	Mutex_Acquire( &glVM8086_Process );
 	Mutex_Release( &glVM8086_Process );
 	
diff --git a/KernelLand/Kernel/arch/x86_64/desctab.asm b/KernelLand/Kernel/arch/x86_64/desctab.asm
index e83317353d6c4ed4d2595282e03e43d3ad6d3364..e5e3f24797c126c102755618f931c1efbd9b2806 100644
--- a/KernelLand/Kernel/arch/x86_64/desctab.asm
+++ b/KernelLand/Kernel/arch/x86_64/desctab.asm
@@ -404,7 +404,7 @@ SyscallStub:
 	mov [rsp+0x10], rdi	; Arg1
 	mov [rsp+0x18], rsi	; Arg2
 	mov [rsp+0x20], rdx	; Arg3
-	mov [rsp+0x28], r10	; Arg4
+	mov [rsp+0x28], r10	; Arg4 (r10 used in place of rcx)
 	mov [rsp+0x30], r8	; Arg5
 	mov [rsp+0x38], r9	; Arg6
 
diff --git a/KernelLand/Kernel/arch/x86_64/include/arch.h b/KernelLand/Kernel/arch/x86_64/include/arch.h
index ec7444c7a2143885487a61eaf653ac22cbe5d272..1360b19243c392228740763ca6879c74dac29e4d 100644
--- a/KernelLand/Kernel/arch/x86_64/include/arch.h
+++ b/KernelLand/Kernel/arch/x86_64/include/arch.h
@@ -105,5 +105,7 @@ extern void	SHORTREL(struct sShortSpinlock *Lock);
 extern void	Debug_PutCharDebug(char ch);
 extern void	Debug_PutStringDebug(const char *Str);
 
+extern void	__AtomicTestSetLoop(Uint *Ptr, Uint Value);
+
 #endif
 
diff --git a/KernelLand/Kernel/arch/x86_64/include/mm_virt.h b/KernelLand/Kernel/arch/x86_64/include/mm_virt.h
index 120afb3f024a8705e5c496c931d6c45795cd5b46..b03875fbbf4bd815861a05b57e917fa300d2b6ee 100644
--- a/KernelLand/Kernel/arch/x86_64/include/mm_virt.h
+++ b/KernelLand/Kernel/arch/x86_64/include/mm_virt.h
@@ -48,6 +48,7 @@
 
 #define	MM_USER_MIN 	0x00000000##00010000
 #define USER_LIB_MAX	0x00007000##00000000
+#define MM_USER_MAX	USER_LIB_MAX
 #define USER_STACK_PREALLOC	0x00000000##00002000	// 8 KiB
 #define USER_STACK_SZ	0x00000000##00020000	// 64 KiB
 #define USER_STACK_TOP	0x00008000##00000000
diff --git a/KernelLand/Kernel/arch/x86_64/lib.c b/KernelLand/Kernel/arch/x86_64/lib.c
index b35db69cdc707681b838bfda15eccdf6ad343397..60770f63679495e9b3d991330cd221d309ea7765 100644
--- a/KernelLand/Kernel/arch/x86_64/lib.c
+++ b/KernelLand/Kernel/arch/x86_64/lib.c
@@ -88,6 +88,8 @@ void SHORTLOCK(struct sShortSpinlock *Lock)
 		Lock->Depth ++;
 		return ;
 	}
+	#else
+	ASSERT( !CPU_HAS_LOCK(Lock) );
 	#endif
 	
 	// Wait for another CPU to release
@@ -146,6 +148,18 @@ void SHORTREL(struct sShortSpinlock *Lock)
 	#endif
 }
 
+void __AtomicTestSetLoop(Uint *Ptr, Uint Value)
+{
+	__ASM__(
+		"1:\n\t"
+		"xor %%eax, %%eax;\n\t"
+		"lock cmpxchg %0, (%1);\n\t"	// if( Ptr==0 ) { ZF=1; Ptr=Value } else { ZF=0; _=Ptr }
+		"jnz 1b;\n\t"
+		:: "r"(Value), "r"(Ptr)
+		: "eax" // EAX clobbered
+		);
+}
+
 // === DEBUG IO ===
 #if USE_GDB_STUB
 void initGdbSerial(void)
@@ -358,11 +372,11 @@ void *memset(void *__dest, int __val, size_t __count)
 		__asm__ __volatile__ ("rep stosb" : : "D"(__dest),"a"(__val),"c"(__count));
 	else {
 		Uint8   *dst = __dest;
+		size_t	qwords = __count / 8;
+		size_t	trail_bytes = __count % 8;
 
-		__asm__ __volatile__ ("rep stosq" : : "D"(dst),"a"(0),"c"(__count/8));
-		dst += __count & ~7;
-		__count = __count & 7;
-		while( __count-- )
+		__asm__ __volatile__ ("rep stosq" : "=D"(dst) : "D"(dst),"a"(0),"c"(qwords));
+		while( trail_bytes-- )
 		        *dst++ = 0;
 	}
 	return __dest;
diff --git a/KernelLand/Kernel/arch/x86_64/mm_virt.c b/KernelLand/Kernel/arch/x86_64/mm_virt.c
index 4e2968fd80e70e99e9a3beb5b7a48b79fbd53a4f..23f04ac2c5b2e73aa9f6fc19232f4e9e532307ec 100644
--- a/KernelLand/Kernel/arch/x86_64/mm_virt.c
+++ b/KernelLand/Kernel/arch/x86_64/mm_virt.c
@@ -53,6 +53,17 @@
 #define INVLPG_ALL()	__asm__ __volatile__ ("mov %cr3,%rax;\n\tmov %rax,%cr3;")
 #define INVLPG_GLOBAL()	__asm__ __volatile__ ("mov %cr4,%rax;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4")
 
+// TODO: INVLPG_ALL is expensive
+#define GET_TEMP_MAPPING(cr3) do { \
+	__ASM__("cli"); \
+	__AtomicTestSetLoop( (Uint *)&TMPCR3(), (cr3) | 3 ); \
+	INVLPG_ALL(); \
+} while(0)
+#define REL_TEMP_MAPPING() do { \
+	TMPCR3() = 0; \
+	__ASM__("sti"); \
+} while(0)
+
 // === CONSTS ===
 //tPAddr	* const gaPageTable = MM_FRACTAL_BASE;
 
@@ -70,6 +81,7 @@ void	MM_int_ClonePageEnt( Uint64 *Ent, void *NextLevel, tVAddr Addr, int bTable
 void	MM_int_DumpTablesEnt(tVAddr RangeStart, size_t Length, tPAddr Expected);
 //void	MM_DumpTables(tVAddr Start, tVAddr End);
  int	MM_GetPageEntryPtr(tVAddr Addr, BOOL bTemp, BOOL bAllocate, BOOL bLargePage, tPAddr **Pointer);
+tPAddr	MM_GetPageFromAS(tProcess *Process, volatile const void *Addr);
  int	MM_MapEx(volatile void *VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge);
 // int	MM_Map(tVAddr VAddr, tPAddr PAddr);
 void	MM_Unmap(tVAddr VAddr);
@@ -78,7 +90,6 @@ void	MM_int_ClearTableLevel(tVAddr VAddr, int LevelBits, int MaxEnts);
  int	MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags);
 
 // === GLOBALS ===
-tMutex	glMM_TempFractalLock;
 tShortSpinlock	glMM_ZeroPage;
 tPAddr	gMM_ZeroPage;
 
@@ -260,7 +271,7 @@ int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
 	// Print Stack Backtrace
 	Error_Backtrace(Regs->RIP, Regs->RBP);
 	
-	MM_DumpTables(0, -1);
+	//MM_DumpTables(0, -1);
 
 	return 1;	
 }
@@ -301,8 +312,6 @@ void MM_DumpTables(tVAddr Start, tVAddr End)
 	const tPAddr	MASK = ~CHANGEABLE_BITS;	// Physical address and access bits
 	tVAddr	rangeStart = 0;
 	tPAddr	expected = CHANGEABLE_BITS;	// CHANGEABLE_BITS is used because it's not a vaild value
-	tVAddr	curPos;
-	Uint	page;
 	tPAddr	expected_pml4 = PF_WRITE|PF_USER;	
 	tPAddr	expected_pdp = PF_WRITE|PF_USER;	
 	tPAddr	expected_pd = PF_WRITE|PF_USER;	
@@ -311,11 +320,12 @@ void MM_DumpTables(tVAddr Start, tVAddr End)
 	
 	End &= (1L << 48) - 1;
 	
-	Start >>= 12;	End >>= 12;
+	Start >>= 12;
+	End >>= 12;
 	
-	for(page = Start, curPos = Start<<12;
-		page < End;
-		curPos += 0x1000, page++)
+	// `page` will not overflow, End is 48-12 bits
+	tVAddr	curPos = Start << 12;
+	for(Uint page = Start; page <= End; curPos += 0x1000, page++)
 	{
 		//Debug("&PAGEMAPLVL4(%i page>>27) = %p", page>>27, &PAGEMAPLVL4(page>>27));
 		//Debug("&PAGEDIRPTR(%i page>>18) = %p", page>>18, &PAGEDIRPTR(page>>18));
@@ -486,7 +496,7 @@ int MM_MapEx(volatile void *VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge)
 	
 	*ent = PAddr | 3;
 
-	if( (tVAddr)VAddr < USER_MAX )
+	if( (tVAddr)VAddr <= USER_MAX )
 		*ent |= PF_USER;
 	INVLPG( VAddr );
 
@@ -626,6 +636,27 @@ tPAddr MM_GetPhysAddr(volatile const void *Ptr)
 	return (*ptr & PADDR_MASK) | (Addr & 0xFFF);
 }
 
+/**
+ * \brief Get the address of a page from another addres space
+ * \return Refenced physical address (or 0 on error)
+ */
+tPAddr MM_GetPageFromAS(tProcess *Process, volatile const void *Addr)
+{
+	GET_TEMP_MAPPING(Process->MemState.CR3);
+	tPAddr	ret = 0;
+	tPAddr *ptr;
+	if(MM_GetPageEntryPtr((tVAddr)Addr, 1,0,0, &ptr) == 0)	// Temp, NoAlloc, NotLarge
+	{
+		if( *ptr & 1 )
+		{
+			ret = (*ptr & ~0xFFF) | ((tVAddr)Addr & 0xFFF);
+			MM_RefPhys( ret );
+		}
+	}
+	REL_TEMP_MAPPING();
+	return ret;
+}
+
 /**
  * \brief Sets the flags on a page
  */
@@ -862,15 +893,16 @@ void *MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
 	void	*ret;
 	
 	// Sanity Check
-	if(MaxBits < 12 || !PhysAddr)	return 0;
+	ASSERTCR(MaxBits, >=, 12, NULL);
 	
 	// Fast Allocate
 	if(Pages == 1 && MaxBits >= PHYS_BITS)
 	{
 		phys = MM_AllocPhys();
-		*PhysAddr = phys;
 		ret = MM_MapHWPages(phys, 1);
 		MM_DerefPhys(phys);
+		if(PhysAddr)
+			*PhysAddr = phys;
 		return ret;
 	}
 	
@@ -881,7 +913,8 @@ void *MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
 	
 	// Allocated successfully, now map
 	ret = MM_MapHWPages(phys, Pages);
-	*PhysAddr = phys;
+	if(PhysAddr)
+		*PhysAddr = phys;
 	// MapHWPages references the pages, so deref them back down to 1
 	for(;Pages--;phys+=0x1000)
 		MM_DerefPhys(phys);
@@ -898,9 +931,8 @@ void *MM_MapTemp(tPAddr PAddr)
 {
 	const int max_slots = (MM_TMPMAP_END - MM_TMPMAP_BASE) / PAGE_SIZE;
 	tVAddr	ret = MM_TMPMAP_BASE;
-	 int	i;
 	
-	for( i = 0; i < max_slots; i ++, ret += PAGE_SIZE )
+	for( int i = 0; i < max_slots; i ++, ret += PAGE_SIZE )
 	{
 		tPAddr	*ent;
 		if( MM_GetPageEntryPtr( ret, 0, 1, 0, &ent) < 0 ) {
@@ -918,6 +950,15 @@ void *MM_MapTemp(tPAddr PAddr)
 	return 0;
 }
 
+void *MM_MapTempFromProc(tProcess *Process, const void *VAddr)
+{
+	// Get paddr
+	tPAddr	paddr = MM_GetPageFromAS(Process, VAddr);
+	if( paddr == 0 )
+		return NULL;
+	return MM_MapTemp(paddr);
+}
+
 void MM_FreeTemp(void *Ptr)
 {
 	MM_Deallocate(Ptr);
@@ -935,9 +976,7 @@ tPAddr MM_Clone(int bNoUserCopy)
 	if(!ret)	return 0;
 	
 	// #2 Alter the fractal pointer
-	Mutex_Acquire(&glMM_TempFractalLock);
-	TMPCR3() = ret | 3;
-	INVLPG_ALL();
+	GET_TEMP_MAPPING(ret);
 	
 	// #3 Set Copy-On-Write to all user pages
 	if( Threads_GetPID() != 0 && !bNoUserCopy )
@@ -1015,9 +1054,7 @@ tPAddr MM_Clone(int bNoUserCopy)
 //	MAGIC_BREAK();
 
 	// #7 Return
-	TMPCR3() = 0;
-	INVLPG_ALL();
-	Mutex_Release(&glMM_TempFractalLock);
+	REL_TEMP_MAPPING();
 //	Log("MM_Clone: RETURN %P", ret);
 	return ret;
 }
@@ -1061,9 +1098,7 @@ tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize)
 	 int	i;
 	
 	// #1 Set temp fractal to PID0
-	Mutex_Acquire(&glMM_TempFractalLock);
-	TMPCR3() = ((tPAddr)gInitialPML4 - KERNEL_BASE) | 3;
-	INVLPG_ALL();
+	GET_TEMP_MAPPING( ((tPAddr)gInitialPML4 - KERNEL_BASE) );
 	
 	// #2 Scan for a free stack addresss < 2^47
 	for(ret = 0x100000; ret < (1ULL << 47); ret += KERNEL_STACK_SIZE)
@@ -1073,7 +1108,7 @@ tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize)
 		if( !(*ptr & 1) )	break;
 	}
 	if( ret >= (1ULL << 47) ) {
-		Mutex_Release(&glMM_TempFractalLock);
+		REL_TEMP_MAPPING();
 		return 0;
 	}
 	
@@ -1105,8 +1140,7 @@ tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize)
 		MM_FreeTemp(tmp_addr);
 	}
 
-	TMPCR3() = 0;
-	Mutex_Release(&glMM_TempFractalLock);
+	REL_TEMP_MAPPING();
 	
 	return ret + i*0x1000;
 }
diff --git a/KernelLand/Kernel/arch/x86_64/proc.asm b/KernelLand/Kernel/arch/x86_64/proc.asm
index aff670aca173c7ac61d6d37b935c26784c6849bd..afde3544dc9c821848cb51ea9e68332f9c926dc3 100644
--- a/KernelLand/Kernel/arch/x86_64/proc.asm
+++ b/KernelLand/Kernel/arch/x86_64/proc.asm
@@ -6,6 +6,8 @@
 [section .text]
 
 [extern Threads_Exit]
+[extern glThreadListLock]
+[extern SHORTREL]
 
 [global GetRIP]
 GetRIP:
@@ -18,6 +20,9 @@ NewTaskHeader:
 	; [rsp+0x08]: Function
 	; [rsp+0x10]: Argument
 
+	mov rdi, glThreadListLock
+	call SHORTREL
+	
 	mov rdi, [rsp+0x10]
 	mov rax, [rsp+0x8]
 	add rsp, 0x10	; Reclaim stack space (thread/fcn)
diff --git a/KernelLand/Kernel/arch/x86_64/proc.c b/KernelLand/Kernel/arch/x86_64/proc.c
index 50b5054894d8987eab5d9f46bbba4143b5d1ced6..7c73bf9f6ac6ad274d8d2d9ef2acd1409d6d18e7 100644
--- a/KernelLand/Kernel/arch/x86_64/proc.c
+++ b/KernelLand/Kernel/arch/x86_64/proc.c
@@ -508,7 +508,10 @@ tTID Proc_Clone(Uint Flags)
 	
 	// Save core machine state
 	rip = Proc_CloneInt(&newThread->SavedState.RSP, &newThread->Process->MemState.CR3, !!(Flags & CLONE_NOUSER));
-	if(rip == 0)	return 0;	// Child
+	if(rip == 0) {
+		SHORTREL(&glThreadListLock);
+		return 0;	// Child
+	}
 	newThread->KernelStack = cur->KernelStack;
 	newThread->SavedState.RIP = rip;
 	newThread->SavedState.SSE = NULL;
@@ -735,62 +738,61 @@ void Proc_DumpThreadCPUState(tThread *Thread)
 
 void Proc_Reschedule(void)
 {
-	tThread	*nextthread, *curthread;
 	 int	cpu = GetCPUNum();
 
-	// TODO: Wait for it?
-	if(IS_LOCKED(&glThreadListLock))        return;
-	
-	curthread = gaCPUs[cpu].Current;
-
-	nextthread = Threads_GetNextToRun(cpu, curthread);
+	if(CPU_HAS_LOCK(&glThreadListLock))
+		return;
+	SHORTLOCK(&glThreadListLock);
 
-	if(nextthread == curthread)	return ;
+	tThread	*curthread = gaCPUs[cpu].Current;
+	tThread	*nextthread = Threads_GetNextToRun(cpu, curthread);
+	
 	if(!nextthread)
 		nextthread = gaCPUs[cpu].IdleThread;
-	if(!nextthread)
-		return ;
-
-	#if DEBUG_TRACE_SWITCH
-	LogF("\nSwitching to task CR3 = 0x%x, RIP = %p, RSP = %p - %i (%s)\n",
-		nextthread->Process->MemState.CR3,
-		nextthread->SavedState.RIP,
-		nextthread->SavedState.RSP,
-		nextthread->TID,
-		nextthread->ThreadName
-		);
-	#endif
-
-	// Update CPU state
-	gaCPUs[cpu].Current = nextthread;
-	gTSSs[cpu].RSP0 = nextthread->KernelStack-sizeof(void*);
-	__asm__ __volatile__ ("mov %0, %%db0" : : "r" (nextthread));
 
-	if( curthread )
+	if(nextthread && nextthread != curthread)
 	{
-		// Save FPU/MMX/XMM/SSE state
-		if( curthread->SavedState.SSE )
+		#if DEBUG_TRACE_SWITCH
+		LogF("\nSwitching to task CR3 = 0x%x, RIP = %p, RSP = %p - %i (%s)\n",
+			nextthread->Process->MemState.CR3,
+			nextthread->SavedState.RIP,
+			nextthread->SavedState.RSP,
+			nextthread->TID,
+			nextthread->ThreadName
+			);
+		#endif
+
+		// Update CPU state
+		gaCPUs[cpu].Current = nextthread;
+		gTSSs[cpu].RSP0 = nextthread->KernelStack-sizeof(void*);
+		__asm__ __volatile__ ("mov %0, %%db0" : : "r" (nextthread));
+
+		if( curthread )
 		{
-			Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF );
-			curthread->SavedState.bSSEModified = 0;
-			Proc_DisableSSE();
+			// Save FPU/MMX/XMM/SSE state
+			if( curthread->SavedState.SSE )
+			{
+				Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF );
+				curthread->SavedState.bSSEModified = 0;
+				Proc_DisableSSE();
+			}
+			SwitchTasks(
+				nextthread->SavedState.RSP, &curthread->SavedState.RSP,
+				nextthread->SavedState.RIP, &curthread->SavedState.RIP,
+				nextthread->Process->MemState.CR3
+				);
+		}
+		else
+		{
+			Uint	tmp;
+			SwitchTasks(
+				nextthread->SavedState.RSP, &tmp,
+				nextthread->SavedState.RIP, &tmp,
+				nextthread->Process->MemState.CR3
+				);
 		}
-		SwitchTasks(
-			nextthread->SavedState.RSP, &curthread->SavedState.RSP,
-			nextthread->SavedState.RIP, &curthread->SavedState.RIP,
-			nextthread->Process->MemState.CR3
-			);
-	}
-	else
-	{
-		Uint	tmp;
-		SwitchTasks(
-			nextthread->SavedState.RSP, &tmp,
-			nextthread->SavedState.RIP, &tmp,
-			nextthread->Process->MemState.CR3
-			);
 	}
-	return ;
+	SHORTREL(&glThreadListLock);
 }
 
 /**
diff --git a/KernelLand/Kernel/bin/elf.c b/KernelLand/Kernel/bin/elf.c
index 7f65d89d1df6bf37771a7c674855b3a39c071d4e..37172cf920c198b4eb626eac3930f8178dfd2155 100644
--- a/KernelLand/Kernel/bin/elf.c
+++ b/KernelLand/Kernel/bin/elf.c
@@ -15,8 +15,8 @@
 #if BITS <= 32
 # define DISABLE_ELF64
 #endif
-static int	GetSymbol(const char *Name, void **Value, size_t *Size);
-static int	GetSymbol(const char *Name, void **Value, size_t *Size) {
+static int	GetSymbol(const char *Name, void **Value, size_t *Size, void *IgnoreBase);
+static int	GetSymbol(const char *Name, void **Value, size_t *Size, void *IgnoreBase) {
 	Uint val;
 	if(!Binary_GetSymbol(Name, &val)) {
 		Log_Notice("ELF", "Lookup of '%s' failed", Name);
@@ -205,15 +205,14 @@ tBinary *Elf_Load32(int FD, Elf32_Ehdr *Header)
 {
 	tBinary	*ret;
 	Elf32_Phdr	*phtab;
-	 int	i, j;
-	 int	iLoadCount;
+	 int	j;
 
 	ENTER("xFD", FD);
 
 	// Check architecture with current CPU
 	// - TODO: Support kernel level emulation
 	#if ARCH_IS_x86
-	if( Header->machine != EM_386 )
+	if( Header->e_machine != EM_386 )
 	{
 		Log_Warning("ELF", "Unknown architecure on ELF-32");
 		LEAVE_RET('n');
@@ -222,7 +221,7 @@ tBinary *Elf_Load32(int FD, Elf32_Ehdr *Header)
 	#endif
 
 	// Check for a program header
-	if(Header->phoff == 0) {
+	if(Header->e_phoff == 0) {
 		#if DEBUG_WARN
 		Log_Warning("ELF", "File does not contain a program header (phoff == 0)");
 		#endif
@@ -231,25 +230,25 @@ tBinary *Elf_Load32(int FD, Elf32_Ehdr *Header)
 	}
 	
 	// Read Program Header Table
-	phtab = malloc( sizeof(Elf32_Phdr) * Header->phentcount );
+	phtab = malloc( sizeof(Elf32_Phdr) * Header->e_phnum );
 	if( !phtab ) {
 		LEAVE('n');
 		return NULL;
 	}
-	LOG("hdr.phoff = 0x%08x", Header->phoff);
-	VFS_Seek(FD, Header->phoff, SEEK_SET);
-	VFS_Read(FD, sizeof(Elf32_Phdr)*Header->phentcount, phtab);
+	LOG("hdr.phoff = 0x%08x", Header->e_phoff);
+	VFS_Seek(FD, Header->e_phoff, SEEK_SET);
+	VFS_Read(FD, sizeof(Elf32_Phdr)*Header->e_phnum, phtab);
 	
 	// Count Pages
-	iLoadCount = 0;
-	LOG("Header->phentcount = %i", Header->phentcount);
-	for( i = 0; i < Header->phentcount; i++ )
+	unsigned iLoadCount = 0;
+	LOG("Header->phentcount = %i", Header->e_phnum);
+	for( int i = 0; i < Header->e_phnum; i++ )
 	{
 		// Ignore Non-LOAD types
-		if(phtab[i].Type != PT_LOAD)
+		if(phtab[i].p_type != PT_LOAD)
 			continue;
 		iLoadCount ++;
-		LOG("phtab[%i] = {VAddr:0x%x, MemSize:0x%x}", i, phtab[i].VAddr, phtab[i].MemSize);
+		LOG("phtab[%i] = {VAddr:0x%x, MemSize:0x%x}", i, phtab[i].p_vaddr, phtab[i].p_memsz);
 	}
 	
 	LOG("iLoadCount = %i", iLoadCount);
@@ -257,56 +256,55 @@ tBinary *Elf_Load32(int FD, Elf32_Ehdr *Header)
 	// Allocate Information Structure
 	ret = malloc( sizeof(tBinary) + sizeof(tBinarySection)*iLoadCount );
 	// Fill Info Struct
-	ret->Entry = Header->entrypoint;
+	ret->Entry = Header->e_entry;
 	ret->Base = -1;		// Set Base to maximum value
 	ret->NumSections = iLoadCount;
 	ret->Interpreter = NULL;
 	
 	// Load Pages
 	j = 0;
-	for( i = 0; i < Header->phentcount; i++ )
+	for( int i = 0; i < Header->e_phnum; i++ )
 	{
 		//LOG("phtab[%i].Type = 0x%x", i, phtab[i].Type);
 		LOG("phtab[%i] = {", i);
-		LOG(" .Type = 0x%08x", phtab[i].Type);
-		LOG(" .Offset = 0x%08x", phtab[i].Offset);
-		LOG(" .VAddr = 0x%08x", phtab[i].VAddr);
-		LOG(" .PAddr = 0x%08x", phtab[i].PAddr);
-		LOG(" .FileSize = 0x%08x", phtab[i].FileSize);
-		LOG(" .MemSize = 0x%08x", phtab[i].MemSize);
-		LOG(" .Flags = 0x%08x", phtab[i].Flags);
-		LOG(" .Align = 0x%08x", phtab[i].Align);
+		LOG(" .Type = 0x%08x",   phtab[i].p_type);
+		LOG(" .Offset = 0x%08x", phtab[i].p_offset);
+		LOG(" .VAddr = 0x%08x",  phtab[i].p_vaddr);
+		LOG(" .PAddr = 0x%08x",  phtab[i].p_paddr);
+		LOG(" .FileSize = 0x%08x", phtab[i].p_filesz);
+		LOG(" .MemSize = 0x%08x", phtab[i].p_memsz);
+		LOG(" .Flags = 0x%08x",  phtab[i].p_flags);
+		LOG(" .Align = 0x%08x",  phtab[i].p_align);
 		LOG(" }");
 		// Get Interpreter Name
-		if( phtab[i].Type == PT_INTERP )
+		if( phtab[i].p_type == PT_INTERP )
 		{
-			char *tmp;
 			if(ret->Interpreter)	continue;
-			tmp = malloc(phtab[i].FileSize);
-			VFS_Seek(FD, phtab[i].Offset, 1);
-			VFS_Read(FD, phtab[i].FileSize, tmp);
+			char* tmp = malloc(phtab[i].p_filesz);
+			VFS_Seek(FD, phtab[i].p_offset, 1);
+			VFS_Read(FD, phtab[i].p_filesz, tmp);
 			ret->Interpreter = Binary_RegInterp(tmp);
 			LOG("Interpreter '%s'", tmp);
 			free(tmp);
 			continue;
 		}
 		// Ignore non-LOAD types
-		if(phtab[i].Type != PT_LOAD)	continue;
+		if(phtab[i].p_type != PT_LOAD)	continue;
 		
 		// Find Base
-		if(phtab[i].VAddr < ret->Base)	ret->Base = phtab[i].VAddr;
+		if(phtab[i].p_vaddr < ret->Base)	ret->Base = phtab[i].p_vaddr;
 		
 		LOG("phtab[%i] = {VAddr:0x%x,Offset:0x%x,FileSize:0x%x}",
-			i, phtab[i].VAddr, phtab[i].Offset, phtab[i].FileSize);
+			i, phtab[i].p_vaddr, phtab[i].p_offset, phtab[i].p_filesz);
 		
-		ret->LoadSections[j].Offset = phtab[i].Offset;
-		ret->LoadSections[j].FileSize = phtab[i].FileSize;
-		ret->LoadSections[j].Virtual = phtab[i].VAddr;
-		ret->LoadSections[j].MemSize = phtab[i].MemSize;
+		ret->LoadSections[j].Offset   = phtab[i].p_offset;
+		ret->LoadSections[j].FileSize = phtab[i].p_filesz;
+		ret->LoadSections[j].Virtual  = phtab[i].p_vaddr;
+		ret->LoadSections[j].MemSize  = phtab[i].p_memsz;
 		ret->LoadSections[j].Flags = 0;
-		if( !(phtab[i].Flags & PF_W) )
+		if( !(phtab[i].p_flags & PF_W) )
 			ret->LoadSections[j].Flags |= BIN_SECTFLAG_RO;
-		if( phtab[i].Flags & PF_X )
+		if( phtab[i].p_flags & PF_X )
 			ret->LoadSections[j].Flags |= BIN_SECTFLAG_EXEC;
 		j ++;
 	}
diff --git a/KernelLand/Kernel/binary.c b/KernelLand/Kernel/binary.c
index 3dab6a9210ca8fc9ef26f7c41710dff3cebc9c49..5170dac06ccda9de159b0f49ac54f3795d31781c 100644
--- a/KernelLand/Kernel/binary.c
+++ b/KernelLand/Kernel/binary.c
@@ -231,23 +231,19 @@ int Proc_int_Execve(const char *File, const char **ArgV, const char **EnvP, int
 	}
 
 	// --- Get argc	
-	for( argc = 0; ArgV && ArgV[argc]; argc ++ );
+	for( argc = 0; ArgV && ArgV[argc]; argc ++ )
+		;
 	
 	// --- Set Process Name
 	Threads_SetName(File);
 	
 	// --- Clear User Address space
-	// NOTE: This is a little roundabout, maybe telling ClearUser to not touch the
-	//       PPD area would be a better idea.
 	if( bClearUser )
 	{
-		 int	nfd = *Threads_GetMaxFD();
-		void	*handles;
-		handles = VFS_SaveHandles(nfd, NULL);
-		VFS_CloseAllUserHandles();
+		// MM_ClearUser should preserve handles
 		MM_ClearUser();
-		VFS_RestoreHandles(nfd, handles);
-		VFS_FreeSavedHandles(nfd, handles);
+		// - NOTE: Not a reliable test, but helps for now
+		ASSERTC( VFS_IOCtl(0, 0, NULL), !=, -1 );
 	}
 	
 	// --- Load new binary
@@ -334,6 +330,8 @@ tVAddr Binary_Load(const char *Path, tVAddr *EntryPoint)
 	if(pBinary->Interpreter) {
 		tVAddr	start;
 		if( Binary_Load(pBinary->Interpreter, &start) == 0 ) {
+			Log_Error("Binary", "Can't load interpeter '%s' for '%s'",
+				pBinary->Interpreter, Path);
 			LEAVE('x', 0);
 			return 0;
 		}
diff --git a/KernelLand/Kernel/debug.c b/KernelLand/Kernel/debug.c
index d0de25abc389255c4ab9378bd57321d4d57b6a4f..eed38de5f6161a4250607df8ecfbdd6faca3cfcc 100644
--- a/KernelLand/Kernel/debug.c
+++ b/KernelLand/Kernel/debug.c
@@ -7,7 +7,7 @@
 #include <debug_hooks.h>
 
 #define	DEBUG_MAX_LINE_LEN	256
-#define	LOCK_DEBUG_OUTPUT	1	// Avoid interleaving of output lines?
+#define	LOCK_DEBUG_OUTPUT	0	// Avoid interleaving of output lines?
 #define TRACE_TO_KTERM  	0	// Send ENTER/DEBUG/LEAVE to debug?
 
 // === IMPORTS ===
@@ -21,7 +21,7 @@ static void	Debug_Putchar(char ch);
 static void	Debug_Puts(int bUseKTerm, const char *Str);
 void	Debug_DbgOnlyFmt(const char *format, va_list args);
 void	Debug_FmtS(int bUseKTerm, const char *format, ...);
-void	Debug_Fmt(int bUseKTerm, const char *format, va_list args);
+bool	Debug_Fmt(int bUseKTerm, const char *format, va_list args);
 void	Debug_SetKTerminal(const char *File);
 
 // === GLOBALS ===
@@ -77,9 +77,8 @@ static void Debug_Puts(int UseKTerm, const char *Str)
 		IPStack_SendDebugText(Str);
 
 	// Output to the kernel terminal
-	if( UseKTerm && gbDebug_IsKPanic < 2 && giDebug_KTerm != -1)
+	if( UseKTerm && gbDebug_IsKPanic < 2 && giDebug_KTerm != -1 && gbInPutChar == 0)
 	{
-		if(gbInPutChar)	return ;
 		gbInPutChar = 1;
 		VFS_Write(giDebug_KTerm, len, Str);
 		gbInPutChar = 0;
@@ -91,17 +90,18 @@ void Debug_DbgOnlyFmt(const char *format, va_list args)
 	Debug_Fmt(0, format, args);
 }
 
-void Debug_Fmt(int bUseKTerm, const char *format, va_list args)
+bool Debug_Fmt(int bUseKTerm, const char *format, va_list args)
 {
 	char	buf[DEBUG_MAX_LINE_LEN];
 	buf[DEBUG_MAX_LINE_LEN-1] = 0;
-	int len = vsnprintf(buf, DEBUG_MAX_LINE_LEN-1, format, args);
+	size_t len = vsnprintf(buf, DEBUG_MAX_LINE_LEN-1, format, args);
 	Debug_Puts(bUseKTerm, buf);
 	if( len > DEBUG_MAX_LINE_LEN-1 ) {
 		// do something
 		Debug_Puts(bUseKTerm, "[...]");
+		return false;
 	}
-	return ;
+	return true;
 }
 
 void Debug_FmtS(int bUseKTerm, const char *format, ...)
@@ -131,25 +131,27 @@ void Debug_KernelPanic(void)
 /**
  * \fn void LogF(const char *Msg, ...)
  * \brief Raw debug log (no new line, no prefix)
+ * \return True if all of the provided text was printed
  */
-void LogF(const char *Fmt, ...)
+bool LogF(const char *Fmt, ...)
 {
-	va_list	args;
-
 	#if LOCK_DEBUG_OUTPUT
-	if(CPU_HAS_LOCK(&glDebug_Lock))	return ;
+	if(CPU_HAS_LOCK(&glDebug_Lock)) {
+		Debug_Puts("[#]");
+		return true;
+	}
 	SHORTLOCK(&glDebug_Lock);
 	#endif
 	
+	va_list	args;
 	va_start(args, Fmt);
-
-	Debug_Fmt(1, Fmt, args);
-
+	bool rv = Debug_Fmt(1, Fmt, args);
 	va_end(args);
 	
 	#if LOCK_DEBUG_OUTPUT
 	SHORTREL(&glDebug_Lock);
 	#endif
+	return rv;
 }
 /**
  * \fn void Debug(const char *Msg, ...)
@@ -249,12 +251,12 @@ void Panic(const char *Fmt, ...)
 	
 	Debug_KernelPanic();
 	
+	Debug_Puts(1, "\x1b[31m");
 	Debug_Puts(1, "Panic: ");
 	va_start(args, Fmt);
 	Debug_Fmt(1, Fmt, args);
 	va_end(args);
-	Debug_Putchar('\r');
-	Debug_Putchar('\n');
+	Debug_Puts(1, "\x1b[0m\r\n");
 
 	Proc_PrintBacktrace();
 	//Threads_Dump();
@@ -265,16 +267,13 @@ void Panic(const char *Fmt, ...)
 
 void Debug_SetKTerminal(const char *File)
 {
-	 int	tmp;
 	if(giDebug_KTerm != -1) {
-		tmp = giDebug_KTerm;
+		// Clear FD to -1 before closing (prevents writes to closed FD)
+		int oldfd = giDebug_KTerm;
 		giDebug_KTerm = -1;
-		VFS_Close(tmp);
+		VFS_Close(oldfd);
 	}
-	tmp = VFS_Open(File, VFS_OPENFLAG_WRITE);
-//	Log_Log("Debug", "Opened '%s' as 0x%x", File, tmp);
-	giDebug_KTerm = tmp;
-//	Log_Log("Debug", "Returning to %p", __builtin_return_address(0));
+	giDebug_KTerm = VFS_Open(File, VFS_OPENFLAG_WRITE);
 }
 
 void Debug_Enter(const char *FuncName, const char *ArgTypes, ...)
@@ -431,16 +430,16 @@ void Debug_HexDump(const char *Header, const void *Data, size_t Length)
 	Uint	pos = 0;
 	LogF("%014lli ", now());
 	Debug_Puts(1, Header);
-	LogF(" (Hexdump of %p)\r\n", Data);
+	LogF(" (Hexdump of %p+%i)\r\n", Data, Length);
 
 	#define	CH(n)	((' '<=cdat[(n)]&&cdat[(n)]<0x7F) ? cdat[(n)] : '.')
 
 	while(Length >= 16)
 	{
 		LogF("%014lli Log: %04x:"
-			" %02x %02x %02x %02x %02x %02x %02x %02x"
-			" %02x %02x %02x %02x %02x %02x %02x %02x"
-			"  %c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c\r\n",
+			" %02x %02x %02x %02x %02x %02x %02x %02x "
+			" %02x %02x %02x %02x %02x %02x %02x %02x "
+			" %c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c\r\n",
 			now(),
 			pos,
 			cdat[ 0], cdat[ 1], cdat[ 2], cdat[ 3], cdat[ 4], cdat[ 5], cdat[ 6], cdat[ 7],
diff --git a/KernelLand/Kernel/drv/dgram_pipe.c b/KernelLand/Kernel/drv/dgram_pipe.c
index c13ca2a71cb41cf1a472cdfcdd0bbeb7bf4ac0c8..667190eb0ad8b3cdc3f88e7f18866d46883f2f10 100644
--- a/KernelLand/Kernel/drv/dgram_pipe.c
+++ b/KernelLand/Kernel/drv/dgram_pipe.c
@@ -34,6 +34,7 @@ struct sIPCPipe_Endpoint
 	tMutex	lList;
 	tIPCPipe_Packet	*OutHead;
 	tIPCPipe_Packet	*OutTail;
+	size_t	ByteCount;
 	tVFS_Node	Node;
 };
 struct sIPCPipe_Channel
@@ -50,8 +51,8 @@ struct sIPCPipe_Server
 	tIPCPipe_Server	*Prev;
 	char	*Name;
 	size_t	MaxBlockSize;	// Max size of a 'packet'
-	size_t	QueueByteLimit;	// Maximum number of bytes held in kernel for this server
-	size_t	CurrentByteCount;
+	// NOTE: Not strictly enforced, can go MaxBlockSize-1 over
+	size_t	QueueByteLimit;	// Maximum number of bytes held in kernel for each endpoint
 	tVFS_Node	ServerNode;
 	tRWLock	lChannelList;
 	tIPCPipe_Channel	*FirstClient;
@@ -85,6 +86,7 @@ tVFS_NodeType	gIPCPipe_ServerNodeType = {
 };
 tVFS_NodeType	gIPCPipe_ChannelNodeType = {
 	.TypeName = "IPC Pipe - Channel",
+	.Flags = VFS_NODETYPEFLAG_STREAM,
 	.Read = IPCPipe_Client_Read,
 	.Write = IPCPipe_Client_Write,
 	.Close = IPCPipe_Client_Close
@@ -210,8 +212,10 @@ tVFS_Node *IPCPipe_Root_FindDir(tVFS_Node *Node, const char *Name, Uint Flags)
 	new_client->Server = srv;
 	new_client->ClientEP.Node.Type = &gIPCPipe_ChannelNodeType;
 	new_client->ClientEP.Node.ImplPtr = new_client;
+	new_client->ClientEP.Node.Size = -1;
 	new_client->ServerEP.Node.Type = &gIPCPipe_ChannelNodeType;
 	new_client->ServerEP.Node.ImplPtr = new_client;
+	new_client->ServerEP.Node.Size = -1;
 
 	// Append to server list
 	RWLock_AcquireWrite(&srv->lChannelList);
@@ -324,13 +328,13 @@ size_t IPCPipe_Client_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *D
 	
 	// Wait for a packet to be ready
 	tTime	timeout_z = 0, *timeout = ((Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL);
-	int rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "IPCPipe Endpoint");
+	int rv = VFS_SelectNode(Node, VFS_SELECT_READ|VFS_SELECT_ERROR, timeout, "IPCPipe Endpoint");
 	if( !rv ) {
 		errno = (Flags & VFS_IOFLAG_NOBLOCK) ? EWOULDBLOCK : EINTR;
 		LEAVE('i', -1);
 		return -1;
 	}
-	if( channel->Server == NULL ) {
+	if( (rv & VFS_SELECT_ERROR) || channel->Server == NULL ) {
 		//errno = EIO;
 		LEAVE('i', -1);
 		return -1;
@@ -351,6 +355,7 @@ size_t IPCPipe_Client_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *D
 	if(!rep->OutHead)
 		rep->OutTail = NULL;
 	VFS_MarkAvaliable(Node, !!rep->OutHead);
+	VFS_MarkFull(&rep->Node, 0);	//	Just read a packet, remote shouldn't be full
 	Mutex_Release(&rep->lList);
 
 	// Return
@@ -363,7 +368,7 @@ size_t IPCPipe_Client_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *D
 	}
 	else
 	{
-		Log_Warning("IPCPipe", "No packet ready but semaphore returned");
+		Log_Warning("IPCPipe", "No packet ready but select returned");
 	}
 	
 	LEAVE('i', ret);
@@ -387,8 +392,21 @@ size_t IPCPipe_Client_Write(tVFS_Node *Node, off_t Offset, size_t Length, const
 		LEAVE('i', 0);
 		return 0;
 	}
-
-	// TODO: Ensure that no more than DEF_MAX_BYTE_LIMIT bytes are in flight at one time
+	
+	// Wait for a packet to be ready
+	tTime	timeout_z = 0, *timeout = ((Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL);
+	int rv = VFS_SelectNode(Node, VFS_SELECT_WRITE|VFS_SELECT_ERROR, timeout, "IPCPipe Endpoint");
+	ASSERTC(rv, >=, 0);
+	if( !rv ) {
+		errno = (Flags & VFS_IOFLAG_NOBLOCK) ? EWOULDBLOCK : EINTR;
+		LEAVE('i', -1);
+		return -1;
+	}
+	if( (rv & VFS_SELECT_ERROR) ||  channel->Server == NULL ) {
+		//errno = EIO;
+		LEAVE('i', -1);
+		return -1;
+	}
 
 	// Create packet structure
 	tIPCPipe_Packet	*pkt = malloc(sizeof(tIPCPipe_Packet)+Length);
@@ -413,6 +431,12 @@ size_t IPCPipe_Client_Write(tVFS_Node *Node, off_t Offset, size_t Length, const
 	else
 		lep->OutHead = pkt;
 	lep->OutTail = pkt;
+	
+	lep->ByteCount += Length;
+	if( lep->ByteCount >= channel->Server->QueueByteLimit ) {
+		VFS_MarkFull(Node, 1);
+	}
+	
 	Mutex_Release(&lep->lList);
 
 	// Signal other end
diff --git a/KernelLand/Kernel/drv/fifo.c b/KernelLand/Kernel/drv/fifo.c
index de3192ab004aae0f4945c73f1aa193522fec9c1e..6d5d4835b7bbfe812c6929680750d98afb5f0271 100644
--- a/KernelLand/Kernel/drv/fifo.c
+++ b/KernelLand/Kernel/drv/fifo.c
@@ -10,15 +10,16 @@
 #include <modules.h>
 #include <fs_devfs.h>
 #include <semaphore.h>
+#include <memfs_helpers.h>
 
 // === CONSTANTS ===
 #define DEFAULT_RING_SIZE	2048
 #define PF_BLOCKING		1
 
 // === TYPES ===
-typedef struct sPipe {
-	struct sPipe	*Next;
-	char	*Name;
+typedef struct sPipe
+{
+	tMemFS_FileHdr	FileHdr;
 	tVFS_Node	Node;
 	Uint	Flags;
 	 int	ReadPos;
@@ -42,6 +43,9 @@ tPipe	*FIFO_Int_NewPipe(int Size, const char *Name);
 
 // === GLOBALS ===
 MODULE_DEFINE(0, 0x0032, FIFO, FIFO_Install, NULL, NULL);
+tMemFS_DirHdr	gFIFO_RootDir = {
+	.FileHdr = { .Name = "FIFO" },
+};
 tVFS_NodeType	gFIFO_DirNodeType = {
 	.TypeName = "FIFO Dir Node",
 	.ReadDir = FIFO_ReadDir,
@@ -71,7 +75,6 @@ tVFS_Node	gFIFO_AnonNode = {
 	.NumACLs = 1,
 	.ACLs = &gVFS_ACL_EveryoneRW,
 	};
-tPipe	*gFIFO_NamedPipes = NULL;
 
 // === CODE ===
 /**
@@ -80,6 +83,7 @@ tPipe	*gFIFO_NamedPipes = NULL;
  */
 int FIFO_Install(char **Options)
 {
+	MemFS_InitDir( &gFIFO_RootDir );
 	DevFS_AddDevice( &gFIFO_DriverInfo );
 	return MODULE_ERR_OK;
 }
@@ -98,22 +102,14 @@ int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data)
  */
 int FIFO_ReadDir(tVFS_Node *Node, int Id, char Dest[FILENAME_MAX])
 {
-	tPipe	*tmp = gFIFO_NamedPipes;
-	
 	// Entry 0 is Anon Pipes
-	if(Id == 0) {
+	if(Id == 0)
+	{
 		strcpy(Dest, "anon");
 		return 0;
 	}
 	
-	// Find the id'th node
-	while(--Id && tmp)	tmp = tmp->Next;
-	// If the list ended, error return
-	if(!tmp)
-		return -EINVAL;
-	// Return good
-	strncpy(Dest, tmp->Name, FILENAME_MAX);
-	return 0;
+	return MemFS_ReadDir(&gFIFO_RootDir, Id-1, Dest);
 }
 
 /**
@@ -123,11 +119,8 @@ int FIFO_ReadDir(tVFS_Node *Node, int Id, char Dest[FILENAME_MAX])
  */
 tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename, Uint Flags)
 {
-	tPipe	*tmp;
-	if(!Filename)	return NULL;
-	
-	// NULL String Check
-	if(Filename[0] == '\0')	return NULL;
+	ASSERTR(Filename, NULL);
+	ASSERTR(Filename[0], NULL);
 	
 	// Anon Pipe
 	if( strcmp(Filename, "anon") == 0 )
@@ -135,19 +128,15 @@ tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename, Uint Flags)
 		if( Flags & VFS_FDIRFLAG_STAT ) {
 			//return &gFIFI_TemplateAnonNode;
 		}
-		tmp = FIFO_Int_NewPipe(DEFAULT_RING_SIZE, "anon");
-		return &tmp->Node;
+		tPipe *ret = FIFO_Int_NewPipe(DEFAULT_RING_SIZE, "anon");
+		return &ret->Node;
 	}
 	
 	// Check Named List
-	tmp = gFIFO_NamedPipes;
-	while(tmp)
-	{
-		if(strcmp(tmp->Name, Filename) == 0)
-			return &tmp->Node;
-		tmp = tmp->Next;
-	}
-	return NULL;
+	tPipe *ret = (tPipe*)MemFS_FindDir(&gFIFO_RootDir, Filename);
+	if(!ret)
+		return NULL;
+	return &ret->Node;
 }
 
 /**
@@ -155,6 +144,25 @@ tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename, Uint Flags)
  */
 tVFS_Node *FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
 {
+	UNIMPLEMENTED();
+	return NULL;
+}
+
+/**
+ * \brief Delete a pipe
+ */
+int FIFO_Unlink(tVFS_Node *Node, const char *OldName)
+{
+	if(Node != &gFIFO_DriverInfo.RootNode)	return 0;
+	
+	// Can't relink anon
+	if(strcmp(OldName, "anon"))	return 0;
+	
+	// Find node
+	tPipe* pipe = (tPipe*)MemFS_Remove(&gFIFO_RootDir, OldName);
+	if(!pipe)	return 0;
+	
+	free(pipe);
 	return 0;
 }
 
@@ -171,54 +179,23 @@ void FIFO_Reference(tVFS_Node *Node)
  */
 void FIFO_Close(tVFS_Node *Node)
 {
-	tPipe	*pipe;
 	if(!Node->ImplPtr)	return ;
 	
 	Node->ReferenceCount --;
 	if(Node->ReferenceCount)	return ;
 	
-	pipe = Node->ImplPtr;
+	tPipe	*pipe = Node->ImplPtr;
 	
-	if(strcmp(pipe->Name, "anon") == 0) {
-		Log_Debug("FIFO", "Pipe %p closed", Node->ImplPtr);
-		free(Node->ImplPtr);
+	if(strcmp(pipe->FileHdr.Name, "anon") == 0)
+	{
+		Log_Debug("FIFO", "Pipe %p closed", pipe);
+		free(pipe);
 		return ;
 	}
 	
 	return ;
 }
 
-/**
- * \brief Delete a pipe
- */
-int FIFO_Unlink(tVFS_Node *Node, const char *OldName)
-{
-	tPipe	*pipe;
-	
-	if(Node != &gFIFO_DriverInfo.RootNode)	return 0;
-	
-	// Can't relink anon
-	if(strcmp(OldName, "anon"))	return 0;
-	
-	// Find node
-	for(pipe = gFIFO_NamedPipes;
-		pipe;
-		pipe = pipe->Next)
-	{
-		if(strcmp(pipe->Name, OldName) == 0)
-			break;
-	}
-	if(!pipe)	return 0;
-	
-	// Unlink the pipe
-	if(Node->ImplPtr) {
-		free(Node->ImplPtr);
-		return 1;
-	}
-	
-	return 0;
-}
-
 /**
  * \brief Read from a fifo pipe
  */
@@ -412,10 +389,8 @@ tPipe *FIFO_Int_NewPipe(int Size, const char *Name)
 	ret->Buffer = (void*)( (Uint)ret + sizeof(tPipe) + sizeof(tVFS_ACL) );
 	
 	// Set name (and FIFO name)
-	ret->Name = ret->Buffer + Size;
-	strcpy(ret->Name, Name);
-	// - Start empty, max of `Size`
-	//Semaphore_Init( &ret->Semaphore, 0, Size, "FIFO", ret->Name );
+	ret->FileHdr.Name = ret->Buffer + Size;
+	strcpy((char*)ret->FileHdr.Name, Name);
 	
 	// Set Node
 	ret->Node.ReferenceCount = 1;
diff --git a/KernelLand/Kernel/drv/proc.c b/KernelLand/Kernel/drv/proc.c
index 03288aeaa475e81b4a53778fff118fe8baa38e7b..60bf4dd8f11ec7127888f8f3636de96e05b8d2fd 100644
--- a/KernelLand/Kernel/drv/proc.c
+++ b/KernelLand/Kernel/drv/proc.c
@@ -50,9 +50,8 @@ tVFS_NodeType	gSysFS_DirNodeType = {
 	.FindDir = SysFS_Comm_FindDir
 	};
 tSysFS_Ent	gSysFS_Version_Kernel = {
-	NULL, NULL,	// Nexts
-	&gSysFS_Version,	// Parent
-	{
+	.Parent = &gSysFS_Version,	// Parent
+	.Node = {
 		.Inode = 1,	// File #1
 		.ImplPtr = NULL,
 		.ImplInt = (Uint)&gSysFS_Version_Kernel,	// Self-Link
@@ -61,12 +60,11 @@ tSysFS_Ent	gSysFS_Version_Kernel = {
 		.ACLs = &gVFS_ACL_EveryoneRO,
 		.Type = &gSysFS_FileNodeType
 	},
-	"Kernel"
+	.Name = {"Kernel"}
 };
 tSysFS_Ent	gSysFS_Version = {
-	NULL, NULL,
-	&gSysFS_Root,
-	{
+	.Parent = &gSysFS_Root,
+	.Node = {
 		.Size = 1,
 		.ImplPtr = &gSysFS_Version_Kernel,
 		.ImplInt = (Uint)&gSysFS_Version,	// Self-Link
@@ -75,7 +73,7 @@ tSysFS_Ent	gSysFS_Version = {
 		.Flags = VFS_FFLAG_DIRECTORY,
 		.Type = &gSysFS_DirNodeType
 	},
-	"Version"
+	.Name = {"Version"}
 };
 // Root of the SysFS tree (just used to keep the code clean)
 tSysFS_Ent	gSysFS_Root = {
@@ -86,7 +84,7 @@ tSysFS_Ent	gSysFS_Root = {
 		.ImplPtr = &gSysFS_Version,
 		.ImplInt = (Uint)&gSysFS_Root	// Self-Link
 	},
-	"/"
+	{"/"}
 };
 tDevFS_Driver	gSysFS_DriverInfo = {
 	NULL, "system",
diff --git a/KernelLand/Kernel/drv/pty.c b/KernelLand/Kernel/drv/pty.c
index 3b30b996e4aba0035584b2cb810124be3a9a5c80..d1f74c58d8fe47eab82114efa7c951b6f58119fe 100644
--- a/KernelLand/Kernel/drv/pty.c
+++ b/KernelLand/Kernel/drv/pty.c
@@ -85,6 +85,7 @@ tVFS_NodeType	gPTY_NodeType_Root = {
 };
 tVFS_NodeType	gPTY_NodeType_Client = {
 	.TypeName = "PTY-Client",
+	.Flags = VFS_NODETYPEFLAG_STREAM,
 	.Read = PTY_ReadClient,
 	.Write = PTY_WriteClient,
 	.IOCtl = PTY_IOCtl,
@@ -93,6 +94,7 @@ tVFS_NodeType	gPTY_NodeType_Client = {
 };
 tVFS_NodeType	gPTY_NodeType_Server = {
 	.TypeName = "PTY-Server",
+	.Flags = VFS_NODETYPEFLAG_STREAM,
 	.Read = PTY_ReadServer,
 	.Write = PTY_WriteServer,
 	.IOCtl = PTY_IOCtl,
@@ -235,6 +237,7 @@ tPTY *PTY_Create(const char *Name, void *Handle, tPTY_OutputFcn Output, tPTY_Req
 	if( InitialMode )
 		ret->Mode = *InitialMode;
 	// - Client node
+	ret->ClientNode.Size = -1;
 	ret->ClientNode.ImplPtr = ret;
 	ret->ClientNode.Type = &gPTY_NodeType_Client;
 	ret->ClientNode.UID = Threads_GetUID();
@@ -624,12 +627,14 @@ size_t PTY_WriteClient(tVFS_Node *Node, off_t Offset, size_t Length, const void
 	// If the server has terminated, send SIGPIPE
 	if( pty->ServerNode && pty->ServerNode->ReferenceCount == 0 )
 	{
+		LOG("SIGPIPE, server has terminated");
 		Threads_PostSignal(SIGPIPE);
 		errno = EIO;
 		return -1;
 	}	
 
 	// Write to either FIFO or directly to output function
+	LOG("pty->OutputFcn = %p", pty->OutputFcn);
 	if( pty->OutputFcn ) {
 		pty->OutputFcn(pty->OutputHandle, Length, Buffer);
 		return Length;
@@ -810,6 +815,8 @@ int PTY_IOCtl(tVFS_Node *Node, int ID, void *Data)
 	
 	int	is_server = !pty || Node == pty->ServerNode;
 
+	LOG("(%i,%p) %s", ID, Data, (is_server?"Server":"Client"));
+
 	switch(ID)
 	{
 	case DRV_IOCTL_TYPE:	return DRV_TYPE_TERMINAL;
diff --git a/KernelLand/Kernel/drv/serial.c b/KernelLand/Kernel/drv/serial.c
index bc7597e4bdb0b6b3b9858c1ccdcf66a3cc24e7cc..df13fc73e11b6f3e25572fa0d49df9f459922437 100644
--- a/KernelLand/Kernel/drv/serial.c
+++ b/KernelLand/Kernel/drv/serial.c
@@ -5,6 +5,7 @@
  * drv/serial.c
  * - Common serial port code
  */
+#define DEBUG	0
 #include <acess.h>
 #include <modules.h>
 #include <fs_devfs.h>
@@ -65,6 +66,7 @@ tSerialPort *Serial_CreatePort(tSerial_OutFcn output, void *handle)
 
 void Serial_ByteReceived(tSerialPort *Port, char Ch)
 {
+	LOG("Port=%p,Ch=%i", Port, Ch);
 	if( !Port )
 		return ;
 	if( Port == gSerial_KernelDebugPort )
@@ -109,6 +111,7 @@ void Serial_ByteReceived(tSerialPort *Port, char Ch)
 	}
 	if( Ch == '\r' )
 		Ch = '\n';
+	LOG("Dispatch to PTY");
 	PTY_SendInput(Port->PTY, &Ch, 1);
 }
 
diff --git a/KernelLand/Kernel/drv/shm.c b/KernelLand/Kernel/drv/shm.c
new file mode 100644
index 0000000000000000000000000000000000000000..da3214132c4490c795afb3e4941a1cb6f660b24b
--- /dev/null
+++ b/KernelLand/Kernel/drv/shm.c
@@ -0,0 +1,302 @@
+/* 
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * drv/shm.c
+ * - Shared memory "device"
+ */
+#define DEBUG	0
+#include <acess.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#include <memfs_helpers.h> 
+#include <semaphore.h>
+
+#define PAGE_COUNT(v)	(((v)+(PAGE_SIZE-1))/PAGE_SIZE)
+
+// === TYPES ===
+#define PAGES_PER_BLOCK	1024
+typedef struct sSHM_BufferBlock
+{
+	struct sSHM_BufferBlock	*Next;
+	tPAddr	Pages[PAGES_PER_BLOCK];
+} tSHM_BufferBlock;
+typedef struct
+{
+	tMemFS_FileHdr	FileHdr;
+	tVFS_Node	Node;
+	size_t	nPages;
+	tSHM_BufferBlock	FirstBlock;
+} tSHM_Buffer;
+
+// === PROTOTYPES ===
+ int	SHM_Install(char **Arguments);
+ int	SHM_Uninstall(void);
+tSHM_Buffer	*SHM_CreateBuffer(const char *Name);
+bool	SHM_AddPages(tSHM_Buffer *Buffer, size_t num);
+void	SHM_DeleteBuffer(tSHM_Buffer *Buffer);
+// - Root directory
+ int	SHM_ReadDir(tVFS_Node *Node, int Id, char Dest[FILENAME_MAX]);
+tVFS_Node	*SHM_FindDir(tVFS_Node *Node, const char *Filename, Uint Flags);
+tVFS_Node	*SHM_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
+ int	SHM_Unlink(tVFS_Node *Node, const char *OldName);
+// - Buffers
+void	SHM_Reference(tVFS_Node *Node);
+void	SHM_Close(tVFS_Node *Node);
+off_t	SHM_Truncate(tVFS_Node *Node, off_t NewSize);
+size_t	SHM_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags);
+size_t	SHM_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags);
+ int	SHM_MMap(struct sVFS_Node *Node, off_t Offset, size_t Length, void *Dest);
+
+// === GLOBALS ===
+MODULE_DEFINE(0, 0x0100, SHM, SHM_Install, SHM_Uninstall, NULL);
+tMemFS_DirHdr	gSHM_RootDir = {
+	.FileHdr = {.Name = "SHMRoot"}
+};
+tVFS_NodeType	gSHM_DirNodeType = {
+	.TypeName = "SHM Root",
+	.ReadDir = SHM_ReadDir,
+	.FindDir = SHM_FindDir,
+	.MkNod   = SHM_MkNod,
+	.Unlink  = SHM_Unlink,
+};
+tVFS_NodeType	gSHM_FileNodeType = {
+	.TypeName = "SHM Buffer",
+	.Read  = SHM_Read,
+	.Write = SHM_Write,
+	.Close = SHM_Close,
+	.MMap  = SHM_MMap,
+	.Truncate = SHM_Truncate,
+	.Reference = SHM_Reference,
+};
+tDevFS_Driver	gSHM_DriverInfo = {
+	.Name = "shm",
+	.RootNode = {
+		.Size = 0,
+		.NumACLs = 1,
+		.ACLs = &gVFS_ACL_EveryoneRW,
+		.Flags = VFS_FFLAG_DIRECTORY,
+		.Type = &gSHM_DirNodeType
+	}
+};
+
+// === CODE ===
+int SHM_Install(char **Arguments)
+{
+	MemFS_InitDir(&gSHM_RootDir);
+	DevFS_AddDevice( &gSHM_DriverInfo );
+	return MODULE_ERR_OK;
+}
+int SHM_Uninstall(void)
+{
+	return MODULE_ERR_OK;
+}
+tSHM_Buffer *SHM_CreateBuffer(const char *Name)
+{
+	tSHM_Buffer *ret = calloc(1, sizeof(tSHM_Buffer) + strlen(Name) + 1);
+	MemFS_InitFile(&ret->FileHdr);
+	ret->FileHdr.Name = (const char*)(ret+1);
+	strcpy((char*)ret->FileHdr.Name, Name);
+	ret->Node.ImplPtr = ret;
+	ret->Node.Type = &gSHM_FileNodeType;
+	ret->Node.ReferenceCount = 1;
+	return ret;
+}
+bool SHM_AddPages(tSHM_Buffer *Buffer, size_t num)
+{
+	tSHM_BufferBlock	*block = &Buffer->FirstBlock;
+	// Search for final block
+	size_t	idx = Buffer->nPages;
+	while( block->Next ) {
+		block = block->Next;
+		idx -= PAGES_PER_BLOCK;
+	}
+	ASSERTC(idx, <=, PAGES_PER_BLOCK);
+	
+	for( size_t i = 0; i < num; i ++ )
+	{
+		if( idx == PAGES_PER_BLOCK )
+		{
+			block->Next = calloc(1, sizeof(tSHM_BufferBlock));
+			if(!block->Next) {
+				Log_Warning("SHM", "Out of memory, allocating new buffer block");
+				return false;
+			}
+			block = block->Next;
+			idx = 0;
+		}
+		ASSERT(block->Pages[idx] == 0);
+		block->Pages[idx] = MM_AllocPhys();
+		if( !block->Pages[idx] ) {
+			Log_Warning("SHM", "Out of memory, allocating page");
+			return false;
+		}
+		Buffer->nPages += 1;
+		idx ++;
+	}
+	return true;
+}
+void SHM_DeleteBuffer(tSHM_Buffer *Buffer)
+{
+	ASSERTCR(Buffer->Node.ReferenceCount,==,0,);
+	
+	// TODO: Destroy multi-block nodes
+	ASSERT(Buffer->FirstBlock.Next == NULL);
+	
+	free(Buffer);
+}
+// - Root directory
+int SHM_ReadDir(tVFS_Node *Node, int Id, char Dest[FILENAME_MAX])
+{
+	return MemFS_ReadDir(&gSHM_RootDir, Id, Dest);
+}
+
+/**
+ * \brief Open a shared memory buffer
+ *
+ * \note Opening 'anon' will always succeed, and will create an anonymous mapping
+ */
+tVFS_Node *SHM_FindDir(tVFS_Node *Node, const char *Filename, Uint Flags)
+{
+	if( strcmp(Filename, "anon") == 0 )
+	{
+		tSHM_Buffer *ret = SHM_CreateBuffer("");
+		return &ret->Node;
+	}
+	
+	tMemFS_FileHdr	*file = MemFS_FindDir(&gSHM_RootDir, Filename);
+	if( !file )
+		return NULL;
+	
+	return &((tSHM_Buffer*)file)->Node;
+}
+
+
+/**
+ * \brief Create a named shared memory file
+ */
+tVFS_Node *SHM_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
+{
+	if( MemFS_FindDir(&gSHM_RootDir, Name) )
+		return NULL;
+	
+	tSHM_Buffer *ret = SHM_CreateBuffer(Name);
+	if( !MemFS_Insert(&gSHM_RootDir, &ret->FileHdr) )
+	{
+		ret->Node.ReferenceCount = 0;
+		SHM_DeleteBuffer(ret);
+		return NULL;
+	}
+
+	return &ret->Node;
+}
+
+/**
+ * \breif Remove a named shared memory buffer (will be deleted when all references drop)
+ */
+int SHM_Unlink(tVFS_Node *Node, const char *OldName)
+{
+	tMemFS_FileHdr	*file = MemFS_Remove(&gSHM_RootDir, OldName);
+	if( !file )
+		return 1;
+
+	tSHM_Buffer *buf = (tSHM_Buffer*)file;
+	if( buf->Node.ReferenceCount == 0 )
+	{
+		SHM_DeleteBuffer(buf);
+	}
+	else
+	{
+		// dangling references, let them clean themselves up later
+	}
+	return 0;
+}
+// - Buffers
+void SHM_Reference(tVFS_Node *Node)
+{
+	Node->ReferenceCount ++;
+}
+void SHM_Close(tVFS_Node *Node)
+{
+	Node->ReferenceCount --;
+	if( Node->ReferenceCount == 0 )
+	{
+		// TODO: How to tell if a buffer should be deleted here?
+		UNIMPLEMENTED();
+	}
+}
+off_t SHM_Truncate(tVFS_Node *Node, off_t NewSize)
+{
+	ENTER("pNode XNewSize", Node, NewSize);
+	tSHM_Buffer	*buffer = Node->ImplPtr;
+	LOG("Node->Size = 0x%llx", Node->Size);
+	if( PAGE_COUNT(NewSize) != PAGE_COUNT(Node->Size) )
+	{
+		 int	page_difference = PAGE_COUNT(NewSize) - PAGE_COUNT(Node->Size);
+		LOG("page_difference = %i", page_difference);
+		if( page_difference < 0 )
+		{
+			// Truncate down
+			// TODO: What if underlying pages are mapped?... should it matter?
+			UNIMPLEMENTED();
+		}
+		else
+		{
+			// Truncate up
+			SHM_AddPages(buffer, page_difference);
+		}
+	}
+	else
+	{
+		LOG("Page count hasn't changed");
+	}
+	Node->Size = NewSize;
+	LEAVE('X', NewSize);
+	return NewSize;
+}
+size_t SHM_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
+{
+	UNIMPLEMENTED();
+	return -1;
+}
+size_t SHM_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
+{
+	// TODO: Should first write determine the fixed size of the buffer?
+	UNIMPLEMENTED();
+	return -1;
+}
+int SHM_MMap(struct sVFS_Node *Node, off_t Offset, size_t Length, void *Dest)
+{
+	tSHM_Buffer	*buf = Node->ImplPtr;
+	if( Offset > Node->Size )	return 1;
+	if( Offset + Length > Node->Size )	return 1;
+	
+	const int pagecount = (Length + Offset % PAGE_SIZE) / PAGE_SIZE;
+	int pagenum = Offset / PAGE_SIZE;
+	
+	tSHM_BufferBlock	*block = &buf->FirstBlock;
+	while( pagenum > PAGES_PER_BLOCK ) {
+		block = block->Next;
+		ASSERT(block);
+		pagenum -= PAGES_PER_BLOCK;
+	}
+	
+	tPage *dst = Dest;
+	for( int i = 0; i < pagecount; i ++ )
+	{
+		if( pagenum == PAGES_PER_BLOCK ) {
+			block = block->Next;
+			ASSERT(block);
+			pagenum = 0;
+		}
+		
+		ASSERT(block->Pages[pagenum]);
+		LOG("%p => %i:%P", dst, pagenum, block->Pages[pagenum]);
+		MM_Map(dst, block->Pages[pagenum]);
+		
+		pagenum ++;
+		dst ++;
+	}
+	return 0;
+}
+
diff --git a/KernelLand/Kernel/drv/vterm.c b/KernelLand/Kernel/drv/vterm.c
index 2c6a0d2f8a79ce34cda825b1f8fc3a21250ed2be..217557058c0376ae5507092f951d9e037ebd7d07 100644
--- a/KernelLand/Kernel/drv/vterm.c
+++ b/KernelLand/Kernel/drv/vterm.c
@@ -5,7 +5,7 @@
  * drv/vterm.c
  * - Virtual Terminal - Initialisation and VFS Interface
  */
-#define DEBUG	1
+#define DEBUG	0
 #include "vterm.h"
 #include <fs_devfs.h>
 #include <modules.h>
@@ -34,7 +34,6 @@ extern void	Debug_SetKTerminal(const char *File);
 // === PROTOTYPES ===
  int	VT_Install(char **Arguments);
  int	VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data);
-void	VT_int_PutFBData(tVTerm *Term, size_t Offset, size_t Length, const void *Data);
 void	VT_PTYOutput(void *Handle, size_t Length, const void *Data);
  int	VT_PTYResize(void *Handle, const struct ptydims *Dims); 
  int	VT_PTYModeset(void *Handle, const struct ptymode *Mode);
@@ -369,15 +368,16 @@ void VT_PTYOutput(void *Handle, size_t Length, const void *Data)
 		VT_int_PutString(term, Data, Length);
 		break;
 	case PTYBUFFMT_FB:
-		// TODO: How do offset?
+		// TODO: How can the offset be done cleanly? (Ask the PTY for its offset?)
+		Warning("TODO: Offsets for VT_PTYOutput FBData");
 		VT_int_PutFBData(term, 0, Length, Data);
 		break;
 	case PTYBUFFMT_2DCMD:
-		// TODO: Impliment 2D commands
 		VT_int_Handle2DCmd(term, Length, Data);
 		break;
 	case PTYBUFFMT_3DCMD:
-		// TODO: Impliment 3D commands
+		// TODO: Implement 3D commands
+		Warning("TODO: VTerm 3D commands");
 		break;
 	}
 }
diff --git a/KernelLand/Kernel/drv/vterm.h b/KernelLand/Kernel/drv/vterm.h
index 05aad87a458ccb0376805c9be956e61816a48f42..75128ba5968760376a3b39f9684aba14c8e62425 100644
--- a/KernelLand/Kernel/drv/vterm.h
+++ b/KernelLand/Kernel/drv/vterm.h
@@ -94,6 +94,11 @@ struct sVTerm
 		 int	CachePos;
 		char	Cache[32];
 		size_t	PreEat;
+		union {
+			struct {
+				size_t	Offset;
+			} Push;
+		} CmdInfo;
 	} Cmd2D;
 
 	tPTY	*PTY;
@@ -134,6 +139,7 @@ extern void	VT_int_ClearLine(tVTerm *Term, int Num);
 extern void	VT_int_ClearInLine(tVTerm *Term, int Row, int FirstCol, int LastCol);
 extern void	VT_int_Resize(tVTerm *Term, int NewWidth, int NewHeight);
 extern void	VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled);
+extern void	VT_int_PutFBData(tVTerm *Term, size_t Offset, size_t Length, const void *Buffer);
 
 extern tVT_Pos	*VT_int_GetWritePosPtr(tVTerm *Term);
 extern size_t	VT_int_GetBufferRows(tVTerm *Term);
diff --git a/KernelLand/Kernel/drv/vterm_2d.c b/KernelLand/Kernel/drv/vterm_2d.c
index 4b750ab77e23a31eb0620b1c7911410d58085fe8..086c2b4f37190aa9e4e4e450b1bbcf7f105c3d02 100644
--- a/KernelLand/Kernel/drv/vterm_2d.c
+++ b/KernelLand/Kernel/drv/vterm_2d.c
@@ -14,6 +14,7 @@ void	VT_int_SetCursorBitmap(tVTerm *Term, int W, int H);
 size_t	VT_int_FillCursorBitmap(tVTerm *Term, size_t DataOfs, size_t Length, const void *Data);
  int	VT_int_2DCmd_SetCursorPos(void *Handle, size_t Offset, size_t Length, const void *Data);
  int	VT_int_2DCmd_SetCursorBitmap(void *Handle, size_t Offset, size_t Length, const void *Data);
+ int	VT_int_2DCmd_SendData(void *Handle, size_t Offset, size_t Length, const void *Data);
 
 // === CODE ===
 void VT_int_SetCursorPos(tVTerm *Term, int X, int Y)
@@ -102,18 +103,15 @@ int VT_int_2DCmd_SetCursorBitmap(void *Handle, size_t Offset, size_t Length, con
 		Length -= ret;
 		Data = (const char*)Data + ret;
 		Offset += ret;
-		if( Length == 0 )
-			return -ret;
 	}
+	
+	ASSERTC(Offset, >=, sizeof(cmd));
 
-
-	if( Offset < sizeof(cmd) ) {
-		// oops?
-		return ret;
+	if( Length > 0 )
+	{
+		ret += VT_int_FillCursorBitmap(Handle, Offset - sizeof(cmd), Length, Data);
 	}
 
-	ret += VT_int_FillCursorBitmap(Handle, Offset - sizeof(cmd), Length, Data);
-
 	LOG("%i + %i ==? %i", ret, Offset, term->Cmd2D.CurrentSize);
 	if( ret + Offset >= term->Cmd2D.CurrentSize )
 		return ret;
@@ -121,6 +119,46 @@ int VT_int_2DCmd_SetCursorBitmap(void *Handle, size_t Offset, size_t Length, con
 	return -ret;
 }
 
+int VT_int_2DCmd_SendData(void *Handle, size_t Offset, size_t Length, const void *Data)
+{
+	tVTerm	*term = Handle;
+	struct ptycmd_senddata	cmd;
+	size_t	ret = 0;
+	
+	if( Offset == 0 )
+	{
+		if( Length < sizeof(cmd) )
+			return 0;
+		memcpy(&cmd, Data, sizeof(cmd));
+		
+		ret = sizeof(cmd);
+		Offset += ret;
+		Length -= ret;
+		Data = (const char*)Data + ret;
+		
+		term->Cmd2D.CmdInfo.Push.Offset = cmd.ofs*4;
+	}
+	
+	ASSERTC(Offset, >=, sizeof(cmd));
+	
+	if( Length > 0 )
+	{
+		size_t	bytes = MIN(term->Width*term->Height*4 - term->Cmd2D.CmdInfo.Push.Offset, Length);
+		LOG("bytes = %i (0x%x), Length = %i", bytes, bytes, Length);
+		
+		VT_int_PutFBData(term, term->Cmd2D.CmdInfo.Push.Offset, bytes, Data );
+		term->Cmd2D.CmdInfo.Push.Offset += bytes;
+		ret += bytes;
+		
+		LOG("bytes(%i) ==? 0 || ret(%i) + Offset(%i) ==? %i",
+			bytes, ret, Offset, term->Cmd2D.CurrentSize);
+		if( bytes == 0 || ret + Offset >= term->Cmd2D.CurrentSize )
+			return ret;
+	}
+	
+	return -ret;
+}
+
 // > 0: Command complete
 // = 0: Not enough data to start
 // < 0: Ate -n bytes, still need more
@@ -128,6 +166,7 @@ typedef int	(*tVT_2DCmdHandler)(void *Handle, size_t Offset, size_t Length, cons
 tVT_2DCmdHandler	gVT_2DCmdHandlers[] = {
 	[PTY2D_CMD_SETCURSORPOS] = VT_int_2DCmd_SetCursorPos,
 	[PTY2D_CMD_SETCURSORBMP] = VT_int_2DCmd_SetCursorBitmap,
+	[PTY2D_CMD_SEND] = VT_int_2DCmd_SendData,
 };
 const int	ciVT_Num2DCmdHandlers = sizeof(gVT_2DCmdHandlers)/sizeof(gVT_2DCmdHandlers[0]);
 
@@ -137,7 +176,7 @@ void VT_int_Handle2DCmd(void *Handle, size_t Length, const void *Data)
 	tVTerm	*term = Handle;
 
 	LOG("Length = 0x%x", Length);
-	// If a command terminated early, we have to clean up its data
+	// If a command didn't consume all the data it said it would, we have to clean up
 _eat:
 	if( term->Cmd2D.PreEat )
 	{
@@ -164,7 +203,10 @@ _eat:
 		}
 		// else begin a new command
 		else
-		{		
+		{
+			// If the new data would fit in the cache, or the cache is already populated
+			// use the cache
+			// - The cache should fit the header for every command, so all good
 			if( Length < cachesize || term->Cmd2D.CachePos != 0 )
 			{
 				adjust = term->Cmd2D.CachePos;
@@ -174,25 +216,33 @@ _eat:
 				dataptr = (void*)term->Cmd2D.Cache;
 				len = term->Cmd2D.CachePos;
 			}
-			else {
+			else
+			{
 				dataptr = (void*)bdata;
 				len = Length;
 				adjust = 0;
 			}
 			const struct ptycmd_header	*hdr = dataptr;
 
-			if( len < sizeof(*hdr) ) {
+			// If there's not enough for the common header, wait for more
+			if( len < sizeof(*hdr) )
+			{
 				return ;
 			}			
 
+			// Parse header
 			term->Cmd2D.Offset = 0;
 			term->Cmd2D.Current = hdr->cmd;
 			term->Cmd2D.CurrentSize = (hdr->len_low | (hdr->len_hi << 8)) * 4;
-			if( term->Cmd2D.CurrentSize == 0 )
+			if( term->Cmd2D.CurrentSize == 0 ) {
+				Log_Warning("VTerm", "Command size too small (==0)");
 				term->Cmd2D.CurrentSize = 2;
+			}
 			LOG("Started %i with %s data",
 				term->Cmd2D.Current, (dataptr == bdata ? "direct" : "cache"));
 		}
+
+		// Sanity check
 		if( term->Cmd2D.Current >= ciVT_Num2DCmdHandlers || !gVT_2DCmdHandlers[term->Cmd2D.Current] )
 		{
 			Log_Notice("VTerm", "2D Comand %i not handled", term->Cmd2D.Current);
@@ -203,42 +253,60 @@ _eat:
 			term->Cmd2D.PreEat = term->Cmd2D.CurrentSize;
 			goto _eat;
 		}
-		else
+		
+		const tVT_2DCmdHandler*	handler = &gVT_2DCmdHandlers[term->Cmd2D.Current];
+		#if 0
+		if( term->Cmd2D.Offset == 0 )
 		{
-			int rv = gVT_2DCmdHandlers[term->Cmd2D.Current](Handle, term->Cmd2D.Offset, len, dataptr);
-			LOG("2DCmd %i: rv=%i", term->Cmd2D.Current, rv);
-			if( rv == 0 && term->Cmd2D.Offset == 0 ) {
-				// 0: Not enough data for header
-				ASSERT( term->Cmd2D.CachePos != cachesize );
-				// Clear current command because this command hasn't started yet
-				term->Cmd2D.Current = 0;
-				// Return, restart happens once all data is ready
+			if( len < handler->HeaderLength ) {
 				return ;
 			}
-			size_t used_bytes = (rv < 0 ? -rv : rv) - adjust;
-			Length -= used_bytes;
-			bdata += used_bytes;
-			term->Cmd2D.CachePos = 0;
-			if( rv < 0 ) {
-				ASSERT( -rv <= len );
-				LOG(" Incomplete");
-				term->Cmd2D.Offset += -rv;
-				continue ;
-			}
-			ASSERT(rv <= len);
+			rv = handler->Header(Handle, len, dataptr);
+		}
+		else
+		{
+			rv = hander->Body(Handle, term->Cmd2D.Offset, len, dataptr);
+		}
+		#endif
+		
+		// Call Handler	
+		int rv = (*handler)(Handle, term->Cmd2D.Offset, len, dataptr);
+		LOG("2DCmd %i: rv=%i", term->Cmd2D.Current, rv);
+		
+		// If it returned 0 on the first call, it lacks space for the header
+		if( rv == 0 && term->Cmd2D.Offset == 0 )
+		{
+			ASSERT( term->Cmd2D.CachePos != cachesize );
+			// Clear current command because this command hasn't started yet
 			term->Cmd2D.Current = 0;
+			// Return, restart happens once all data is ready
+			return ;
+		}
 
-			// Eat up any uneaten data
-			// - TODO: Need to eat across writes
-			ASSERT( term->Cmd2D.Offset + rv <= term->Cmd2D.CurrentSize );
-			if( term->Cmd2D.Offset + rv < term->Cmd2D.CurrentSize )
-			{
-				size_t	diff = term->Cmd2D.CurrentSize - (term->Cmd2D.Offset + rv);
-				LOG("Left %i bytes", diff);
-				term->Cmd2D.PreEat = diff;
-				goto _eat;
-			}
-			LOG("Done (%i bytes left)", Length);
+		// Consume the byte count returned (adjust is the number of bytes that were already cached)
+		size_t used_bytes = (rv < 0 ? -rv : rv) - adjust;
+		Length -= used_bytes;
+		bdata += used_bytes;
+		term->Cmd2D.CachePos = 0;
+		// If a negative count was returned, more data is expected
+		if( rv < 0 ) {
+			ASSERT( -rv <= len );
+			LOG(" Incomplete");
+			term->Cmd2D.Offset += -rv;
+			continue ;
+		}
+		ASSERT(rv <= len);
+		term->Cmd2D.Current = 0;
+
+		// Eat up any uneaten data
+		ASSERT( term->Cmd2D.Offset + rv <= term->Cmd2D.CurrentSize );
+		if( term->Cmd2D.Offset + rv < term->Cmd2D.CurrentSize )
+		{
+			size_t	diff = term->Cmd2D.CurrentSize - (term->Cmd2D.Offset + rv);
+			LOG("Left %i bytes", diff);
+			term->Cmd2D.PreEat = diff;
+			goto _eat;
 		}
+		LOG("Done (%i bytes left)", Length);
 	}
 }
diff --git a/KernelLand/Kernel/drv/vterm_input.c b/KernelLand/Kernel/drv/vterm_input.c
index 6c25cc81974a888863f4b653a9630f6d8abd8500..aa517d9df69758af9832d6a905124c955f655d9f 100644
--- a/KernelLand/Kernel/drv/vterm_input.c
+++ b/KernelLand/Kernel/drv/vterm_input.c
@@ -95,20 +95,30 @@ void VT_KBCallBack(Uint32 Codepoint)
 		
 //		Log_Debug("VTerm", "Magic Ctrl-Alt-0x%x", term->RawScancode);	
 
+		const unsigned int scroll_step = term->TextHeight / 2;
+		// Note the lack of giVT_Scrollback+1, view top can't go above size-onescreen
+		const unsigned int scroll_max = term->TextHeight * giVT_Scrollback;
 		switch(term->RawScancode)
 		{
-		// Scrolling
+		// VTerm scrolling
+		// - Scrolls half a screen at a time
+		// - View up (text goes down)
 		case KEYSYM_PGUP:
 			if( term->Flags & VT_FLAG_ALTBUF )
 				return ;
-			term->ViewTopRow = MAX(0, term->ViewTopRow - 1);
+			Log_Debug("VTerm", "ScrollUp - Old=%i, step=%i", term->ViewTopRow, scroll_step);
+			term->ViewTopRow = (term->ViewTopRow > scroll_step ? term->ViewTopRow - scroll_step : 0);
+			Log_Debug("VTerm", "ScrollUp - New=%i", term->ViewTopRow);
 			VT_int_UpdateScreen(term, 1);
 			return;
+		// - View down (text goes up)
 		case KEYSYM_PGDN:
 			if( term->Flags & VT_FLAG_ALTBUF )
 				return ;
-			// Note the lack of giVT_Scrollback+1, view top can't go above size-onescreen
-			term->ViewTopRow = MIN(term->ViewTopRow + 1, term->Height * giVT_Scrollback);
+			
+			Log_Debug("VTerm", "ScrollDown - Old=%i, max=%i", term->ViewTopRow, scroll_max);
+			term->ViewTopRow = MIN(term->ViewTopRow + scroll_step, scroll_max);
+			Log_Debug("VTerm", "ScrollDown - New=%i", term->ViewTopRow);
 			VT_int_UpdateScreen(term, 1);
 			return;
 		}
diff --git a/KernelLand/Kernel/drv/vterm_output.c b/KernelLand/Kernel/drv/vterm_output.c
index 532d2686376cbcd04f9072ffa47390f3259cd941..95c552f5f898417fc41db9a8edf3f8766ffb2834 100644
--- a/KernelLand/Kernel/drv/vterm_output.c
+++ b/KernelLand/Kernel/drv/vterm_output.c
@@ -5,9 +5,9 @@
  * drv/vterm_input.c
  * - Virtual Terminal - Input code
  */
+#define DEBUG	0
 #include "vterm.h"
 #include <api_drv_video.h>
-#define DEBUG	0
 
 // === CODE ===
 /**
@@ -52,8 +52,12 @@ void VT_int_ScrollFramebuffer( tVTerm *Term, int Count )
 	// Only update if this is the current terminal
 	if( Term != gpVT_CurTerm )	return;
 	
+	ENTER("pTerm iCount",
+		Term, Count);
+	
 	if( Count > Term->ScrollHeight )	Count = Term->ScrollHeight;
 	if( Count < -Term->ScrollHeight )	Count = -Term->ScrollHeight;
+	LOG("Count = %i", Count);
 	
 	// Switch to 2D Command Stream
 	tmp = VIDEO_BUFFMT_2DSTREAM;
@@ -81,6 +85,7 @@ void VT_int_ScrollFramebuffer( tVTerm *Term, int Count )
 	// Restore old mode (this function is only called during text mode)
 	tmp = VIDEO_BUFFMT_TEXT;
 	VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp);
+	LEAVE('-');
 }
 
 void VT_int_UpdateCursor( tVTerm *Term, int bShow )
@@ -88,6 +93,8 @@ void VT_int_UpdateCursor( tVTerm *Term, int bShow )
 	tVideo_IOCtl_Pos	csr_pos;
 
 	if( Term != gpVT_CurTerm )	return ;
+	
+	ENTER("pTerm bShow", Term, bShow);
 
 	if( !bShow )
 	{
@@ -119,6 +126,7 @@ void VT_int_UpdateCursor( tVTerm *Term, int bShow )
 		csr_pos.y = Term->VideoCursorY;
 	}
 	VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &csr_pos);
+	LEAVE('-');
 }	
 
 /**
@@ -127,15 +135,21 @@ void VT_int_UpdateCursor( tVTerm *Term, int bShow )
  */
 void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
 {
+	ENTER("pTerm iUpdateAll", Term, UpdateAll);
 	// Only update if this is the current terminal
-	if( Term != gpVT_CurTerm )	return;
-	
+	if( Term != gpVT_CurTerm ) {
+		LOG("Term != gpVT_CurTerm (%p)", gpVT_CurTerm);
+		LEAVE('-');
+		return;
+	}
+		
 	switch( Term->Mode )
 	{
 	case TERM_MODE_TEXT: {
 		size_t view_pos = (Term->Flags & VT_FLAG_ALTBUF) ? 0 : Term->ViewTopRow*Term->TextWidth;
 		const tVT_Pos *wrpos = VT_int_GetWritePosPtr(Term);
 		const tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text;
+		LOG("view_pos = %i, wrpos = %p (R%i,C%i), buffer=%p", view_pos, wrpos, wrpos->Row, wrpos->Col, buffer);
 		// Re copy the entire screen?
 		if(UpdateAll) {
 			VFS_WriteAt(
@@ -148,6 +162,7 @@ void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
 		// Only copy the current line
 		else {
 			size_t	ofs = wrpos->Row * Term->TextWidth;
+			LOG("ofs = %i", ofs);
 			VFS_WriteAt(
 				giVT_OutputDevHandle,
 				(ofs - view_pos)*sizeof(tVT_Char),
@@ -161,5 +176,6 @@ void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll )
 	}
 	
 	VT_int_UpdateCursor(Term, 1);
+	LEAVE('-');
 }
 
diff --git a/KernelLand/Kernel/drv/vterm_termbuf.c b/KernelLand/Kernel/drv/vterm_termbuf.c
index 50787228063fb0910894b358c3dca53610f05050..1a14f8252ab8481f45c3c477e36df1f4336ab7eb 100644
--- a/KernelLand/Kernel/drv/vterm_termbuf.c
+++ b/KernelLand/Kernel/drv/vterm_termbuf.c
@@ -25,6 +25,7 @@ extern int	Term_HandleVT100(tVTerm *Term, int Len, const char *Buf);
  */
 void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
 {
+	ENTER("pTerm pBuffer iCount", Term, Buffer, Count);
 	// Iterate
 	for( int ofs = 0; ofs < Count; )
 	{
@@ -43,7 +44,9 @@ void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
 		ofs += esc_len;
 	}
 	// Update Screen
-	VT_int_UpdateScreen( Term, 1 );
+	LOG("Update");
+	VT_int_UpdateScreen( Term, 0 );
+	LEAVE('-');
 }
 
 void VT_int_PutRawString(tVTerm *Term, const Uint8 *String, size_t Bytes)
@@ -76,7 +79,7 @@ void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
 	{
 		ASSERTC(wrpos->Col, <=, Term->TextWidth);
 		VT_int_UpdateScreen( Term, 0 );
-		//wrpos->Row ++;
+		wrpos->Row ++;
 		wrpos->Col = 0;
 	}
 
@@ -121,6 +124,7 @@ void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
 	case '\0':	// Ignore NULL byte
 		return;
 	case '\n':
+		LOG("Newline, update @ %i", write_pos);
 		VT_int_UpdateScreen( Term, 0 );	// Update the line before newlining
 		wrpos->Row ++;
 		// TODO: Force scroll?
@@ -165,10 +169,13 @@ void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
 		buffer[ write_pos ].Ch = Ch;
 		buffer[ write_pos ].Colour = Term->CurColour;
 		// Update the line before wrapping
-		if( (write_pos + 1) % Term->TextWidth == 0 )
+		if( (write_pos + 1) % Term->TextWidth == 0 ) {
+			LOG("Line wrap, update @ %i", write_pos);
 			VT_int_UpdateScreen( Term, 0 );
-		write_pos ++;
+			// NOTE: Code at the top of PutChar handles the actual wrapping
+		}
 		wrpos->Col ++;
+		write_pos ++;
 		break;
 	}
 	
@@ -176,11 +183,12 @@ void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
 
 	HEAP_VALIDATE();
 	
-	//LEAVE('-');
+	// TODO: Schedule a delayed screen update
 }
 
 void VT_int_ScrollText(tVTerm *Term, int Count)
 {
+	ENTER("pTerm iCount", Term, Count);
 	tVT_Char	*buf;
 	 int	scroll_top, scroll_height;
 	
@@ -274,6 +282,7 @@ void VT_int_ScrollText(tVTerm *Term, int Count)
 	*wrpos = init_wrpos;
 
 	HEAP_VALIDATE();
+	LEAVE('-');
 }
 
 /**
diff --git a/KernelLand/Kernel/drv/vterm_vt100.c b/KernelLand/Kernel/drv/vterm_vt100.c
index 4428b64ee4fc215d6299c26eaa5b60b090316e5f..0d59a666fc7f6158968087cceb55352a17571a1b 100644
--- a/KernelLand/Kernel/drv/vterm_vt100.c
+++ b/KernelLand/Kernel/drv/vterm_vt100.c
@@ -44,7 +44,7 @@ void Display_ScrollDown(tTerminal *Term, int CountDown)
 	LOG("(%i)", CountDown);
 	VT_int_UpdateScreen(Term, 0);
 	if( Term->Flags & VT_FLAG_ALTBUF )
-		VT_int_ScrollText(Term, CountDown);
+		VT_int_ScrollText(Term, -CountDown);
 	else
 	{
 		if(Term->ViewTopRow + CountDown < 0)
diff --git a/KernelLand/Kernel/drv/zero-one.c b/KernelLand/Kernel/drv/zero-one.c
index fa2b502f3059a61feafc748311c2d1b7a82f1d39..b21d1eff8d31622797c949b5ac9dc9a624ab17c0 100644
--- a/KernelLand/Kernel/drv/zero-one.c
+++ b/KernelLand/Kernel/drv/zero-one.c
@@ -23,33 +23,38 @@ size_t	CoreDevs_Read_GRandom(tVFS_Node *Node, off_t Offset, size_t Length, void
 MODULE_DEFINE(0, 0x0100, CoreDevs, CoreDevs_Install, NULL, NULL);
 tVFS_NodeType	gCoreDevs_NT_Null = {
 	.TypeName = "CoreDevs-null",
+	.Flags = VFS_NODETYPEFLAG_STREAM,
 	.Read  = CoreDevs_Read_Null,
 	.Write = CoreDevs_Write
 };
 tVFS_NodeType	gCoreDevs_NT_Zero = {
 	.TypeName = "CoreDevs-zero",
+	.Flags = VFS_NODETYPEFLAG_STREAM,
 	.Read  = CoreDevs_Read_Zero,
 	.Write = CoreDevs_Write
 };
 tVFS_NodeType	gCoreDevs_NT_One = {
 	.TypeName = "CoreDevs-one",
+	.Flags = VFS_NODETYPEFLAG_STREAM,
 	.Read  = CoreDevs_Read_One,
 	.Write = CoreDevs_Write
 };
 tVFS_NodeType	gCoreDevs_NT_FRandom = {
 	.TypeName = "CoreDevs-frandom",
+	.Flags = VFS_NODETYPEFLAG_STREAM,
 	.Read  = CoreDevs_Read_FRandom,
 	.Write = CoreDevs_Write
 };
 tVFS_NodeType	gCoreDevs_NT_GRandom = {
 	.TypeName = "CoreDevs-grandom",
+	.Flags = VFS_NODETYPEFLAG_STREAM,
 	.Read  = CoreDevs_Read_GRandom,
 	.Write = CoreDevs_Write
 };
 tDevFS_Driver	gCoreDevs_Null = {
 	NULL, "null",
 	{
-	.Size = 0, .NumACLs = 1,
+	.Size = -1, .NumACLs = 1,
 	.ACLs = &gVFS_ACL_EveryoneRW,
 	.Type = &gCoreDevs_NT_Null
 	}
@@ -57,7 +62,7 @@ tDevFS_Driver	gCoreDevs_Null = {
 tDevFS_Driver	gCoreDevs_Zero = {
 	NULL, "zero",
 	{
-	.Size = 0, .NumACLs = 1,
+	.Size = -1, .NumACLs = 1,
 	.ACLs = &gVFS_ACL_EveryoneRW,
 	.Type = &gCoreDevs_NT_Zero
 	}
@@ -65,7 +70,7 @@ tDevFS_Driver	gCoreDevs_Zero = {
 tDevFS_Driver	gCoreDevs_One = {
 	NULL, "one",
 	{
-	.Size = 0, .NumACLs = 1,
+	.Size = -1, .NumACLs = 1,
 	.ACLs = &gVFS_ACL_EveryoneRW,
 	.Type = &gCoreDevs_NT_One
 	}
@@ -73,7 +78,7 @@ tDevFS_Driver	gCoreDevs_One = {
 tDevFS_Driver	gCoreDevs_FRandom = {
 	NULL, "frandom",
 	{
-	.Size = 0, .NumACLs = 1,
+	.Size = -1, .NumACLs = 1,
 	.ACLs = &gVFS_ACL_EveryoneRW,
 	.Type = &gCoreDevs_NT_FRandom,
 	.DataAvaliable = 1
@@ -82,7 +87,7 @@ tDevFS_Driver	gCoreDevs_FRandom = {
 tDevFS_Driver	gCoreDevs_GRandom = {
 	NULL, "grandom",
 	{
-	.Size = 0, .NumACLs = 1,
+	.Size = -1, .NumACLs = 1,
 	.ACLs = &gVFS_ACL_EveryoneRW,
 	.Type = &gCoreDevs_NT_GRandom,
 	.DataAvaliable = 1
diff --git a/KernelLand/Kernel/drvutil_video.c b/KernelLand/Kernel/drvutil_video.c
index 4343ab6f181f51dc77e37375b3822651c0224bde..2bffb1ae92f0b932eccaf4bda790c02964c55db3 100644
--- a/KernelLand/Kernel/drvutil_video.c
+++ b/KernelLand/Kernel/drvutil_video.c
@@ -536,8 +536,6 @@ void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf)
 void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf)
 {
 	 int	bytes_per_px = (Buf->Depth + 7) / 8;
-	 int	y, save_pitch;
-	Uint8	*dest, *src;
 
 	// Just a little sanity
 	if( !Buf->CursorBitmap || Buf->CursorX == -1 )	return ;
@@ -546,16 +544,21 @@ void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf)
 //	Debug("DrvUtil_Video_RemoveCursor: (Buf=%p) dest_x=%i, dest_y=%i", Buf, Buf->CursorDestX, Buf->CursorDestY);
 
 	// Set up
-	save_pitch = Buf->CursorBitmap->W * bytes_per_px;
-	dest = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px;
-	src = Buf->CursorSaveBuf;
-	
+	size_t	save_pitch = Buf->CursorBitmap->W * bytes_per_px;
+	Uint8	*dst = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px;
+	const Uint8	*src = Buf->CursorSaveBuf;
+
+	ASSERT(Buf->Framebuffer);
+	ASSERT(src);
+	ASSERT(CheckMem(dst, Buf->CursorRenderH*Buf->Pitch));
+	ASSERT(CheckMem(src, Buf->CursorRenderH*save_pitch));
+
 	// Copy each line back
-	for( y = 0; y < Buf->CursorRenderH; y ++ )
+	for( int y = 0; y < Buf->CursorRenderH; y ++ )
 	{
-		memcpy( dest, src, Buf->CursorRenderW * bytes_per_px );
+		memcpy( dst, src, Buf->CursorRenderW * bytes_per_px );
 		src += save_pitch;
-		dest += Buf->Pitch;
+		dst += Buf->Pitch;
 	}
 	
 	// Set the cursor as removed
diff --git a/KernelLand/Kernel/emergency_console.c b/KernelLand/Kernel/emergency_console.c
new file mode 100644
index 0000000000000000000000000000000000000000..dd6d708b31c2c22fbea3c808167af3ce41733dfd
--- /dev/null
+++ b/KernelLand/Kernel/emergency_console.c
@@ -0,0 +1,226 @@
+/*
+ * Acess 2 Kernel
+ * - By John Hodge (thePowersGang)
+ * 
+ * emergency_console.c
+ * - Kernel-land emergency console/shell
+ */
+#include <acess.h>
+#include <stdarg.h>
+
+#define STDIN	0
+#define STDOUT	1
+#define STDERR	2
+
+// === PROTOTYPES ===
+void EmergencyConsole(void);
+// -- Commands
+void Command_ls(const char* path);
+void Command_hd(const char* path);
+void Command_mount(const char *fs, const char *dev, const char *point);
+// --
+static char **split_args(char *line);
+static char *read_line(int fd);
+static int dprintf(int fd, const char *fmt, ...);// __attribute__((printf(2,3)));
+
+// === CODE ===
+void EmergencyConsole(void)
+{
+	for(;;)
+	{
+		dprintf(STDOUT, "(kernel)$ ");
+		char *line = read_line(STDIN);
+		
+		// Explode line into args
+		char **args = split_args(line);
+		// Get command from first arg
+		ASSERT(args);
+		ASSERT(args[0]);
+		if( strcmp(args[0], "help") == 0 ) {
+			dprintf(STDOUT,
+				"Commands:\n"
+				"ls <path> - List the contents of a directory\n"
+				"hd <path> - Dump a file in a 16 bytes/line hex format\n"
+				"mount <fs> <device> <point> - Mount a filesystem\n"
+				);
+		}
+		else if( strcmp(args[0], "ls") == 0 ) {
+			Command_ls(args[1]); 
+		}
+		else if( strcmp(args[0], "hd") == 0 ) {
+			Command_hd(args[1]);
+		}
+		else if( strcmp(args[0], "mount") == 0 ) {
+			Command_mount(args[1], args[2], args[3]);
+		}
+		else
+		{
+			dprintf(STDERR, "Unknown command '%s'\n", args[0]);
+		}
+		// Free args
+		free(args);
+		free(line);
+	}
+}
+
+void Command_ls(const char* path)
+{
+	dprintf(STDERR, "ls: TODO - Implement\n");
+}
+void Command_hd(const char* path)
+{
+	dprintf(STDERR, "hd: TODO - Implement\n");
+}
+void Command_mount(const char *fs, const char *dev, const char *point)
+{
+	dprintf(STDERR, "mount: TODO - Implement\n");
+}
+
+// Allocates return array (NUL terminated), but mangles input string
+static int split_args_imp(char *line, char **buf)
+{
+	 int	argc = 0;
+	 int	pos = 0;
+	enum {
+		MODE_SPACE,
+		MODE_NORM,
+		MODE_SQUOTE,
+		MODE_DQUOTE,
+	} mode = MODE_SPACE;
+	for( char *chp = line; *chp; chp ++ )
+	{
+		if( *chp == ' ' ) {
+			if( mode != MODE_SPACE ) {
+				if(buf) buf[argc][pos] = '\0';
+				argc ++;
+			}
+			mode = MODE_SPACE;
+			continue ;
+		}
+		
+		switch( mode )
+		{
+		case MODE_SPACE:
+			if( buf )
+				buf[argc] = chp;
+			pos = 0;
+		case MODE_NORM:
+			switch( *chp )
+			{
+			case '"':
+				mode = MODE_DQUOTE;
+				break;
+			case '\'':
+				mode = MODE_SQUOTE;
+				break;
+			case '\\':
+				chp ++;
+				switch( *chp )
+				{
+				case '\0':
+					dprintf(STDERR, "err: Trailing '\\'\n");
+					break;
+				case '\\':
+				case '\'':
+				case '"':
+					if(buf)	buf[argc][pos++] = *chp;
+					break;
+				default:
+					dprintf(STDERR, "err: Unkown escape '%c'\n", *chp);
+					break;
+				}
+				break;
+			default:
+				if(buf)	buf[argc][pos++] = *chp;
+				break;
+			}
+			break;
+		case MODE_SQUOTE:
+			switch( *chp )
+			{
+			case '\'':
+				mode = MODE_NORM;
+				break;
+			case '\0':
+				dprintf(STDERR, "err: Unterminated \' string\n");
+				break;
+			default:
+				if(buf)	buf[argc][pos++] = *chp;
+				break;
+			}
+			break;
+		case MODE_DQUOTE:
+			switch( *chp )
+			{
+			case '\"':
+				mode = MODE_NORM;
+				break;
+			case '\\':
+				chp ++;
+				switch(*chp)
+				{
+				case '\0':
+					dprintf(STDERR, "err: Trailing '\\' in \" string\n");
+					break;
+				case '\\':
+				case '"':
+					if(buf)	buf[argc][pos++] = *chp;
+					break;
+				default:
+					dprintf(STDERR, "err: Unkown escape '%c'\n", *chp);
+					break;
+				}
+				break;
+			case '\0':
+				dprintf(STDERR, "err: Unterminated \" string\n");
+				break;
+			default:
+				if(buf)	buf[argc][pos++] = *chp;
+				break;
+			}
+			break;
+		}
+	}
+
+	if(buf) buf[argc][pos++] = '\0';
+	argc ++;
+	return argc;
+}
+static char **split_args(char *line)
+{
+	// 1. Count
+	int count = split_args_imp(line, NULL);
+	char **ret = malloc( (count + 1) * sizeof(char*) );
+	
+	split_args_imp(line, ret);
+	ret[count] = NULL;
+	return ret;
+}
+
+static char *read_line(int fd)
+{
+	// Read line (or up to ~128 bytes)
+	// - This assumes a default PTY state (i.e. line buffered, echo on)
+	char *ret = malloc(128);
+	size_t len = VFS_Read(STDIN, 128, ret);
+	ret[len] = 0;
+	return ret;
+}
+
+static int dprintf(int fd, const char *fmt, ...)
+{
+	va_list	args;
+	va_start(args, fmt);
+	size_t len = vsnprintf(NULL, 0, fmt, args);
+	va_end(args);
+	
+	char buf[len+1];
+	va_start(args, fmt);
+	vsnprintf(buf, len+1, fmt, args);
+	va_end(args);
+	
+	VFS_Write(fd, len, buf);
+	
+	return len;
+}
+
diff --git a/KernelLand/Kernel/events.c b/KernelLand/Kernel/events.c
index b0225efe2750eccae269209618b19871a761145a..a572d43362d1e66628a7cd258a7bdfee581892fa 100644
--- a/KernelLand/Kernel/events.c
+++ b/KernelLand/Kernel/events.c
@@ -7,8 +7,8 @@
  */
 #define DEBUG	0
 #include <acess.h>
-#include <threads_int.h>
 #include <events.h>
+#include <threads_int.h>
 
 // === CODE ===
 void Threads_PostEvent(tThread *Thread, Uint32 EventMask)
@@ -87,7 +87,7 @@ Uint32 Threads_WaitEvents(Uint32 EventMask)
 	{
 		Threads_int_Sleep(THREAD_STAT_EVENTSLEEP, NULL, EventMask,
 			&us, NULL, &us->IsLocked);
-		// Woken when lock is acquired
+		// Woken when an event fires
 		SHORTLOCK( &us->IsLocked );
 	}
 	
diff --git a/KernelLand/Kernel/heap.c b/KernelLand/Kernel/heap.c
index f7054ea4375684ed981eb729d26ce875c183f606..ba4c9adbdd9add234a3bd0c7649056f33f920bb6 100755
--- a/KernelLand/Kernel/heap.c
+++ b/KernelLand/Kernel/heap.c
@@ -353,6 +353,8 @@ void Heap_Deallocate(const char *File, int Line, void *Ptr)
 	// INVLPTR is returned from Heap_Allocate when the allocation
 	// size is zero.
 	if( Ptr == INVLPTR )	return;
+	// free(NULL) is a no-op
+	if( Ptr == NULL )	return;
 	
 	// Alignment Check
 	if( (tVAddr)Ptr % sizeof(void*) != 0 ) {
diff --git a/KernelLand/Kernel/include/acess.h b/KernelLand/Kernel/include/acess.h
index fe1a0f972cd56acfdb754b50a5e8148dd4ee420d..83611bb6851b31fc2d9aaa9e29f88d75d98e378d 100644
--- a/KernelLand/Kernel/include/acess.h
+++ b/KernelLand/Kernel/include/acess.h
@@ -27,7 +27,7 @@
 #define DEPRECATED	__attribute__((deprecated))
 //! Mark a parameter as unused
 #define UNUSED(x)	UNUSED_##x __attribute__((unused))
-//! 
+//! Apply alignment to a variable 
 #define ALIGN(x)	__attribute__((aligned(x)))
 
 /**
@@ -85,11 +85,11 @@ extern const char	gsBuildInfo[];
  * \{
  * \todo Move to mm_virt.h
  */
-#define	MM_PFLAG_RO		0x01	// Writes disallowed
-#define	MM_PFLAG_EXEC	0x02	// Allow execution
-#define	MM_PFLAG_NOPAGE	0x04	// Prevent from being paged out
-#define	MM_PFLAG_COW	0x08	// Copy-On-Write
-#define	MM_PFLAG_KERNEL	0x10	// Kernel-Only (Ring0)
+#define	MM_PFLAG_RO	0x01	//!< Writes disallowed
+#define	MM_PFLAG_EXEC	0x02	//!< Allow execution
+#define	MM_PFLAG_NOPAGE	0x04	//!< Prevent from being paged out
+#define	MM_PFLAG_COW	0x08	//!< Copy-On-Write
+#define	MM_PFLAG_KERNEL	0x10	//!< Kernel-Only (Ring0)
 /**
  * \}
  */
@@ -143,7 +143,9 @@ typedef struct sKernelSymbol {
  * \name IRQ hander registration
  * \{
  */
+//! Register a callback for when an IRQ is raised
 extern int	IRQ_AddHandler(int Num, void (*Callback)(int, void*), void *Ptr);
+//! Remove a previously registered IRQ handler
 extern void	IRQ_RemHandler(int Handle);
 /**
  * \}
@@ -240,6 +242,15 @@ extern Uint	MM_GetFlags(volatile const void *VAddr);
  * \note There is only a limited ammount of slots avaliable
  */
 extern void	*MM_MapTemp(tPAddr PAddr);
+/**
+ * \brief Peform a temporary map of a page from another process
+ * \param Process	Source process
+ * \param Address	Source virtual address
+ * \return Virtual address of page in memory
+ * \note Limited slots
+ */
+struct sProcess;
+extern void	*MM_MapTempFromProc(struct sProcess *Process, const void *Address);
 /**
  * \brief Free a temporarily mapped page
  * \param Ptr	Pointer to page base
diff --git a/KernelLand/Kernel/include/acess_string.h b/KernelLand/Kernel/include/acess_string.h
index 12e9ba47e8698cae2f32a82cdbb8d7869ed7e30d..b6407932c2c29fce19ea6d79a894f7db5d2072d9 100644
--- a/KernelLand/Kernel/include/acess_string.h
+++ b/KernelLand/Kernel/include/acess_string.h
@@ -10,7 +10,7 @@
 #include <stdarg.h>
 
 /**
- * \name Strings
+ * \name String Manipulation
  * \{
  */
 // - stdio.h in userland
@@ -38,6 +38,7 @@ extern unsigned long	strtoul(const char *str, char **end, int base);
 extern signed long long	strtoll(const char *str, char **end, int base);
 extern signed long	strtol(const char *str, char **end, int base);
 
+//! \brief String comparison (case-insensitive)
 extern int	strucmp(const char *Str1, const char *Str2);
 extern int	strpos(const char *Str, char Ch);
 extern int	strpos8(const char *str, Uint32 search);
diff --git a/KernelLand/Kernel/include/apidoc_mainpage.h b/KernelLand/Kernel/include/apidoc_mainpage.h
index 8b5b85fb35f1e396806ed7678c6d85b917909249..7a5e427dff4fa1b32ad3dfa2aaecdcb461bf39b7 100644
--- a/KernelLand/Kernel/include/apidoc_mainpage.h
+++ b/KernelLand/Kernel/include/apidoc_mainpage.h
@@ -23,6 +23,8 @@
  *    user.
  *  - Drivers for specific types of hardware must behave in the specific
  *    way described here.
+ * - \ref library	"Library Functions"
+ *  - Kernel's version of libc and other helper functions
  * 
  * \page drivers Device Drivers
  * 
diff --git a/KernelLand/Kernel/include/drv_pci.h b/KernelLand/Kernel/include/drv_pci.h
index 1032814fc121e00fa2d8bba77cde1904e9f2e169..bdc8c850073222ddb4474799b2800bc3bb1a786a 100644
--- a/KernelLand/Kernel/include/drv_pci.h
+++ b/KernelLand/Kernel/include/drv_pci.h
@@ -82,5 +82,6 @@ extern Uint8	PCI_GetIRQ(tPCIDev id);
 extern Uint32	PCI_GetBAR(tPCIDev id, int BAR);
 extern Uint64	PCI_GetValidBAR(tPCIDev id, int BAR, tPCI_BARType BARType);
 //extern Uint16	PCI_AssignPort(tPCIDev id, int bar, int count);
+//extern void*	PCI_MapMemBAR(tPCIDev id, int BAR, tPCI_BARType BARType, size_t* Size, tPAddr* PAddr);
 
 #endif
diff --git a/KernelLand/Kernel/include/logdebug.h b/KernelLand/Kernel/include/logdebug.h
index 0710c6bc2cae9d901bb36f79e4940e224761840f..f13187e67f5ff065b41940b63dba530c8d88c100 100644
--- a/KernelLand/Kernel/include/logdebug.h
+++ b/KernelLand/Kernel/include/logdebug.h
@@ -38,7 +38,7 @@ extern void	Log_Debug(const char *Ident, const char *Message, ...);
 extern void	Debug_KernelPanic(void);	//!< Initiate a kernel panic
 extern void	Panic(const char *Msg, ...) NORETURN;	//!< Print a panic message (initiates a kernel panic)
 extern void	Warning(const char *Msg, ...);	//!< Print a warning message
-extern void	LogF(const char *Fmt, ...);	//!< Print a log message without a trailing newline
+extern bool	LogF(const char *Fmt, ...);	//!< Print a log message without a trailing newline
 extern void	LogFV(const char *Fmt, va_list Args);	//!< va_list non-newline log message
 extern void	Log(const char *Fmt, ...);	//!< Print a log message
 extern void	Debug(const char *Fmt, ...);	//!< Print a debug message (doesn't go to KTerm)
@@ -49,6 +49,7 @@ extern void	Debug_Leave(const char *FuncName, char RetType, ...);
 extern void	Debug_HexDump(const char *Header, const void *Data, size_t Length);
 
 #define UNIMPLEMENTED()	Warning("'%s' unimplemented", __func__)
+#define TODO(fmt, ...)	Panic("TODO: ", fmt ,## __VA_ARGS__)
 #if DEBUG
 # define ENTER(_types...)	Debug_Enter((char*)__func__, _types)
 # define LOG(_fmt...)	Debug_Log((char*)__func__, _fmt)
@@ -72,8 +73,8 @@ extern void	Debug_HexDump(const char *Header, const void *Data, size_t Length);
 #define assert(expr)	ASSERTV(expr, "")
 #define ASSERT(expr)	ASSERTV(expr, "")
 #define ASSERTR(expr,rv)	ASSERTRV(expr, rv, "")
-#define ASSERTC(l,rel,r)	ASSERTV(l rel r, ": 0x%x"#rel"0x%x", l, r)
-#define ASSERTCR(l,rel,r,rv)	ASSERTRV(l rel r, rv, ": 0x%x"#rel"0x%x", l, r)
+#define ASSERTC(l,rel,r)	ASSERTV ((l) rel (r), ": 0x%x"#rel"0x%x", (Uint)l, (Uint)r)
+#define ASSERTCR(l,rel,r,rv)	ASSERTRV((l) rel (r), rv, ": 0x%x"#rel"0x%x", (Uint)l, (Uint)r)
 /**
  * \}
  */
diff --git a/KernelLand/Kernel/include/memfs_helpers.h b/KernelLand/Kernel/include/memfs_helpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..c20e1704bae3504e55482097dea9418d4000b308
--- /dev/null
+++ b/KernelLand/Kernel/include/memfs_helpers.h
@@ -0,0 +1,56 @@
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * memfs_helpers.h
+ * - Helpers for in-memory filesystems
+ *
+ * Provides name lookup, iteration, and node insertion
+ */
+#ifndef _MEMFS_HELPERS_H_
+#define _MEMFS_HELPERS_H_
+
+#include <vfs.h>
+
+typedef struct sMemFS_FileHdr	tMemFS_FileHdr;
+typedef struct sMemFS_DirHdr	tMemFS_DirHdr;
+
+extern void	MemFS_InitDir (tMemFS_DirHdr  *Dir);
+extern void	MemFS_InitFile(tMemFS_FileHdr *File);
+
+/*
+ * \brief Fetch the name of the file at the specified position
+ * \return standard tVFS_NodeType.ReadDir return values
+ */
+extern int	MemFS_ReadDir(tMemFS_DirHdr *Dir, int Pos, char Name[FILENAME_MAX]);
+/*
+ * \brief Look up a file in a directory
+ */
+extern tMemFS_FileHdr	*MemFS_FindDir(tMemFS_DirHdr *Dir, const char *Name);
+/**
+ * \brief Remove a named file from a directory
+ * \return File header for \a Name, or NULL if not found
+ */
+extern tMemFS_FileHdr	*MemFS_Remove(tMemFS_DirHdr *Dir, const char *Name);
+/**
+ * \brief Insert a pre-constructed file header into the directory
+ * \param Dir	Directory
+ * \return false if name already exists
+ */
+extern bool	MemFS_Insert(tMemFS_DirHdr *Dir, tMemFS_FileHdr *File);
+
+struct sMemFS_FileHdr
+{
+	tMemFS_FileHdr	*Next;
+	const char	*Name;
+};
+
+struct sMemFS_DirHdr
+{
+	tMemFS_FileHdr	FileHdr;
+	//tRWLock	Lock;
+	tMemFS_FileHdr	*FirstChild;
+};
+
+#endif
+
diff --git a/KernelLand/Kernel/include/syscalls.h b/KernelLand/Kernel/include/syscalls.h
index 9916458f0ba72f0d73646b3efd178ed0a1d1d1d7..9c68195df814e473467dd24bf5725179b7c7fea2 100644
--- a/KernelLand/Kernel/include/syscalls.h
+++ b/KernelLand/Kernel/include/syscalls.h
@@ -53,25 +53,34 @@
 #define SYS_COPYFD	69	// Create a copy of a file handle
 #define SYS_FDCTL	70	// Modify flags of a file descriptor
 #define SYS_READ	71	// Read from an open file
-#define SYS_WRITE	72	// Write to an open file
-#define SYS_IOCTL	73	// Perform an IOCtl Call
-#define SYS_SEEK	74	// Seek to a new position in the file
-#define SYS_READDIR	75	// Read from an open directory
-#define SYS_GETACL	76	// Get an ACL Value
-#define SYS_SETACL	77	// Set an ACL Value
-#define SYS_FINFO	78	// Get file information
-#define SYS_MKDIR	79	// Create a new directory
-#define SYS_LINK	80	// Create a new link to a file
-#define SYS_SYMLINK	81	// Create a symbolic link
-#define SYS_UNLINK	82	// Delete a file
-#define SYS_TELL	83	// Return the current file position
-#define SYS_CHDIR	84	// Change current directory
-#define SYS_GETCWD	85	// Get current directory
-#define SYS_MOUNT	86	// Mount a filesystem
-#define SYS_SELECT	87	// Wait for file handles
+#define SYS_READAT	72	// Read from an open file (with offset)
+#define SYS_WRITE	73	// Write to an open file
+#define SYS_WRITEAT	74	// Write to an open file (with offset)
+#define SYS_TRUNCATE	75	// Change the size of an open file
+#define SYS_IOCTL	76	// Perform an IOCtl Call
+#define SYS_SEEK	77	// Seek to a new position in the file
+#define SYS_READDIR	78	// Read from an open directory
+#define SYS_GETACL	79	// Get an ACL Value
+#define SYS_SETACL	80	// Set an ACL Value
+#define SYS_FINFO	81	// Get file information
+#define SYS_MKDIR	82	// Create a new directory
+#define SYS_LINK	83	// Create a new link to a file
+#define SYS_SYMLINK	84	// Create a symbolic link
+#define SYS_UNLINK	85	// Delete a file
+#define SYS_TELL	86	// Return the current file position
+#define SYS_CHDIR	87	// Change current directory
+#define SYS_GETCWD	88	// Get current directory
+#define SYS_MOUNT	89	// Mount a filesystem
+#define SYS_SELECT	90	// Wait for file handles
+#define SYS_MMAP	91	// Map a file into this address space
+#define SYS_MUNMAP	92	// Unmap a file
+#define SYS_MARSHALFD	93	// Create a reference to a FD suitable for handing to another process
+#define SYS_UNMARSHALFD	94	// Accept a marshaled FD
 
-#define NUM_SYSCALLS	88
-#define SYS_DEBUG	0x100
+#define NUM_SYSCALLS	95
+#define SYS_DEBUGS	0x100
+#define SYS_DEBUGF	0x101
+#define SYS_DEBUGHEX	0x102
 
 #if !defined(__ASSEMBLER__) && !defined(NO_SYSCALL_STRS)
 static const char *cSYSCALL_NAMES[] = {
@@ -147,7 +156,10 @@ static const char *cSYSCALL_NAMES[] = {
 	"SYS_COPYFD",
 	"SYS_FDCTL",
 	"SYS_READ",
+	"SYS_READAT",
 	"SYS_WRITE",
+	"SYS_WRITEAT",
+	"SYS_TRUNCATE",
 	"SYS_IOCTL",
 	"SYS_SEEK",
 	"SYS_READDIR",
@@ -163,6 +175,10 @@ static const char *cSYSCALL_NAMES[] = {
 	"SYS_GETCWD",
 	"SYS_MOUNT",
 	"SYS_SELECT",
+	"SYS_MMAP",
+	"SYS_MUNMAP",
+	"SYS_MARSHALFD",
+	"SYS_UNMARSHALFD",
 
 	""
 };
diff --git a/KernelLand/Kernel/include/syscalls.inc.asm b/KernelLand/Kernel/include/syscalls.inc.asm
index ee3c8870b21eca0007ed511222b599e23174cd0d..35743b10681ce9d0a9a0ed0a6621abfd0392e15c 100644
--- a/KernelLand/Kernel/include/syscalls.inc.asm
+++ b/KernelLand/Kernel/include/syscalls.inc.asm
@@ -45,19 +45,26 @@
 %define SYS_COPYFD	69	 ;Create a copy of a file handle
 %define SYS_FDCTL	70	 ;Modify flags of a file descriptor
 %define SYS_READ	71	 ;Read from an open file
-%define SYS_WRITE	72	 ;Write to an open file
-%define SYS_IOCTL	73	 ;Perform an IOCtl Call
-%define SYS_SEEK	74	 ;Seek to a new position in the file
-%define SYS_READDIR	75	 ;Read from an open directory
-%define SYS_GETACL	76	 ;Get an ACL Value
-%define SYS_SETACL	77	 ;Set an ACL Value
-%define SYS_FINFO	78	 ;Get file information
-%define SYS_MKDIR	79	 ;Create a new directory
-%define SYS_LINK	80	 ;Create a new link to a file
-%define SYS_SYMLINK	81	 ;Create a symbolic link
-%define SYS_UNLINK	82	 ;Delete a file
-%define SYS_TELL	83	 ;Return the current file position
-%define SYS_CHDIR	84	 ;Change current directory
-%define SYS_GETCWD	85	 ;Get current directory
-%define SYS_MOUNT	86	 ;Mount a filesystem
-%define SYS_SELECT	87	 ;Wait for file handles
+%define SYS_READAT	72	 ;Read from an open file (with offset)
+%define SYS_WRITE	73	 ;Write to an open file
+%define SYS_WRITEAT	74	 ;Write to an open file (with offset)
+%define SYS_TRUNCATE	75	 ;Change the size of an open file
+%define SYS_IOCTL	76	 ;Perform an IOCtl Call
+%define SYS_SEEK	77	 ;Seek to a new position in the file
+%define SYS_READDIR	78	 ;Read from an open directory
+%define SYS_GETACL	79	 ;Get an ACL Value
+%define SYS_SETACL	80	 ;Set an ACL Value
+%define SYS_FINFO	81	 ;Get file information
+%define SYS_MKDIR	82	 ;Create a new directory
+%define SYS_LINK	83	 ;Create a new link to a file
+%define SYS_SYMLINK	84	 ;Create a symbolic link
+%define SYS_UNLINK	85	 ;Delete a file
+%define SYS_TELL	86	 ;Return the current file position
+%define SYS_CHDIR	87	 ;Change current directory
+%define SYS_GETCWD	88	 ;Get current directory
+%define SYS_MOUNT	89	 ;Mount a filesystem
+%define SYS_SELECT	90	 ;Wait for file handles
+%define SYS_MMAP	91	 ;Map a file into this address space
+%define SYS_MUNMAP	92	 ;Unmap a file
+%define SYS_MARSHALFD	93	 ;Create a reference to a FD suitable for handing to another process
+%define SYS_UNMARSHALFD	94	 ;Accept a marshaled FD
diff --git a/KernelLand/Kernel/include/threads.h b/KernelLand/Kernel/include/threads.h
index ed90d0bb81ad7e11f06f0ba381d69dd93e331863..02039329f828ab76e5311b66670b5bf77f3cc21a 100644
--- a/KernelLand/Kernel/include/threads.h
+++ b/KernelLand/Kernel/include/threads.h
@@ -21,6 +21,7 @@ enum eFaultNumbers
 #define GETMSG_IGNORE	((void*)-1)
 
 typedef struct sThread	tThread;
+typedef struct sProcess	tProcess;
 
 // === FUNCTIONS ===
 extern tThread	*Proc_GetCurThread(void);
@@ -32,9 +33,9 @@ extern int	Threads_SetGID(tUID ID);
 extern tTID	Threads_WaitTID(int TID, int *Status);
 
 
-extern int	*Threads_GetMaxFD(void);
-extern char	**Threads_GetCWD(void);
-extern char	**Threads_GetChroot(void);
+extern int	*Threads_GetMaxFD(tProcess *Process);
+extern char	**Threads_GetCWD(tProcess *Process);
+extern char	**Threads_GetChroot(tProcess *Process);
 
 extern int	Proc_SendMessage(Uint Dest, int Length, void *Data);
 extern int	Proc_GetMessage(Uint *Source, Uint BufSize, void *Buffer);
diff --git a/KernelLand/Kernel/include/vfs.h b/KernelLand/Kernel/include/vfs.h
index f7666137bbdb21db53ed485bcd64423d0f9226b1..b030627e0c983d2dc6661d72546fd6b0fb20704c 100644
--- a/KernelLand/Kernel/include/vfs.h
+++ b/KernelLand/Kernel/include/vfs.h
@@ -216,6 +216,21 @@ typedef struct sVFS_Node
  * \}
  */
 
+/**
+ * \name tVFS_NodeType.Type flags
+ * \brief Flags for node types
+ * \{
+ */
+//\! Calls to VFS_Write should not generate calls to .Trunctate
+//\! 
+//\! If this flag is set, writing over the end of the file will not call .Truncate automatically
+#define VFS_NODETYPEFLAG_NOAUTOEXPAND	0x001
+//\! Node type describes a stream (offset ignored, seeking disallowed)
+#define VFS_NODETYPEFLAG_STREAM 	0x002
+/**
+ * \}
+ */
+
 /**
  * \brief Functions for a specific node type
  */
@@ -226,6 +241,11 @@ struct sVFS_NodeType
 	 */
 	const char	*TypeName;
 
+	/**
+	 * \brief Flags describing operational quirks
+	 */
+	unsigned int	Flags;
+
 	/**
 	 * \name Common Functions
 	 * \brief Functions that are used no matter the value of .Flags
@@ -293,7 +313,18 @@ struct sVFS_NodeType
 	 * \return Boolean Failure
 	 * \note If NULL, the VFS implements it using .Read
 	 */
-	 int	(*MMap)(struct sVFS_Node *Node, off_t Offset, int Length, void *Dest);
+	 int	(*MMap)(struct sVFS_Node *Node, off_t Offset, size_t Length, void *Dest);
+	
+	/**
+	 * \brief Resize a file
+	 * \param Node	Pointer to this node
+	 * \param NewSize	New file size
+	 * \return Actual new file size
+	 * \note If NULL, \a Write may be called with Offset + Length > Size
+	 *
+	 * Called to increase/decrease the size of a file. If the 
+	 */
+	off_t	(*Truncate)(struct sVFS_Node *Node, off_t NewSize);
 	
 	/**
 	 * \}
@@ -469,7 +500,7 @@ extern tVFS_ACL	*VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group);
  * \param Type	Type of wait
  * \param Timeout	Time to wait (NULL for infinite wait)
  * \param Name	Name to show in debug output
- * \return Number of nodes that actioned (0 or 1)
+ * \return Bitset of Type flags that applied
  */
 extern int	VFS_SelectNode(tVFS_Node *Node, int Type, tTime *Timeout, const char *Name);
 
diff --git a/KernelLand/Kernel/include/vfs_ext.h b/KernelLand/Kernel/include/vfs_ext.h
index 65a81624d1df05a5a23ee9609b253cc1edf9f26b..42d986549cdc50e7dada4118350a700c5bbbe717 100644
--- a/KernelLand/Kernel/include/vfs_ext.h
+++ b/KernelLand/Kernel/include/vfs_ext.h
@@ -226,6 +226,20 @@ extern int	VFS_DuplicateFD(int SrcFD, int DstFD);
  */
 extern int	VFS_SetFDFlags(int FD, int Mask, int Value);
 
+/**
+ * \brief Save specified file handle such that it can be passed between processes
+ * \param FD	File descriptor to save
+ * \return Marshalled handle, or (uint64_t)-1 on error
+ */
+extern Uint64	VFS_MarshalHandle(int FD);
+
+/**
+ * \brief Restore a marshalled handle into the current process
+ * \param Handle returned by VFS_MarshalHandle
+ * \return File descriptor, or -1 on error
+ */
+extern int	VFS_UnmarshalHandle(Uint64 Handle);
+
 /**
  * \brief Get file information from an open file
  * \param FD	File handle returned by ::VFS_Open
@@ -314,6 +328,16 @@ extern size_t	VFS_ReadAt(int FD, Uint64 Offset, size_t Length, void *Buffer);
  */
 extern size_t	VFS_WriteAt(int FD, Uint64 Offset, size_t Length, const void *Buffer);
 
+/**
+ * \brief Set the valid size of a file
+ * \param FD	File descriptor
+ * \param Size	New file size
+ * \return Actual new file size (-1 if error occurred)
+ *
+ * \note Not all files support this call (will return ENOTIMPL)
+ */
+extern off_t	VFS_Truncate(int FD, off_t Size);
+
 /**
  * \brief Sends an IOCtl request to the driver
  * \param FD	File handle returned by ::VFS_Open
diff --git a/KernelLand/Kernel/include/vfs_threads.h b/KernelLand/Kernel/include/vfs_threads.h
index 95ec2278b69891a5ba06eb7a42b2d33c6eb88beb..a9829def6cf130d17f734171a357b1d5fe9abe3a 100644
--- a/KernelLand/Kernel/include/vfs_threads.h
+++ b/KernelLand/Kernel/include/vfs_threads.h
@@ -10,7 +10,7 @@
 
 // === FUNCTIONS ===
 extern void	VFS_ReferenceUserHandles(void);
-extern void	VFS_CloseAllUserHandles(void);
+extern void	VFS_CloseAllUserHandles(struct sProcess *Process);
 
 extern void	*VFS_SaveHandles(int NumFDs, int *FDs);
 extern void	VFS_RestoreHandles(int NumFDs, void *Handles);
diff --git a/KernelLand/Kernel/libc.c b/KernelLand/Kernel/libc.c
index 2e08a17c807993f3609089d65c83d22f4115fc2d..572184d9bd5a94a05342526ce4f84f763e03861d 100644
--- a/KernelLand/Kernel/libc.c
+++ b/KernelLand/Kernel/libc.c
@@ -74,6 +74,7 @@ EXPORT(CheckMem);
 // === CODE ===
 // - Import userland stroi.c file
 #define _LIB_H_
+#define _SysDebug(f,v...)	Log_Debug("libc", f ,## v)
 #include "../../Usermode/Libraries/libc.so_src/strtoi.c"
 
 int ParseInt(const char *string, int *Val)
@@ -380,7 +381,7 @@ int vsnprintf(char *__s, const size_t __maxlen, const char *__format, va_list ar
 				break;
 			}
 			p = va_arg(args, char*);	// Get Argument
-			if( !p || !CheckString(p) )	p = "(inval)";	// Avoid #PFs  
+			if( p && !CheckString(p) )	goto invalString;	// Avoid #PFs  
 		printString:
 			if(!p)		p = "(null)";
 			len = strlen(p);
@@ -388,6 +389,20 @@ int vsnprintf(char *__s, const size_t __maxlen, const char *__format, va_list ar
 			while(*p && precision--) { PUTCH(*p); p++;} 
 			if( bPadLeft )	while(len++ < minSize)	PUTCH(pad);
 			break;
+		invalString:	
+			PUTCH('(');PUTCH('i');PUTCH('n');PUTCH('v');PUTCH('a');	PUTCH('l');PUTCH(':');
+			PUTCH('*');PUTCH('0');PUTCH('x');
+			val = (tVAddr)p;
+			for( len = BITS/4; len -- && ((val>>(len*4))&15) == 0; )
+				;
+			len ++;
+			if( len == 0 )
+				PUTCH( '0' );
+			else
+				while( len -- )
+					PUTCH( cUCDIGITS[ (val>>(len*4))&15 ] );
+			PUTCH(')');
+			break;
 		
 		case 'C':	// Non-Null Terminated Character Array
 			p = va_arg(args, char*);
diff --git a/KernelLand/Kernel/logging.c b/KernelLand/Kernel/logging.c
index 46d4c0494c9f3336ca4eb9671bd21faaade7a3fa..3ccfef2b03159d61cb72f25e27f437bfbe5eef01 100644
--- a/KernelLand/Kernel/logging.c
+++ b/KernelLand/Kernel/logging.c
@@ -158,18 +158,18 @@ void Log_Int_PrintMessage(tLogEntry *Entry)
 	if( CPU_HAS_LOCK(&glLogOutput) )
 		return ;	// TODO: Error?
 	SHORTLOCK( &glLogOutput );
-	LogF("%s%014lli",
+	bool completed = LogF(
+		"%s%014lli%s [%-8s] %i - %.*s\x1B[0m\r\n",
 		csaLevelColours[Entry->Level],
-		Entry->Time
-		);
-	LogF("%s [%-8s] %i - %.*s",
+		Entry->Time,
 		csaLevelCodes[Entry->Level],
 		Entry->Ident,
 		Threads_GetTID(),
 		Entry->Length,
 		Entry->Data
 		);
-	LogF("\x1B[0m\r\n");	// Separate in case Entry->Data is too long
+	if( !completed )
+		LogF("\x1B[0m\r\n");	// Separate in case Entry->Data is too long
 	SHORTREL( &glLogOutput );
 }
 
diff --git a/KernelLand/Kernel/memfs_helpers.c b/KernelLand/Kernel/memfs_helpers.c
new file mode 100644
index 0000000000000000000000000000000000000000..a7e5fc4a45fae6539a658657e06795b357cc2690
--- /dev/null
+++ b/KernelLand/Kernel/memfs_helpers.c
@@ -0,0 +1,54 @@
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * memfs_helpers.c
+ * - Helpers for in-memory filesystems
+ */
+#include <memfs_helpers.h>
+
+// === CODE ===
+void MemFS_InitDir(tMemFS_DirHdr *Dir)
+{
+	MemFS_InitFile(&Dir->FileHdr);
+}
+void MemFS_InitFile(tMemFS_FileHdr *File)
+{
+	File->Next = NULL;
+}
+
+int MemFS_ReadDir(tMemFS_DirHdr *Dir, int Pos, char Name[FILENAME_MAX])
+{
+	 int	i = 0;
+	// TODO: Lock
+	for( tMemFS_FileHdr *file = Dir->FirstChild; file; file = file->Next, i ++ )
+	{
+		if( i == Pos )
+		{
+			strncpy(Name, file->Name, FILENAME_MAX);
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+tMemFS_FileHdr *MemFS_FindDir(tMemFS_DirHdr *Dir, const char *Name)
+{
+	// TODO: Lock
+	for( tMemFS_FileHdr *file = Dir->FirstChild; file; file = file->Next )
+	{
+		if( strcmp(file->Name, Name) == 0 )
+			return file;
+	}
+	return NULL;
+}
+tMemFS_FileHdr *MemFS_Remove(tMemFS_DirHdr *Dir, const char *Name)
+{
+	UNIMPLEMENTED();
+	return NULL;
+}
+bool MemFS_Insert(tMemFS_DirHdr *Dir, tMemFS_FileHdr *File)
+{
+	UNIMPLEMENTED();
+	return false;
+}
+
diff --git a/KernelLand/Kernel/rwlock.c b/KernelLand/Kernel/rwlock.c
index c39ce10abcef7037a1e40daf7b1bb6d2d78d1977..1fd64592325ee191dd2f54eb69a1ba792d121306 100644
--- a/KernelLand/Kernel/rwlock.c
+++ b/KernelLand/Kernel/rwlock.c
@@ -21,7 +21,8 @@ int RWLock_AcquireRead(tRWLock *Lock)
 	SHORTLOCK( &Lock->Protector );
 	
 	// Check if the lock is already held by a writer
-	if( Lock->Owner )
+	// - OR, there's a writer waiting to write
+	if( Lock->Owner || Lock->WriterWaiting )
 	{
 		LOG("Waiting");
 		Threads_int_Sleep(THREAD_STAT_RWLOCKSLEEP, Lock, 0,
diff --git a/KernelLand/Kernel/syscalls.c b/KernelLand/Kernel/syscalls.c
index d2f91858a6d2f91c02491f6af2ef6c49e9d7108f..7b4855ce5e2c6d8b852c714235fe1e2adf54a11e 100644
--- a/KernelLand/Kernel/syscalls.c
+++ b/KernelLand/Kernel/syscalls.c
@@ -37,6 +37,12 @@
 	if(tmp[i]) break;\
 } while(0)
 
+#if BITS==64
+#define ARG64(idx1,idx2)	***ARG64 not used on 64-bit***
+#else
+#define ARG64(idx1, idx2)	(Regs->Arg##idx1|(((Uint64)Regs->Arg##idx2)<<32))
+#endif
+
 // === IMPORTS ===
 extern Uint	Binary_Load(const char *file, Uint *entryPoint);
 
@@ -250,7 +256,7 @@ void SyscallHandler(tSyscallRegs *Regs)
 		#if BITS == 64
 		ret = VFS_Seek( Regs->Arg1, Regs->Arg2, Regs->Arg3 );
 		#else
-		ret = VFS_Seek( Regs->Arg1, Regs->Arg2|(((Uint64)Regs->Arg3)<<32), Regs->Arg4 );
+		ret = VFS_Seek( Regs->Arg1, ARG64(2, 3), Regs->Arg4 );
 		#endif
 		break;
 		
@@ -262,11 +268,41 @@ void SyscallHandler(tSyscallRegs *Regs)
 		CHECK_NUM_NONULL( (void*)Regs->Arg2, Regs->Arg3 );
 		ret = VFS_Write( Regs->Arg1, Regs->Arg3, (void*)Regs->Arg2 );
 		break;
+	case SYS_WRITEAT:
+		#if BITS == 64
+		CHECK_NUM_NONULL( (void*)Regs->Arg5, Regs->Arg3 );
+		ret = VFS_WriteAt( Regs->Arg1, Regs->Arg2, Regs->Arg3, (void*)Regs->Arg4 );
+		#else
+		CHECK_NUM_NONULL( (void*)Regs->Arg5, Regs->Arg4 );
+		Debug("VFS_WriteAt(%i, %lli, %i, %p)",
+			Regs->Arg1, ARG64(2, 3), Regs->Arg4, (void*)Regs->Arg5);
+		ret = VFS_WriteAt( Regs->Arg1, ARG64(2, 3), Regs->Arg4, (void*)Regs->Arg5 );
+		#endif
+		break;
 	
 	case SYS_READ:
 		CHECK_NUM_NONULL( (void*)Regs->Arg2, Regs->Arg3 );
 		ret = VFS_Read( Regs->Arg1, Regs->Arg3, (void*)Regs->Arg2 );
 		break;
+	case SYS_READAT:
+		CHECK_NUM_NONULL( (void*)Regs->Arg5, Regs->Arg2 );
+		#if BITS == 64
+		ret = VFS_ReadAt( Regs->Arg1, Regs->Arg2, Regs->Arg3, (void*)Regs->Arg4 );
+		#else
+		ret = VFS_ReadAt( Regs->Arg1, Regs->Arg2, ARG64(3, 4), (void*)Regs->Arg5 );
+		#endif
+		break;
+	
+	case SYS_TRUNCATE:
+		ret = VFS_Truncate(
+			Regs->Arg1,
+			#if BITS == 32
+			ARG64(2,3)
+			#else
+			Regs->Arg2
+			#endif
+			);
+		break;
 	
 	case SYS_FINFO:
 		CHECK_NUM_NONULL( (void*)Regs->Arg2, sizeof(tFInfo) + Regs->Arg3*sizeof(tVFS_ACL) );
@@ -369,6 +405,21 @@ void SyscallHandler(tSyscallRegs *Regs)
 			);
 		break;
 
+	case SYS_MMAP:
+		ret = (tVAddr)VFS_MMap(
+			(void*)Regs->Arg1, Regs->Arg2,
+			Regs->Arg3&0xFFFF, Regs->Arg3>>16,
+			Regs->Arg4,
+			#if BITS == 32
+			ARG64(5,6)
+			#else
+			Regs->Arg5
+			#endif
+			);
+		break;
+	case SYS_MUNMAP:
+		ret = VFS_MUnmap( (void*)Regs->Arg1, Regs->Arg2 );
+		break;
 	
 	// Create a directory
 	case SYS_MKDIR:
@@ -378,16 +429,37 @@ void SyscallHandler(tSyscallRegs *Regs)
 	
 	case SYS_UNLINK:
 		Log_Error("Syscalls", "TODO: Impliment SYS_UNLINK");
-		// Fall
+		break;
+	
+	case SYS_MARSHALFD:
+		ret = VFS_MarshalHandle(Regs->Arg1);
+		break;
+	case SYS_UNMARSHALFD:
+		#if BITS == 64
+		ret = VFS_UnmarshalHandle( Regs->Arg1 );
+		#else
+		ret = VFS_UnmarshalHandle( ARG64(1,2) );
+		#endif
+		break;
+
 	// -- Debug
 	//#if DEBUG_BUILD
-	case SYS_DEBUG:
+	case SYS_DEBUGS:
+		CHECK_STR_NONULL( (char*)Regs->Arg1 );
+		Log("Log: %08lli [%i] %s", now(), Threads_GetTID(), (const char*)Regs->Arg1);
+		break;
+	case SYS_DEBUGF:
 		CHECK_STR_NONULL( (char*)Regs->Arg1 );
 		LogF("Log: %08lli [%i] ", now(), Threads_GetTID());
 		LogF((const char*)Regs->Arg1,
 			Regs->Arg2, Regs->Arg3, Regs->Arg4, Regs->Arg5, Regs->Arg6);
 		LogF("\r\n");
 		break;
+	case SYS_DEBUGHEX:
+		CHECK_STR_NONULL( (char*)Regs->Arg1 );
+		CHECK_NUM_NONULL( (void*)Regs->Arg2, Regs->Arg3 );
+		Debug_HexDump( (const char*)Regs->Arg1, (void*)Regs->Arg2, Regs->Arg3 );
+		break;
 	//#endif
 	
 	// -- Default (Return Error)
diff --git a/KernelLand/Kernel/syscalls.lst b/KernelLand/Kernel/syscalls.lst
index 42ae03c312a063c1a685bde4c4b99d7501e388ef..bea821b5392a15d4931eaaba23cbe425e1008b28 100644
--- a/KernelLand/Kernel/syscalls.lst
+++ b/KernelLand/Kernel/syscalls.lst
@@ -53,7 +53,10 @@ SYS_CLOSE	Close a file
 SYS_COPYFD	Create a copy of a file handle
 SYS_FDCTL	Modify flags of a file descriptor
 SYS_READ	Read from an open file
+SYS_READAT	Read from an open file (with offset)
 SYS_WRITE	Write to an open file
+SYS_WRITEAT	Write to an open file (with offset)
+SYS_TRUNCATE	Change the size of an open file
 SYS_IOCTL	Perform an IOCtl Call
 SYS_SEEK	Seek to a new position in the file
 SYS_READDIR	Read from an open directory
@@ -69,3 +72,8 @@ SYS_CHDIR	Change current directory
 SYS_GETCWD	Get current directory
 SYS_MOUNT	Mount a filesystem
 SYS_SELECT	Wait for file handles
+SYS_MMAP	Map a file into this address space
+SYS_MUNMAP	Unmap a file
+SYS_MARSHALFD	Create a reference to a FD suitable for handing to another process
+SYS_UNMARSHALFD	Accept a marshaled FD
+
diff --git a/KernelLand/Kernel/system.c b/KernelLand/Kernel/system.c
index 0c701c33cff61b57a5221bc2ed19d49b8ffac2b2..9892af3725deb6c964be7f1ea30e2f8a602101f5 100644
--- a/KernelLand/Kernel/system.c
+++ b/KernelLand/Kernel/system.c
@@ -14,6 +14,7 @@ extern int	Modules_LoadBuiltins(void);
 extern void	Modules_SetBuiltinParams(char *Name, char *ArgString);
 extern void	Debug_SetKTerminal(const char *File);
 extern void	Timer_CallbackThread(void *);
+extern void	EmergencyConsole(void);
 
 // === PROTOTYPES ===
 void	System_Init(char *Commandline);
@@ -22,6 +23,7 @@ void	System_ExecuteCommandLine(void);
 void	System_ParseVFS(char *Arg);
 void	System_ParseModuleArgs(char *Arg);
 void	System_ParseSetting(char *Arg);
+void	System_EmergencyConsole(void);
 
 // === GLOBALS ===
 const char	*gsInitBinary = "/Acess/SBin/init";
@@ -41,6 +43,7 @@ void System_Init(char *CommandLine)
 	Modules_LoadBuiltins();
 	Arch_LoadBootModules();
 	
+	Log_Log("Config", "Running command line '%s", CommandLine);
 	System_ExecuteCommandLine();
 	
 	// - Execute the Config Script
@@ -52,12 +55,24 @@ void System_Init(char *CommandLine)
 		VFS_Open("/Devices/pts/vt0", VFS_OPENFLAG_WRITE|VFS_OPENFLAG_USER);	// 1: stdout
 		VFS_DuplicateFD(1, 2);	// 2: stderr
 		Proc_Execve(gsInitBinary, args, &args[1], 0);
+		
+		System_EmergencyConsole();
+		
 		Log_KernelPanic("System", "Unable to spawn init '%s'", gsInitBinary);
 	}
 	
 	// Set the debug to be echoed to the terminal
 	Log_Log("Config", "Kernel now echoes to VT7 (Ctrl-Alt-F8)");
 	Debug_SetKTerminal("/Devices/pts/vt7");
+	
+	// Run a thread to reap unowned threads
+	for( ;; )
+	{
+		 int	status;
+		// TODO: Inform init when a thread dies
+		int tid = Threads_WaitTID(-1, &status);
+		Log_Debug("Thread0", "Thread %i exited with status %i", tid, status);
+	}
 }
 
 /**
@@ -265,3 +280,8 @@ void System_ParseSetting(char *Arg)
 	}
 }
 
+void System_EmergencyConsole(void)
+{
+	// TODO: Support an emergency kernel-land console (with FS viewing support)
+	EmergencyConsole();
+}
diff --git a/KernelLand/Kernel/threads.c b/KernelLand/Kernel/threads.c
index 545896c4435b77b78a1ede666420fdd2ea3d5a28..469c5ec18dd3beae6afe01d71d9f4c14a1a67009 100644
--- a/KernelLand/Kernel/threads.c
+++ b/KernelLand/Kernel/threads.c
@@ -4,6 +4,7 @@
  * threads.c
  * - Common Thread Control
  */
+#define DEBUG	0
 #include <acess.h>
 #include <threads.h>
 #include <threads_int.h>
@@ -198,7 +199,7 @@ void Threads_Delete(tThread *Thread)
 			proc->Next->Prev = proc->Prev;
 
 		// VFS Cleanup
-		VFS_CloseAllUserHandles();
+		VFS_CloseAllUserHandles( proc );
 		// Architecture cleanup
 		Proc_ClearProcess( proc );
 		// VFS Configuration strings
@@ -329,11 +330,10 @@ void Threads_SetPriority(tThread *Thread, int Pri)
  */
 tThread *Threads_CloneTCB(Uint Flags)
 {
-	tThread	*cur, *new;
-	cur = Proc_GetCurThread();
+	tThread *cur = Proc_GetCurThread();
 	
 	// Allocate and duplicate
-	new = malloc(sizeof(tThread));
+	tThread *new = malloc(sizeof(tThread));
 	if(new == NULL) { errno = -ENOMEM; return NULL; }
 	memcpy(new, cur, sizeof(tThread));
 	
@@ -480,18 +480,26 @@ tTID Threads_WaitTID(int TID, int *Status)
 		tTID	ret = -1;
 		if( ev & THREAD_EVENT_DEADCHILD )
 		{
+			tThread	* const us = Proc_GetCurThread();
 			// A child died, get the TID
-			tThread	*us = Proc_GetCurThread();
 			ASSERT(us->LastDeadChild);
-			ret = us->LastDeadChild->TID;
+			tThread	*dead_thread = us->LastDeadChild;
+			us->LastDeadChild = dead_thread->Next;
+			if( us->LastDeadChild )
+				Threads_PostEvent( us, THREAD_EVENT_DEADCHILD );
+			else
+				Threads_ClearEvent( THREAD_EVENT_DEADCHILD );
+			Mutex_Release(&us->DeadChildLock);
+			
+			ret = dead_thread->TID;
 			// - Mark as dead (as opposed to undead)
-			ASSERT(us->LastDeadChild->Status == THREAD_STAT_ZOMBIE);
-			us->LastDeadChild->Status = THREAD_STAT_DEAD;
+			ASSERTC(dead_thread->Status, ==, THREAD_STAT_ZOMBIE);
+			dead_thread->Status = THREAD_STAT_DEAD;
 			// - Set return status
 			if(Status)
-				*Status = us->LastDeadChild->RetStatus;
-			us->LastDeadChild = NULL;
-			Mutex_Release(&us->DeadChildLock);
+				*Status = dead_thread->RetStatus;
+			
+			Threads_Delete( dead_thread );
 		}
 		else
 		{
@@ -722,9 +730,11 @@ void Threads_Kill(tThread *Thread, int Status)
 	SHORTREL( &glThreadListLock );
 	// TODO: It's possible that we could be timer-preempted here, should disable that... somehow
 	Mutex_Acquire( &Thread->Parent->DeadChildLock );	// released by parent
+	Thread->Next = Thread->Parent->LastDeadChild;
 	Thread->Parent->LastDeadChild = Thread;
 	Threads_PostEvent( Thread->Parent, THREAD_EVENT_DEADCHILD );
 	
+	// Process cleanup happens on reaping
 	Log("Thread %i went *hurk* (%i)", Thread->TID, Status);
 	
 	// And, reschedule
@@ -750,6 +760,7 @@ void Threads_Yield(void)
 void Threads_int_WaitForStatusEnd(enum eThreadStatus Status)
 {
 	tThread	*us = Proc_GetCurThread();
+	LOG("us = %p(%i %s), status=%i", us, us->TID, us->ThreadName, Status);
 	ASSERT(Status != THREAD_STAT_ACTIVE);
 	ASSERT(Status != THREAD_STAT_DEAD);
 	while( us->Status == Status )
@@ -1246,17 +1257,20 @@ int *Threads_GetErrno(void)
 }
 
 // --- Configuration ---
-int *Threads_GetMaxFD(void)
+int *Threads_GetMaxFD(tProcess *Process)
 {
-	return &Proc_GetCurThread()->Process->MaxFD;
+	if(!Process)	Process = Proc_GetCurThread()->Process;
+	return &Process->MaxFD;
 }
-char **Threads_GetChroot(void)
+char **Threads_GetChroot(tProcess *Process)
 {
-	return &Proc_GetCurThread()->Process->RootDir;
+	if(!Process)	Process = Proc_GetCurThread()->Process;
+	return &Process->RootDir;
 }
-char **Threads_GetCWD(void)
+char **Threads_GetCWD(tProcess *Process)
 {
-	return &Proc_GetCurThread()->Process->CurrentWorkingDir;
+	if(!Process)	Process = Proc_GetCurThread()->Process;
+	return &Process->CurrentWorkingDir;
 }
 // ---
 
@@ -1314,22 +1328,16 @@ void Threads_int_DumpThread(tThread *thread)
  */
 void Threads_DumpActive(void)
 {
-	tThread	*thread;
-	tThreadList	*list;
-	#if SCHEDULER_TYPE == SCHED_RR_PRI
-	 int	i;
-	#endif
-	
 	Log("Active Threads: (%i reported)", giNumActiveThreads);
 	
 	#if SCHEDULER_TYPE == SCHED_RR_PRI
-	for( i = 0; i < MIN_PRIORITY+1; i++ )
+	for( int i = 0; i < MIN_PRIORITY+1; i++ )
 	{
-		list = &gaActiveThreads[i];
+		tThreadList *list = &gaActiveThreads[i];
 	#else
-		list = &gActiveThreads;
+		tThreadList *list = &gActiveThreads;
 	#endif
-		for(thread=list->Head;thread;thread=thread->Next)
+		for(tThread *thread = list->Head; thread; thread = thread->Next)
 		{
 			Threads_int_DumpThread(thread);
 			if(thread->Status != THREAD_STAT_ACTIVE)
@@ -1465,6 +1473,7 @@ tThread *Threads_int_GetRunnable(void)
 	// Single-list round-robin
 	// -----------------------------------
 	tThread *thread = gActiveThreads.Head;
+	LOG("thread = %p", thread);
 	if( thread )
 	{
 		gActiveThreads.Head = thread->Next;
@@ -1486,23 +1495,20 @@ tThread *Threads_int_GetRunnable(void)
  */
 tThread *Threads_GetNextToRun(int CPU, tThread *Last)
 {
-	// If this CPU has the lock, we must let it complete
-	if( CPU_HAS_LOCK( &glThreadListLock ) )
-		return Last;
+	ASSERT( CPU_HAS_LOCK(&glThreadListLock) );
 	
 	// Don't change threads if the current CPU has switches disabled
-	if( gaThreads_NoTaskSwitch[CPU] )
+	if( gaThreads_NoTaskSwitch[CPU] ) {
+		LOG("- Denied");
 		return Last;
-
-	// Lock thread list
-	SHORTLOCK( &glThreadListLock );
+	}
 	
 	// Make sure the current (well, old) thread is marked as de-scheduled	
 	if(Last)	Last->CurCPU = -1;
 
 	// No active threads, just take a nap
 	if(giNumActiveThreads == 0) {
-		SHORTREL( &glThreadListLock );
+		LOG("- No active");
 		#if DEBUG_TRACE_TICKETS
 		Log("No active threads");
 		#endif
@@ -1545,7 +1551,7 @@ tThread *Threads_GetNextToRun(int CPU, tThread *Last)
 
 	// Call actual scheduler	
 	tThread	*thread = Threads_int_GetRunnable();
-		
+	
 	// Anything to do?
 	if( thread )
 	{
@@ -1571,8 +1577,6 @@ tThread *Threads_GetNextToRun(int CPU, tThread *Last)
 		Warning("No runnable thread for CPU%i", CPU);
 	}
 	
-	SHORTREL( &glThreadListLock );
-	
 	return thread;
 }
 
diff --git a/KernelLand/Kernel/time.c b/KernelLand/Kernel/time.c
index 15b61907216536d3af5b50b178c8ace8866f431d..478c41f3d1410070e3bea1ddd922f1fd5eab70e1 100644
--- a/KernelLand/Kernel/time.c
+++ b/KernelLand/Kernel/time.c
@@ -76,8 +76,9 @@ void Timer_CallTimers()
 {
 	// Tick the random number generator every time timers are checked
 	rand();
-	
+
 	SHORTLOCK(&gTimers_ListLock);
+	LOG("gTimers = %p (%lli ms)", gTimers, (gTimers ? gTimers->FiresAfter : 0));
 	while( gTimers && gTimers->FiresAfter < now() )
 	{
 		ASSERT( gTimers != gTimers->Next );	
diff --git a/KernelLand/Kernel/vfs/handle.c b/KernelLand/Kernel/vfs/handle.c
index 661ced8124adf7df94b85f09bd7600a6a81ad2fc..d500aa91b775d5edddab8515cd12fc7233d7caea 100644
--- a/KernelLand/Kernel/vfs/handle.c
+++ b/KernelLand/Kernel/vfs/handle.c
@@ -39,7 +39,7 @@ tVFS_Handle *VFS_GetHandle(int FD)
 		if(FD >= MAX_KERNEL_FILES)	return NULL;
 		h = &gaKernelHandles[ FD ];
 	} else {
-		if(FD >= *Threads_GetMaxFD())	return NULL;
+		if(FD >= *Threads_GetMaxFD(NULL))	return NULL;
 		h = &gaUserHandles[ FD ];
 	}
 	
@@ -59,7 +59,7 @@ int VFS_SetHandle(int FD, tVFS_Node *Node, int Mode)
 		h = &gaKernelHandles[FD];
 	}
 	else {
-		if( FD >= *Threads_GetMaxFD())	return -1;
+		if( FD >= *Threads_GetMaxFD(NULL))	return -1;
 		h = &gaUserHandles[FD];
 	}
 	h->Node = Node;
@@ -72,7 +72,7 @@ int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode)
 	// Check for a user open
 	if(bIsUser)
 	{
-		 int	max_handles = *Threads_GetMaxFD();
+		 int	max_handles = *Threads_GetMaxFD(NULL);
 		// Allocate Buffer
 		if( MM_GetPhysAddr( gaUserHandles ) == 0 )
 		{
@@ -132,14 +132,13 @@ int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode)
 
 void VFS_ReferenceUserHandles(void)
 {
-	 int	i;
-	 int	max_handles = *Threads_GetMaxFD();
+	const int	max_handles = *Threads_GetMaxFD(NULL);
 
 	// Check if this process has any handles
 	if( MM_GetPhysAddr( gaUserHandles ) == 0 )
 		return ;
 	
-	for( i = 0; i < max_handles; i ++ )
+	for( int i = 0; i < max_handles; i ++ )
 	{
 		tVFS_Handle	*h;
 		h = &gaUserHandles[i];
@@ -150,22 +149,34 @@ void VFS_ReferenceUserHandles(void)
 	}
 }
 
-void VFS_CloseAllUserHandles(void)
+void VFS_CloseAllUserHandles(struct sProcess *Process)
 {
-	 int	max_handles = *Threads_GetMaxFD();
+	const int	max_handles = *Threads_GetMaxFD(Process);
+	ENTER("pProcess", Process);
 
+	if( max_handles >= PAGE_SIZE / sizeof(tVFS_Handle) )
+		TODO("More than a page of handles");
+
+	tVFS_Handle	*handles = MM_MapTempFromProc(Process, gaUserHandles);
+	LOG("handles=%p", handles);
 	// Check if this process has any handles
-	if( MM_GetPhysAddr( gaUserHandles ) == 0 )
+	if( !handles ) {
+		LEAVE('-');
 		return ;
+	}
 	
 	for( int i = 0; i < max_handles; i ++ )
 	{
-		tVFS_Handle	*h;
-		h = &gaUserHandles[i];
+		tVFS_Handle	*h = &handles[i];
+		LOG("handles[%i].Node = %p", i, h->Node);
 		if( !h->Node )
 			continue ;
 		_CloseNode(h->Node);
+		h->Node = NULL;
 	}
+	
+	MM_FreeTemp(handles);
+	LEAVE('-');
 }
 
 /**
@@ -174,7 +185,7 @@ void VFS_CloseAllUserHandles(void)
 void *VFS_SaveHandles(int NumFDs, int *FDs)
 {
 	tVFS_Handle	*ret;
-	 int	max_handles = *Threads_GetMaxFD();
+	const int	max_handles = *Threads_GetMaxFD(NULL);
 	
 	// Check if this process has any handles
 	if( MM_GetPhysAddr( gaUserHandles ) == 0 )
@@ -235,11 +246,16 @@ void *VFS_SaveHandles(int NumFDs, int *FDs)
 void VFS_RestoreHandles(int NumFDs, void *Handles)
 {
 	tVFS_Handle	*handles = Handles;
-	 int	max_handles = *Threads_GetMaxFD();
+	const int	max_handles = *Threads_GetMaxFD(NULL);
 
 	// NULL = nothing to do
 	if( !Handles )
-		return ;	
+		return ;
+	
+	if( NumFDs > max_handles ) {
+		Log_Notice("VFS", "RestoreHandles: Capping from %i FDs to %i", NumFDs, max_handles);
+		NumFDs = max_handles;
+	}
 
 	// Allocate user handle area (and dereference existing handles)
 	for( int i = 0; i < NumFDs; i ++ )
diff --git a/KernelLand/Kernel/vfs/io.c b/KernelLand/Kernel/vfs/io.c
index 271e920de7919d49ab8b6964aa597e6799d9a20f..35c1ebf88bec10504cd8fc974da690c3e75f5e4c 100644
--- a/KernelLand/Kernel/vfs/io.c
+++ b/KernelLand/Kernel/vfs/io.c
@@ -100,16 +100,32 @@ size_t VFS_ReadAt(int FD, Uint64 Offset, size_t Length, void *Buffer)
  */
 size_t VFS_Write(int FD, size_t Length, const void *Buffer)
 {
-	tVFS_Handle	*h;
-	size_t	ret;
-	
-	h = VFS_GetHandle(FD);
+	tVFS_Handle	*h = VFS_GetHandle(FD);
 	if(!h) {
 		LOG("FD%i is not open", FD);
 		errno = EBADF;
 		return -1;
 	}
 	
+	size_t ret = VFS_WriteAt(FD, h->Position, Length, Buffer);
+	if(ret == (size_t)-1)	return -1;
+
+	if( !(h->Node->Type->Flags & VFS_NODETYPEFLAG_STREAM) )
+		h->Position += ret;
+	return ret;
+}
+
+/**
+ * \fn Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer)
+ * \brief Write data to a file at a given offset
+ */
+size_t VFS_WriteAt(int FD, Uint64 Offset, size_t Length, const void *Buffer)
+{
+	LOG("FD=%i,Offset=%lli,Length=%i,Buffer=%p",
+		FD, Offset, Length, Buffer);
+	tVFS_Handle	*h = VFS_GetHandle(FD);
+	if(!h)	return -1;
+	
 	if( !(h->Mode & VFS_OPENFLAG_WRITE) ) {
 		LOG("FD%i not opened for writing", FD);
 		errno = EBADF;
@@ -121,57 +137,63 @@ size_t VFS_Write(int FD, size_t Length, const void *Buffer)
 		return -1;
 	}
 
-	if( !h->Node->Type || !h->Node->Type->Write ) {
+	const tVFS_NodeType*	nodetype = h->Node->Type;
+	if(!nodetype || !nodetype->Write) {
 		LOG("FD%i has no write method", FD);
 		errno = EINTERNAL;
 		return 0;
 	}
-
-	if( !MM_GetPhysAddr(h->Node->Type->Write) ) {
+	
+	if( !MM_GetPhysAddr(nodetype->Write) ) {
 		Log_Error("VFS", "Node type %p(%s) write method is junk %p",
-			h->Node->Type, h->Node->Type->TypeName,
-			h->Node->Type->Write);
+			nodetype, nodetype->TypeName, nodetype->Write);
 		errno = EINTERNAL;
 		return -1;
 	}
 	
-	Uint flags = 0;
-	flags |= (h->Mode & VFS_OPENFLAG_NONBLOCK) ? VFS_IOFLAG_NOBLOCK : 0;
-	ret = h->Node->Type->Write(h->Node, h->Position, Length, Buffer, flags);
-	if(ret != Length)	LOG("%i/%i written", ret, Length);
-	if(ret == (size_t)-1)	return -1;
-
-	h->Position += ret;
-	return ret;
-}
-
-/**
- * \fn Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer)
- * \brief Write data to a file at a given offset
- */
-size_t VFS_WriteAt(int FD, Uint64 Offset, size_t Length, const void *Buffer)
-{
-	tVFS_Handle	*h;
-	size_t	ret;
-	
-	h = VFS_GetHandle(FD);
-	if(!h)	return -1;
-	
-	if( !(h->Mode & VFS_OPENFLAG_WRITE) )	return -1;
-	if( h->Node->Flags & VFS_FFLAG_DIRECTORY )	return -1;
-
-	if(!h->Node->Type || !h->Node->Type->Write)	return 0;
-
-	if( !MM_GetPhysAddr(h->Node->Type->Write) ) {
-		Log_Error("VFS", "Node type %p(%s) write method is junk %p",
-			h->Node->Type, h->Node->Type->TypeName,
-			h->Node->Type->Write);
-		return -1;
+	// Bounds checks
+	if( h->Node->Size != (Uint64)-1 && Offset > h->Node->Size ) {
+		Log_Notice("VFS", "Write starting past EOF of FD%x (%lli > %lli)",
+			FD, Offset, h->Node->Size);
+		//errno = ESPIPE;
+		return 0;
+	}
+	if( Offset + Length > h->Node->Size )
+	{
+		// Going OOB
+		if( !nodetype->Truncate )
+		{
+			LOG("No .Truncate method, emiting write past EOF");
+		}
+		else if( nodetype->Flags & VFS_NODETYPEFLAG_NOAUTOEXPAND )
+		{
+			LOG("NOAUTOEXPAND set, truncating length from %i to %i",
+				Length, h->Node->Size - Offset);
+			Length = h->Node->Size - Offset;
+		}
+		else if( nodetype->Truncate(h->Node, Offset + Length) != Offset + Length )
+		{
+			// oh... fail? Truncate to current size
+			LOG(".Truncate failed, truncating length from %i to %i",
+				Length, h->Node->Size - Offset);
+			Length = h->Node->Size - Offset;
+		}
+		else
+		{
+			// Expansion, node size should now fit
+			LOG("Expanded file");
+		}
 	}
+	
+	// Create flag set
 	Uint flags = 0;
 	flags |= (h->Mode & VFS_OPENFLAG_NONBLOCK) ? VFS_IOFLAG_NOBLOCK : 0;
-	ret = h->Node->Type->Write(h->Node, Offset, Length, Buffer, flags);
+	
+	// Dispatch the read!
+	size_t ret = nodetype->Write(h->Node, Offset, Length, Buffer, flags);
 	if(ret == (size_t)-1)	return -1;
+	if(ret != Length)	LOG("%i/%i written", ret, Length);
+
 	return ret;
 }
 
@@ -198,11 +220,14 @@ Uint64 VFS_Tell(int FD)
  */
 int VFS_Seek(int FD, Sint64 Offset, int Whence)
 {
-	tVFS_Handle	*h;
-	
-	h = VFS_GetHandle(FD);
+	tVFS_Handle	*h = VFS_GetHandle(FD);
 	if(!h)	return -1;
 	
+	if( (h->Node->Type->Flags & VFS_NODETYPEFLAG_STREAM) ) {
+		LOG("Seeking in stream");
+		return -1;
+	}
+	
 	// Set relative to current position
 	if(Whence == 0) {
 		LOG("(FD%x)->Position += %lli", FD, Offset);
@@ -225,15 +250,34 @@ int VFS_Seek(int FD, Sint64 Offset, int Whence)
 	return 0;
 }
 
+/*
+ * Truncate/Expand a file's allocation
+ */
+off_t VFS_Truncate(int FD, off_t Size)
+{
+	tVFS_Handle	*h = VFS_GetHandle(FD);
+	if(!h) {
+		errno = EBADF;
+		return -1;
+	}
+	
+	if( !h->Node->Type->Truncate)
+	{
+		Log_Notice("VFS", "Nodetype '%s' doesn't have a Truncate method", h->Node->Type->TypeName);
+		errno = ENOTIMPL;
+		return -1;	
+	}
+	
+	return h->Node->Type->Truncate(h->Node, Size);
+}
+
 /**
  * \fn int VFS_IOCtl(int FD, int ID, void *Buffer)
  * \brief Call an IO Control on a file
  */
 int VFS_IOCtl(int FD, int ID, void *Buffer)
 {
-	tVFS_Handle	*h;
-	
-	h = VFS_GetHandle(FD);
+	tVFS_Handle	*h = VFS_GetHandle(FD);
 	if(!h) {
 		LOG("FD%i is invalid", FD);
 		errno = EINVAL;
diff --git a/KernelLand/Kernel/vfs/main.c b/KernelLand/Kernel/vfs/main.c
index 0261eca3b9c3598ec913c6c1329a602a085934af..4ed782982d9a401cda595ba43da47f4fa71fd12a 100644
--- a/KernelLand/Kernel/vfs/main.c
+++ b/KernelLand/Kernel/vfs/main.c
@@ -59,9 +59,10 @@ int VFS_Init(void)
 	VFS_MkDir("/Devices");
 	VFS_MkDir("/Mount");
 	VFS_Mount("dev", "/Devices", "devfs", "");
-		
-	Log_Debug("VFS", "Setting max files");
-	*Threads_GetMaxFD() = 32;
+	
+	// Set default max user file count
+	// - Applies to PID0, but propagated to all children
+	*Threads_GetMaxFD(NULL) = 32;
 	return 0;
 }
 
diff --git a/KernelLand/Kernel/vfs/mmap.c b/KernelLand/Kernel/vfs/mmap.c
index b6d5c2d91921425bb8864cb04dd797e91776526a..9016d564bd921f7c3d13090c35e9aaac8d65e349 100644
--- a/KernelLand/Kernel/vfs/mmap.c
+++ b/KernelLand/Kernel/vfs/mmap.c
@@ -10,6 +10,7 @@
 #include <vfs.h>
 #include <vfs_ext.h>
 #include <vfs_int.h>
+#include <mm_virt.h>	// MM_USER_MAX
 
 #define MMAP_PAGES_PER_BLOCK	16
 
@@ -25,31 +26,63 @@ struct sVFS_MMapPageBlock
 // === PROTOTYPES ===
 //void	*VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset);
 void	*VFS_MMap_Anon(void *Destination, size_t Length, Uint FlagsSet, Uint FlagsMask);
+int	VFS_MMap_MapPage(tVFS_Node *Node, unsigned int PageNum, tVFS_MMapPageBlock *pb, void *mapping_dest, unsigned int Protection);
 //int	VFS_MUnmap(void *Addr, size_t Length);
+bool	_range_free(const tPage *Base, Uint NumPages);
 
 // === CODE ===
 void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset)
 {
-	tVAddr	mapping_base;
-	 int	npages, pagenum;
-	tVFS_MMapPageBlock	*pb, *prev;
-
-	ENTER("pDestHint iLength xProtection xFlags xFD XOffset", DestHint, Length, Protection, Flags, FD, Offset);
+	ENTER("pDestHint xLength xProtection xFlags xFD XOffset", DestHint, Length, Protection, Flags, FD, Offset);
 
 	if( Flags & MMAP_MAP_ANONYMOUS )
 		Offset = (tVAddr)DestHint & 0xFFF;
 	
-	npages = ((Offset & (PAGE_SIZE-1)) + Length + (PAGE_SIZE - 1)) / PAGE_SIZE;
-	pagenum = Offset / PAGE_SIZE;
+	unsigned int npages = ((Offset & (PAGE_SIZE-1)) + Length + (PAGE_SIZE - 1)) / PAGE_SIZE;
+	unsigned int pagenum = Offset / PAGE_SIZE;
+	LOG("npages=%u,pagenum=%u", npages, pagenum);
+
+	tVAddr mapping_base = (tVAddr)DestHint;
 
-	mapping_base = (tVAddr)DestHint;
+	if( Flags & MMAP_MAP_FIXED )
+	{
+		ASSERT( (Flags & MMAP_MAP_FIXED) && DestHint != NULL );
+		// Keep and use the hint
+		// - TODO: Validate that the region pointed to by the hint is correct
+	}
+	else
+	{
+		Log_Warning("VFS", "MMap: TODO Handle non-fixed mappings");
+		
+		// Locate a free location in the address space (between brk and MM_USER_MAX)
+		// TODO: Prefer first location after DestHint, but can go below
+		
+		// Search downwards from the top of user memory
+		mapping_base = 0;
+		for( tPage *dst = (tPage*)MM_USER_MAX - npages; dst > (tPage*)PAGE_SIZE; dst -- )
+		{
+			if( _range_free(dst, npages) ) {
+				mapping_base = (tVAddr)dst;
+				break;
+			}
+		}
+		if( mapping_base == 0 )
+		{
+			Log_Warning("VFS", "MMap: Out of address space");
+			errno = ENOMEM;
+			LEAVE('n');
+			return NULL;
+		}
+	}
 	tPage	*mapping_dest = (void*)(mapping_base & ~(PAGE_SIZE-1));
 
-	if( DestHint == NULL )
+	if( !_range_free(mapping_dest, npages) )
 	{
-		// TODO: Locate space for the allocation
-		LEAVE('n');
-		return NULL;
+		LOG("Specified range is not free");
+		//errno = EINVAL;
+		//LEAVE('n');
+		//return NULL;
+		Log_Warning("VFS", "MMap: Overwriting/replacing maps at %p+%x", mapping_base, Length);
 	}
 
 	// Handle anonymous mappings
@@ -67,14 +100,14 @@ void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD,
 	
 	Mutex_Acquire( &h->Node->Lock );
 
+	tVFS_MMapPageBlock	*pb, **pb_pnp = (tVFS_MMapPageBlock**)&h->Node->MMapInfo;
 	// Search for existing mapping for each page
 	// - Sorted list of 16 page blocks
-	for(
-		pb = h->Node->MMapInfo, prev = NULL;
-		pb && pb->BaseOffset + MMAP_PAGES_PER_BLOCK <= pagenum;
-		prev = pb, pb = pb->Next
-		)
-		;
+	for( pb = h->Node->MMapInfo; pb; pb_pnp = &pb->Next, pb = pb->Next )
+	{
+		if( pb->BaseOffset + MMAP_PAGES_PER_BLOCK > pagenum )
+			break;
+	}
 
 	LOG("pb = %p, pb->BaseOffset = %X", pb, pb ? pb->BaseOffset : 0);
 
@@ -89,74 +122,22 @@ void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD,
 		}
 		pb->Next = old_pb;
 		pb->BaseOffset = pagenum - pagenum % MMAP_PAGES_PER_BLOCK;
-		if(prev)
-			prev->Next = pb;
-		else
-			h->Node->MMapInfo = pb;
+		*pb_pnp = pb;
 	}
 
 	// - Map (and allocate) pages
 	while( npages -- )
 	{
-		assert( pagenum >= pb->BaseOffset );
-		assert( pagenum - pb->BaseOffset < MMAP_PAGES_PER_BLOCK );
+		ASSERTC( pagenum, >=, pb->BaseOffset );
+		ASSERTC( pagenum - pb->BaseOffset, <, MMAP_PAGES_PER_BLOCK );
 		if( MM_GetPhysAddr( mapping_dest ) == 0 )
 		{
-			if( pb->PhysAddrs[pagenum - pb->BaseOffset] == 0 )
-			{
-				tVFS_NodeType	*nt = h->Node->Type;
-				if( !nt ) 
-				{
-					// TODO: error
-				}
-				else if( nt->MMap )
-					nt->MMap(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, mapping_dest);
-				else
-				{
-					 int	read_len;
-					// Allocate pages and read data
-					if( MM_Allocate(mapping_dest) == 0 ) {
-						// TODO: Unwrap
-						Mutex_Release( &h->Node->Lock );
-						LEAVE('n');
-						return NULL;
-					}
-					// TODO: Clip read length
-					read_len = nt->Read(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE,
-						mapping_dest, 0);
-					// TODO: This was commented out, why?
-					if( read_len != PAGE_SIZE ) {
-						memset( (char*)mapping_dest + read_len, 0, PAGE_SIZE-read_len );
-					}
-				}
-				pb->PhysAddrs[pagenum - pb->BaseOffset] = MM_GetPhysAddr( mapping_dest );
-				MM_SetPageNode( pb->PhysAddrs[pagenum - pb->BaseOffset], h->Node );
-				MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] );
-				LOG("Read and map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest,
-					pb->PhysAddrs[pagenum - pb->BaseOffset]);
-			}
-			else
+			LOG("Map page to %p", mapping_dest);
+			if( VFS_MMap_MapPage(h->Node, pagenum, pb, mapping_dest, Protection) )
 			{
-				MM_Map( mapping_dest, pb->PhysAddrs[pagenum - pb->BaseOffset] );
-				MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] );
-				LOG("Cached map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest,
-					pb->PhysAddrs[pagenum - pb->BaseOffset]);
-			}
-			h->Node->ReferenceCount ++;
-		
-			// Set flags
-			if( !(Protection & MMAP_PROT_WRITE) ) {
-				MM_SetFlags(mapping_dest, MM_PFLAG_RO, MM_PFLAG_RO);
-			}
-			else {
-				MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO);
-			}
-			
-			if( Protection & MMAP_PROT_EXEC ) {
-				MM_SetFlags(mapping_dest, MM_PFLAG_EXEC, MM_PFLAG_EXEC);
-			}
-			else {
-				MM_SetFlags(mapping_dest, 0, MM_PFLAG_EXEC);
+				Mutex_Release( &h->Node->Lock );
+				LEAVE('n');
+				return NULL;
 			}
 		}
 		else
@@ -167,25 +148,27 @@ void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD,
 				MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO);
 			}
 		}
-		if( Flags & MMAP_MAP_PRIVATE )
+		if( Flags & MMAP_MAP_PRIVATE ) {
+			// TODO: Don't allow the page to change underneath either
 			MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW);
+		}
 		pagenum ++;
 		mapping_dest ++;
 
 		// Roll on to next block if needed
 		if(pagenum - pb->BaseOffset == MMAP_PAGES_PER_BLOCK)
 		{
-			if( pb->Next && pb->Next->BaseOffset == pagenum )
-				pb = pb->Next;
-			else
+			if( !pb->Next || pb->Next->BaseOffset != pagenum )
 			{
-				tVFS_MMapPageBlock	*oldpb = pb;
-				pb = malloc( sizeof(tVFS_MMapPageBlock) );
-				pb->Next = oldpb->Next;
-				pb->BaseOffset = pagenum;
-				memset(pb->PhysAddrs, 0, sizeof(pb->PhysAddrs));
-				oldpb->Next = pb;
+				if( pb->Next )	ASSERTC(pb->Next->BaseOffset % MMAP_PAGES_PER_BLOCK, ==, 0);
+				tVFS_MMapPageBlock	*newpb = malloc( sizeof(tVFS_MMapPageBlock) );
+				newpb->Next = pb->Next;
+				newpb->BaseOffset = pagenum;
+				memset(newpb->PhysAddrs, 0, sizeof(newpb->PhysAddrs));
+				pb->Next = newpb;
 			}
+	
+			pb = pb->Next;
 		}
 	}
 	
@@ -263,7 +246,81 @@ void *VFS_MMap_Anon(void *Destination, size_t Length, Uint FlagsSet, Uint FlagsM
 	return Destination;
 }
 
+int VFS_MMap_MapPage(tVFS_Node *Node, unsigned int pagenum, tVFS_MMapPageBlock *pb, void *mapping_dest, unsigned int Protection)
+{
+	if( pb->PhysAddrs[pagenum - pb->BaseOffset] != 0 )
+	{
+		MM_Map( mapping_dest, pb->PhysAddrs[pagenum - pb->BaseOffset] );
+		MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] );
+		LOG("Cached map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest,
+			pb->PhysAddrs[pagenum - pb->BaseOffset]);
+	}
+	else
+	{
+		tVFS_NodeType	*nt = Node->Type;
+		if( !nt ) 
+		{
+			// TODO: error
+		}
+		else if( nt->MMap )
+			nt->MMap(Node, pagenum*PAGE_SIZE, PAGE_SIZE, mapping_dest);
+		else
+		{
+			 int	read_len;
+			// Allocate pages and read data
+			if( MM_Allocate(mapping_dest) == 0 ) {
+				// TODO: Unwrap
+				return 1;
+			}
+			// TODO: Clip read length
+			read_len = nt->Read(Node, pagenum*PAGE_SIZE, PAGE_SIZE, mapping_dest, 0);
+			// TODO: This was commented out, why?
+			if( read_len != PAGE_SIZE ) {
+				memset( (char*)mapping_dest + read_len, 0, PAGE_SIZE-read_len );
+			}
+		}
+		pb->PhysAddrs[pagenum - pb->BaseOffset] = MM_GetPhysAddr( mapping_dest );
+		MM_SetPageNode( pb->PhysAddrs[pagenum - pb->BaseOffset], Node );
+		MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] );
+		LOG("Read and map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest,
+			pb->PhysAddrs[pagenum - pb->BaseOffset]);
+	}
+	// TODO: Huh?
+	Node->ReferenceCount ++;
+
+	// Set flags
+	if( !(Protection & MMAP_PROT_WRITE) ) {
+		MM_SetFlags(mapping_dest, MM_PFLAG_RO, MM_PFLAG_RO);
+	}
+	else {
+		MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO);
+	}
+	
+	if( Protection & MMAP_PROT_EXEC ) {
+		MM_SetFlags(mapping_dest, MM_PFLAG_EXEC, MM_PFLAG_EXEC);
+	}
+	else {
+		MM_SetFlags(mapping_dest, 0, MM_PFLAG_EXEC);
+	}
+	
+	return 0;
+}
+
 int VFS_MUnmap(void *Addr, size_t Length)
 {
+	UNIMPLEMENTED();
 	return 0;
 }
+
+bool _range_free(const tPage *Base, Uint NumPages)
+{
+	for( int i = 0; i < NumPages; i ++ )
+	{
+		if( MM_GetPhysAddr(Base + i) )
+		{
+			// Oh.
+			return false;
+		}
+	}
+	return true;
+}
diff --git a/KernelLand/Kernel/vfs/open.c b/KernelLand/Kernel/vfs/open.c
index f0b5734a4ec13696d5e590b64314c0842e090fd8..e5a206fbe3a6376dcc8bb00500cd0c7c8dcf2232 100644
--- a/KernelLand/Kernel/vfs/open.c
+++ b/KernelLand/Kernel/vfs/open.c
@@ -15,16 +15,29 @@
 #define MAX_PATH_SLASHES	256
 #define MAX_NESTED_LINKS	4
 #define MAX_PATH_LEN	255
+#define MAX_MARSHALLED_HANDLES	16	// Max outstanding
 
 // === IMPORTS ===
 extern tVFS_Mount	*gVFS_RootMount;
 extern tVFS_Node	*VFS_MemFile_Create(const char *Path);
 
+// === TYPES ===
+typedef struct sVFS_MarshaledHandle
+{
+	Uint32	Magic;
+	tTime	AllocTime;
+	tVFS_Handle	Handle;
+} tVFS_MarshalledHandle;
+
 // === PROTOTYPES ===
 void	_ReferenceMount(tVFS_Mount *Mount, const char *DebugTag);
 void	_DereferenceMount(tVFS_Mount *Mount, const char *DebugTag);
  int	VFS_int_CreateHandle(tVFS_Node *Node, tVFS_Mount *Mount, int Mode);
 
+// === GLOBALS ===
+tMutex	glVFS_MarshalledHandles;
+tVFS_MarshalledHandle	gaVFS_MarshalledHandles[MAX_MARSHALLED_HANDLES];
+
 // === CODE ===
 void _ReferenceMount(tVFS_Mount *Mount, const char *DebugTag)
 {
@@ -49,9 +62,9 @@ char *VFS_GetAbsPath(const char *Path)
 	char	*tmpStr;
 	int		iPos = 0;
 	int		iPos2 = 0;
-	const char	*chroot = *Threads_GetChroot();
+	const char	*chroot = *Threads_GetChroot(NULL);
 	 int	chrootLen;
-	const char	*cwd = *Threads_GetCWD();
+	const char	*cwd = *Threads_GetCWD(NULL);
 	 int	cwdLen;
 	
 	ENTER("sPath", Path);
@@ -343,7 +356,7 @@ restart_parse:
 			}
 			
 			if(iNestedLinks > MAX_NESTED_LINKS) {
-				Log_Notice("VFS", "VFS_ParsePath - Nested link limit exceeded");
+				Log_Notice("VFS", "VFS_ParsePath - Nested link limit exceeded on '%.*s'", ofs, Path);
 				errno = ENOENT;
 				goto _error;
 			}
@@ -378,7 +391,8 @@ restart_parse:
 		// Handle Non-Directories
 		if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) )
 		{
-			Log_Warning("VFS", "VFS_ParsePath - Path segment is not a directory");
+			Log_Warning("VFS", "VFS_ParsePath - Path segment '%.*s' is not a directory (curNode{%p}->Flags = 0x%x)",
+				ofs+nextSlash, Path, curNode, curNode->Flags);
 			errno = ENOTDIR;
 			goto _error;
 		}
@@ -407,7 +421,7 @@ restart_parse:
 
 	// Check final finddir call	
 	if( !curNode->Type || !curNode->Type->FindDir ) {
-		Log_Warning("VFS", "VFS_ParsePath - FindDir doesn't exist for element of '%s'", Path);
+		Log_Warning("VFS", "VFS_ParsePath - FindDir doesn't exist for leaf of '%s' (dir '%.*s')", Path, ofs, Path);
 		errno = ENOENT;
 		goto _error;
 	}
@@ -733,6 +747,7 @@ int VFS_Reopen(int FD, const char *Path, int Flags)
 
 	int newf = VFS_Open(Path, Flags);
 	if( newf == -1 ) {
+		// errno = set by VFS_Open
 		return -1;
 	}
 	
@@ -759,11 +774,13 @@ void VFS_Close(int FD)
 	h = VFS_GetHandle(FD);
 	if(h == NULL) {
 		Log_Warning("VFS", "Invalid file handle passed to VFS_Close, 0x%x", FD);
+		errno = EINVAL;
 		return;
 	}
 
 	if( h->Node == NULL ) {
 		Log_Warning("VFS", "Non-open handle passed to VFS_Close, 0x%x", FD);
+		errno = EINVAL;
 		return ;
 	}	
 
@@ -771,6 +788,7 @@ void VFS_Close(int FD)
 	if(h->Node->Close && !MM_GetPhysAddr(h->Node->Close)) {
 		Log_Warning("VFS", "Node %p's ->Close method is invalid (%p)",
 			h->Node, h->Node->Close);
+		errno = EINTERNAL;
 		return ;
 	}
 	#endif
@@ -858,7 +876,7 @@ int VFS_ChDir(const char *Dest)
 	VFS_Close(fd);
 	
 	{
-		char	**cwdptr = Threads_GetCWD();
+		char	**cwdptr = Threads_GetCWD(NULL);
 		// Free old working directory
 		if( *cwdptr )	free( *cwdptr );
 		// Set new
@@ -910,7 +928,7 @@ int VFS_ChRoot(const char *New)
 
 	// Update	
 	{
-		char	**chroot_ptr = Threads_GetChroot();
+		char	**chroot_ptr = Threads_GetChroot(NULL);
 		if( *chroot_ptr )	free( *chroot_ptr );
 		*chroot_ptr = buf;
 	}
@@ -920,6 +938,96 @@ int VFS_ChRoot(const char *New)
 	return 1;
 }
 
+/*
+ * Marshal a handle so that it can be transferred between processes
+ */
+Uint64 VFS_MarshalHandle(int FD)
+{
+	tVFS_Handle	*h = VFS_GetHandle(FD);
+	if(!h || !h->Node) {
+		errno = EBADF;
+		return -1;
+	}
+	
+	// Allocate marshal location
+	 int	ret = -1;
+	Mutex_Acquire(&glVFS_MarshalledHandles);
+	for( int i = 0; i < MAX_MARSHALLED_HANDLES; i ++ )
+	{
+		tVFS_MarshalledHandle*	mh = &gaVFS_MarshalledHandles[i];
+		if( mh->Handle.Node == NULL ) {
+			mh->Handle.Node = h->Node;
+			mh->AllocTime = now();
+			ret = i;
+		}
+		if( now() - mh->AllocTime > 2000 ) {
+			Log_Notice("VFS", "TODO: Expire marshalled handle");
+		}
+	}
+	Mutex_Release(&glVFS_MarshalledHandles);
+	if( ret < 0 ) {
+		// TODO: Need to clean up lost handles to avoid DOS
+		Log_Warning("VFS", "Out of marshaled handle slots");
+		errno = EAGAIN;
+		return -1;
+	}
+	
+	// Populate
+	tVFS_MarshalledHandle*	mh = &gaVFS_MarshalledHandles[ret];
+	mh->Handle = *h;
+	_ReferenceMount(h->Mount, "MarshalHandle");
+	_ReferenceNode(h->Node);
+	mh->Magic = rand();
+	
+	return (Uint64)mh->Magic << 32 | ret;
+}
+
+/*
+ * Un-marshal a handle into the current process
+ * NOTE: Does not support unmarshalling into kernel handle list
+ */
+int VFS_UnmarshalHandle(Uint64 Handle)
+{
+	Uint32	magic = Handle >> 32;
+	 int	id = Handle & 0xFFFFFFFF;
+	
+	// Range check
+	if( id >= MAX_MARSHALLED_HANDLES ) {
+		LOG("ID too high (%i > %i)", id, MAX_MARSHALLED_HANDLES);
+		errno = EINVAL;
+		return -1;
+	}
+	
+	
+	// Check validity
+	tVFS_MarshalledHandle	*mh = &gaVFS_MarshalledHandles[id];
+	if( mh->Handle.Node == NULL ) {
+		LOG("Target node is NULL");
+		errno = EINVAL;
+		return -1;
+	}
+	if( mh->Magic != magic ) {
+		LOG("Magic mismatch (0x%08x != 0x%08x)", magic, mh->Magic);
+		errno = EACCES;
+		return -1;
+	}
+	
+	Mutex_Acquire(&glVFS_MarshalledHandles);
+	// - Create destination handle
+	 int	ret = VFS_AllocHandle(true, mh->Handle.Node, mh->Handle.Mode);
+	// - Clear allocation
+	mh->Handle.Node = NULL;
+	Mutex_Release(&glVFS_MarshalledHandles);
+	if( ret == -1 ) {
+		errno = ENFILE;
+		return -1;
+	}
+	
+	// No need to reference node/mount, new handle takes marshalled reference
+	
+	return ret;
+}
+
 // === EXPORTS ===
 EXPORT(VFS_Open);
 EXPORT(VFS_Close);
diff --git a/KernelLand/Kernel/vfs/select.c b/KernelLand/Kernel/vfs/select.c
index 660bca89611731009e210aa53f59500b4d80efa4..a18b91c58dce0fe1a65d7b376f969ca201426403 100644
--- a/KernelLand/Kernel/vfs/select.c
+++ b/KernelLand/Kernel/vfs/select.c
@@ -55,13 +55,14 @@ void	VFS_int_Select_SignalAll(tVFS_SelectList *List);
 // === FUNCTIONS ===
 int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *Name)
 {
-	tThread	*thisthread = Proc_GetCurThread();
-	 int	ret, type;
+	tThread	* const thisthread = Proc_GetCurThread();
+	 int	ret;
 	
 	ENTER("pNode iTypeFlags pTimeout sName", Node, TypeFlags, Timeout, Name);
 	
 	// Initialise
-	for( type = 0; type < 3; type ++ )
+	ret = 0;
+	for( int type = 0; type < 3; type ++ )
 	{
 		tVFS_SelectList	**list;
 		 int	*flag, wanted, maxAllowed;
@@ -77,17 +78,19 @@ int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *N
 		VFS_int_Select_AddThread(*list, thisthread, maxAllowed);
 		if( *flag == wanted )
 		{
-			VFS_int_Select_RemThread(*list, thisthread);
-			LEAVE('i', 1);
-			return 1;
+			ret |= (1 << type);
 		}
 	}
 
 	// Wait for things	
-	if( !Timeout )
+	if( ret )
+	{
+		// Skip wait, conditions already met
+		LOG("ret = %i, skipping wait", ret);
+	}
+	else if( !Timeout )
 	{
 		LOG("Semaphore_Wait()");
-		// TODO: Actual timeout
 		Threads_WaitEvents( THREAD_EVENT_VFS|THREAD_EVENT_SIGNAL );
 	}
 	else if( *Timeout > 0 )
@@ -95,7 +98,6 @@ int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *N
 		tTimer *t = Time_AllocateTimer(NULL, NULL);
 		// Clear timer event
 		Threads_ClearEvent( THREAD_EVENT_TIMER );
-		// TODO: Convert *Timeout?
 		LOG("Timeout %lli ms", *Timeout);
 		Time_ScheduleTimer( t, *Timeout );
 		// Wait for the timer or a VFS event
@@ -105,13 +107,13 @@ int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *N
 	
 	// Get return value
 	ret = 0;
-	for( type = 0; type < 3; type ++ )
+	for( int type = 0; type < 3; type ++ )
 	{
 		tVFS_SelectList	**list;
 		 int	*flag, wanted, maxAllowed;
 		if( !(TypeFlags & (1 << type)) )	continue;
 		VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed);
-		LOG("VFS_int_Select_RemThread()");
+		LOG("VFS_int_Select_RemThread() for %i", type);
 		ASSERT(*list);
 		VFS_int_Select_RemThread(*list, thisthread);
 		ret = ret || *flag == wanted;
diff --git a/KernelLand/Modules/Display/BochsGA/bochsvbe.c b/KernelLand/Modules/Display/BochsGA/bochsvbe.c
index a6338ae10f9ec03ecc72c81185e465e3a91d4247..7674ed9559dd4026d1eb83f437ffaa3a93a16f4c 100644
--- a/KernelLand/Modules/Display/BochsGA/bochsvbe.c
+++ b/KernelLand/Modules/Display/BochsGA/bochsvbe.c
@@ -118,8 +118,15 @@ int BGA_Install(char **Arguments)
 	dev = PCI_GetDevice(0x1234, 0x1111, 0);
 	if(dev == -1)
 		base = VBE_DISPI_LFB_PHYSICAL_ADDRESS;
-	else
-		base = PCI_GetBAR(dev, 0);
+	else {
+		Log_Debug("BGA", "BARs %x,%x,%x,%x,%x,%x",
+			PCI_GetBAR(dev, 0), PCI_GetBAR(dev, 1), PCI_GetBAR(dev, 2),
+			PCI_GetBAR(dev, 3), PCI_GetBAR(dev, 4), PCI_GetBAR(dev, 5));
+		base = PCI_GetValidBAR(dev, 0, PCI_BARTYPE_MEM);
+		// TODO: Qemu/bochs have MMIO versions of the registers in BAR2
+		// - This range is non-indexed
+		//mmio_base = PCI_GetValidBAR(dev, 2, PCI_BARTYPE_MEM);
+	}
 
 	// Map Framebuffer to hardware address
 	gBGA_Framebuffer = (void *) MM_MapHWPages(base, 768);	// 768 pages (3Mb)
diff --git a/KernelLand/Modules/Display/Tegra2Vid/main.c b/KernelLand/Modules/Display/Tegra2Vid/main.c
index 9ca70984cd031a401558f457b244eda592e84146..a249fd8dc51f683a9ecb9bf171e993a5155ea269 100644
--- a/KernelLand/Modules/Display/Tegra2Vid/main.c
+++ b/KernelLand/Modules/Display/Tegra2Vid/main.c
@@ -196,7 +196,7 @@ int Tegra2Vid_IOCtl(tVFS_Node *Node, int ID, void *Data)
 	
 	switch(ID)
 	{
-	BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaTegra2Vid_IOCtls);
+	BASE_IOCTLS(DRV_TYPE_VIDEO, "Tegra2", VERSION, csaTegra2Vid_IOCtls);
 
 	case VIDEO_IOCTL_SETBUFFORMAT:
 		DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo );
diff --git a/KernelLand/Modules/Display/VESA/main.c b/KernelLand/Modules/Display/VESA/main.c
index 32022efa6b7caaaea79ebda5218c33b9ddfb0877..62fb25a2ea82438f428d8b159b251f674b00602b 100644
--- a/KernelLand/Modules/Display/VESA/main.c
+++ b/KernelLand/Modules/Display/VESA/main.c
@@ -16,7 +16,11 @@
 #include <limits.h>
 
 // === CONSTANTS ===
-#define USE_BIOS	1
+#ifdef ARCHDIR_is_x86
+# define USE_BIOS	1
+#else
+# define USE_BIOS	0
+#endif
 #define VESA_DEFAULT_FRAMEBUFFER	(KERNEL_BASE|0xA0000)
 #define BLINKING_CURSOR	0
 #if BLINKING_CURSOR
@@ -33,6 +37,7 @@ size_t	Vesa_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buff
  int	Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data);
 void	Vesa_int_HideCursor(void);
 void	Vesa_int_ShowCursor(void);
+ int	Vesa_int_SetCursor(tVideo_IOCtl_Bitmap *Cursor);
 void	Vesa_FlipCursor(void *Arg);
 Uint16	VBE_int_GetWord(const tVT_Char *Char);
 void	VBE_int_Text_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);
@@ -124,6 +129,10 @@ int VBE_int_GetModeList(void)
 	
 	// Allocate Info Block
 	info = VM8086_Allocate(gpVesa_BiosState, 512, &infoPtr.seg, &infoPtr.ofs);
+	if( info == NULL ) {
+		Log_Warning("VBE", "VM8086 allocation error");
+		return MODULE_ERR_NOTNEEDED;
+	}
 	// Set Requested Version
 	memcpy(info->signature, "VBE2", 4);
 	// Set Registers
@@ -361,13 +370,16 @@ int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data)
 	case VIDEO_IOCTL_SETBUFFORMAT:
 		Vesa_int_HideCursor();
 		ret = gVesa_BufInfo.BufferFormat;
-		if(Data)	gVesa_BufInfo.BufferFormat = *(int*)Data;
+		if(Data)
+			gVesa_BufInfo.BufferFormat = *(int*)Data;
 		if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)
-			DrvUtil_Video_SetCursor( &gVesa_BufInfo, &gDrvUtil_TextModeCursor );
+			Vesa_int_SetCursor(&gDrvUtil_TextModeCursor);
 		Vesa_int_ShowCursor();
 		return ret;
 	
 	case VIDEO_IOCTL_SETCURSOR:	// Set cursor position
+		if( !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) )
+			return -EINVAL;
 		Vesa_int_HideCursor();
 		giVesaCursorX = ((tVideo_IOCtl_Pos*)Data)->x;
 		giVesaCursorY = ((tVideo_IOCtl_Pos*)Data)->y;
@@ -375,9 +387,7 @@ int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data)
 		return 0;
 	
 	case VIDEO_IOCTL_SETCURSORBITMAP:
-		if( gpVesaCurMode->flags & FLAG_LFB )
-			DrvUtil_Video_SetCursor( &gVesa_BufInfo, Data );
-		return 0;
+		return Vesa_int_SetCursor(Data);
 	}
 	return 0;
 }
@@ -451,9 +461,9 @@ int Vesa_Int_SetMode(int mode)
 	
 	Mutex_Release( &glVesa_Lock );
 
-	// TODO: Disableable backbuffer
-	gVesa_BufInfo.BackBuffer  = realloc(gVesa_BufInfo.BackBuffer,
-		modeptr->height * modeptr->pitch);
+	// TODO: Allow disabling of back-buffer
+	gVesa_DriverStruct.RootNode.Size = modeptr->height * modeptr->pitch;
+	gVesa_BufInfo.BackBuffer  = realloc(gVesa_BufInfo.BackBuffer, modeptr->height * modeptr->pitch);
 	gVesa_BufInfo.Framebuffer = gpVesa_Framebuffer;
 	gVesa_BufInfo.Pitch = modeptr->pitch;
 	gVesa_BufInfo.Width = modeptr->width;
@@ -607,6 +617,21 @@ void Vesa_int_ShowCursor(void)
 	}
 }
 
+int Vesa_int_SetCursor(tVideo_IOCtl_Bitmap *Cursor)
+{
+	if( !CheckMem(Cursor, sizeof(tVideo_IOCtl_Bitmap)) )
+		return -EINVAL;
+	
+	if( gpVesaCurMode && gpVesaCurMode->flags & FLAG_LFB )
+	{
+		DrvUtil_Video_SetCursor( &gVesa_BufInfo, Cursor );
+	}
+	else
+	{
+	}
+	return 0;
+}
+
 /**
  * \brief Swaps the text cursor on/off
  */
diff --git a/KernelLand/Modules/Display/VGA/vgaregs.c b/KernelLand/Modules/Display/VGA/vgaregs.c
new file mode 100644
index 0000000000000000000000000000000000000000..fa1bc8401192236369617e18c7467b67fe3a8c88
--- /dev/null
+++ b/KernelLand/Modules/Display/VGA/vgaregs.c
@@ -0,0 +1,81 @@
+/*
+ * Acess2 VGA Driver
+ * - By John Hodge (thePowersGang)
+ */
+
+#define VGA_CRTC_IDX	0x3B4
+#define VGA_CRTC_DATA	0x3B5
+#define VGA_FEAT_IS1	0x3BA	// Features / Input Status 1 (B/W Chips)
+#define VGA_ATTR_WRITE	0x3C0
+#define VGA_ATTR_READ	0x3C1
+#define VGA_MISC_IS0	0x3C2	// Misc Output / Input Status 0
+#define VGA_MBSLEEP	0x3C3	// "Motherboard Sleep"
+#define VGA_SEQ_IDX	0x3C4
+#define VGA_SEQ_DATA	0x3C5
+#define VGA_DACMASK	0x3C6
+#define VGA_PADR_DACST	0x3C7
+#define VGA_PIXWRMODE	0x3C8
+#define VGA_PIXDATA	0x3C9
+#define VGA_FEATRD	0x3CA
+//                	0x3CB
+#define VGA_MISCOUT	0x3CC
+//                	0x3CD
+#define VGA_GRAPH_IDX	0x3CE
+#define VGA_GRAPH_DATA	0x3CF
+//                   	0x3D0 -- 0x3D3
+#define VGA_CRTC_IDX	0x3D4
+#define VGA_CRTC_DATA	0x3D5
+//                   	0x3D6 -- 0x3D9
+#define VGA_FEAT_IS1_C	0x3DA	// Features / Input Status 1 (Colour Chips)
+
+// === CODE ===
+void VGA_WriteAttr(Uint8 Index, Uint8 Data)
+{
+	Index &= 0x1F;
+	SHORTLOCK(&glVGA_Attr);
+	inb(0x3DA);
+	outb(VGA_ATTR_WRITE, Index);
+	outb(VGA_ATTR_WRITE, Data);
+	SHORTREL(&glVGA_Attr);
+}
+
+Uint8 VGA_ReadAttr(Uint8 Index)
+{
+	Uint8	ret;
+	SHORTLOCK(&glVGA_Attr);
+	inb(0x3DA);
+	outb(VGA_ATTR_WRITE, Index);
+	ret = inb(VGA_ATTR_READ);
+	SHORTREL(&glVGA_Attr);
+	return ret;
+}
+
+void VGA_WriteSeq(Uint8 Index, Uint8 Data)
+{
+	outb(VGA_SEQ_IDX, Index);
+	outb(VGA_SEQ_DATA, Data);
+}
+Uint8 VGA_ReadSeq(Uint8 Index)
+{
+	outb(VGA_SEQ_IDX, Index);
+	return inb(VGA_SEQ_DATA);
+}
+void VGA_WriteGraph(Uint8 Index, Uint8 Data)
+{
+	outb(VGA_GRAPH_IDX, Index);
+	outb(VGA_GRAPH_DATA, Data);
+}
+Uint8 VGA_ReadGraph(Uint8 Index)
+{
+	outb(VGA_GRAPH_IDX, Index);
+	return inb(VGA_GRAPH_DATA);
+}
+
+void VGA_WriteMiscOut(Uint8 Data)
+{
+	outb(VGA_MISC_IS0, Data);
+}
+Uint8 VGA_ReadMiscOut(void)
+{
+	return inb(VGA_MISCOUT);
+}
diff --git a/KernelLand/Modules/Filesystems/InitRD/GenerateInitRD.php b/KernelLand/Modules/Filesystems/InitRD/GenerateInitRD.php
index 5034b4bafabaf7f6cb984d6420f071e3d0a87aad..52377e6fed5179eec5558f67ce8f3e45e61e4fef 100644
--- a/KernelLand/Modules/Filesystems/InitRD/GenerateInitRD.php
+++ b/KernelLand/Modules/Filesystems/InitRD/GenerateInitRD.php
@@ -12,6 +12,8 @@ $gOutput = <<<EOF
 
 EOF;
 
+define("DEBUG_ENABLED", false);
+
 $ACESSDIR = getenv("ACESSDIR");
 $ARCH = getenv("ARCH");
 
@@ -143,7 +145,8 @@ EOF;
 		{
 			$path = $item[1];
 			
-			echo $path,"\n";
+			if( DEBUG_ENABLED )
+				echo $path,"\n";
 			$size = filesize($path);
 	
 			$_sym = "_binary_".str_replace(array("/","-",".","+"), "_", $path)."_start";
diff --git a/KernelLand/Modules/Filesystems/InitRD/files.lst b/KernelLand/Modules/Filesystems/InitRD/files.lst
index efae3be159293d84abdead8e2f801fd4aa318a3b..bac8284079a8ccdfd2ced0c87165ee1d2fc99fad 100644
--- a/KernelLand/Modules/Filesystems/InitRD/files.lst
+++ b/KernelLand/Modules/Filesystems/InitRD/files.lst
@@ -35,6 +35,7 @@ Dir "Libs" {
 	File "__BIN__/Libs/liburi.so"
 	File "__BIN__/Libs/libimage_sif.so"
 	File "__BIN__/Libs/libaxwin3.so"
+	File "__BIN__/Libs/libaxwin4.so"
 	File "__BIN__/Libs/libposix.so"
 	File "__BIN__/Libs/libpsocket.so"
 	File "__BIN__/Libs/libunicode.so"
@@ -61,6 +62,10 @@ Dir "Apps" {
 			File "toolbar_open.sif" "__SRC__/Usermode/Applications/axwin3_src/WM/resources/.toolbar_open.sif"
 			File "toolbar_save.sif" "__SRC__/Usermode/Applications/axwin3_src/WM/resources/.toolbar_save.sif"
 		}
+		Dir "4.0" {
+			File "__BIN__/Apps/AxWin/4.0/AxWinServer"
+			File "__BIN__/Apps/AxWin/4.0/AxWinUI"
+		}
 	}
 }
 #Dir "Keen5" {
diff --git a/KernelLand/Modules/IPStack/icmp.c b/KernelLand/Modules/IPStack/icmp.c
index fa9927ee1adba1af8de758e15bb3df32d2335c4a..f34d790fd01751a1616f91c11857ba203de07298 100644
--- a/KernelLand/Modules/IPStack/icmp.c
+++ b/KernelLand/Modules/IPStack/icmp.c
@@ -26,7 +26,7 @@ struct {
  */
 void ICMP_Initialise()
 {
-	IPv4_RegisterCallback(IP4PROT_ICMP, ICMP_GetPacket);
+	IPv4_RegisterCallback(IP4PROT_ICMP, ICMP_GetPacket, NULL);
 }
 
 /**
@@ -64,12 +64,15 @@ void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buff
 		{
 		case 3:	// Port Unreachable
 			Log_Debug("ICMPv4", "Destination Unreachable (Port Unreachable)");
+			IPv4_HandleError( Interface, IPERR_PORT_UNREACHABLE,
+				htons(Length)-sizeof(tICMPHeader), hdr->Data );
 			break;
 		default:
 			Log_Debug("ICMPv4", "Destination Unreachable (Code %i)", hdr->Code);
+			IPv4_HandleError( Interface, IPERR_MISC,
+				htons(Length)-sizeof(tICMPHeader), hdr->Data );
 			break;
 		}
-//		IPv4_Unreachable( Interface, hdr->Code, htons(hdr->Length)-sizeof(tICMPHeader), hdr->Data );
 		break;
 	
 	// -- 8: Echo Request
diff --git a/KernelLand/Modules/IPStack/icmpv6.h b/KernelLand/Modules/IPStack/icmpv6.h
index bda70b13003c00a77c93ff2bc506423fe64d8750..66f61341f175773cd25255822cfe346a430881b7 100644
--- a/KernelLand/Modules/IPStack/icmpv6.h
+++ b/KernelLand/Modules/IPStack/icmpv6.h
@@ -26,32 +26,32 @@ typedef struct {
 	Uint16	RouterLifetime;	// Seconds, life time as a default router (wtf does that mean?)
 	Uint32	ReachableTime;
 	Uint32	RetransTimer;	// Miliseconds, time between transmissions of RAs from this router
-	Uint8	Options[];
+	Uint8	Options[0];
 } PACKED tICMPv6_RA;
 
 typedef struct {
 	Uint32	Reserved;
 	tIPv6	TargetAddress;
-	Uint8	Options[];
+	Uint8	Options[0];
 } PACKED tICMPv6_NS;
 
 typedef struct {
 	Uint32	Flags;
 	tIPv6	TargetAddress;
-	Uint8	Options[];
+	Uint8	Options[0];
 } PACKED tICMPv6_NA;
 
 typedef struct {
 	Uint32	Reserved;
 	tIPv6	TargetAddress;
 	tIPv6	DestinationAddress;
-	Uint8	Options[];
+	Uint8	Options[0];
 } PACKED tICMPv6_Redirect;
 
 typedef struct {
 	Uint8	Type;	// 1,2
 	Uint8	Length;	// Length of field in units of 8 bytes (incl header), typically 1
-	Uint8	Address[];
+	Uint8	Address[0];
 } PACKED tICMPv6_Opt_LinkAddr;
 
 typedef struct {
@@ -62,7 +62,7 @@ typedef struct {
 	Uint32	ValidLifetime;
 	Uint32	PreferredLifetime;
 	Uint32	_reserved2;
-	tIPv6	Prefix[];
+	tIPv6	Prefix[0];
 } PACKED tICMPv6_Opt_Prefix;
 
 typedef struct {
@@ -70,7 +70,7 @@ typedef struct {
 	Uint8	Length;
 	Uint16	_rsvd1;
 	Uint32	_rsvd2;
-	Uint8	Data[];	// All or part of the redirected message (not exceeding MTU)
+	Uint8	Data[0];	// All or part of the redirected message (not exceeding MTU)
 } PACKED tICMPv6_Opt_Redirect;
 
 typedef struct {
diff --git a/KernelLand/Modules/IPStack/ipstack.h b/KernelLand/Modules/IPStack/ipstack.h
index ddcdde9649ea22810e8a2a273809fc32281c5ddf..3833ce7b0d6c2ed345765f511a6678f2d6529383 100644
--- a/KernelLand/Modules/IPStack/ipstack.h
+++ b/KernelLand/Modules/IPStack/ipstack.h
@@ -15,7 +15,16 @@ typedef struct sAdapter	tAdapter;
 typedef struct sInterface	tInterface;
 typedef struct sSocketFile	tSocketFile;
 
-typedef void	(*tIPCallback)(tInterface *Interface, void *Address, int Length, void *Buffer);
+typedef enum eIPErrorMode
+{
+	IPERR_MISC,
+	IPERR_HOST_UNREACHABLE,
+	IPERR_PORT_UNREACHABLE,
+} tIPErrorMode;
+
+// NOTE: Non-const to allow reuse of Rx buffer for prepping Tx
+typedef void	tIPRxCallback(tInterface *Interface, void *Address, int Length, void *Buffer);
+typedef void	tIPErrorCallback(tInterface *Interface, tIPErrorMode mode, const void *Address, int Length, const void *Buffer);
 
 enum eInterfaceTypes {
 	AF_NULL,
diff --git a/KernelLand/Modules/IPStack/ipv4.c b/KernelLand/Modules/IPStack/ipv4.c
index 000fe3966381c15393074d029408a5326ce31e30..371fe4026f224502521daa03df9bc91e388c42bc 100644
--- a/KernelLand/Modules/IPStack/ipv4.c
+++ b/KernelLand/Modules/IPStack/ipv4.c
@@ -20,7 +20,7 @@ extern  int	ICMP_Ping(tInterface *Interface, tIPv4 Addr);
 
 // === PROTOTYPES ===
  int	IPv4_Initialise();
- int	IPv4_RegisterCallback(int ID, tIPCallback Callback);
+// int	IPv4_RegisterCallback(int ID, tIPRxCallback Callback, );
 void	IPv4_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer);
 tInterface	*IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast);
 Uint32	IPv4_Netmask(int FixedBits);
@@ -28,7 +28,10 @@ Uint16	IPv4_Checksum(const void *Buf, size_t Length);
  int	IPv4_Ping(tInterface *Iface, tIPv4 Addr);
 
 // === GLOBALS ===
-tIPCallback	gaIPv4_Callbacks[256];
+struct {
+	tIPRxCallback*	rx_cb;
+	tIPErrorCallback*	err_cb;
+} gaIPv4_Callbacks[256];
 
 // === CODE ===
 /**
@@ -46,11 +49,12 @@ int IPv4_Initialise()
  * \param ID	8-bit packet type ID
  * \param Callback	Callback function
  */
-int IPv4_RegisterCallback(int ID, tIPCallback Callback)
+int IPv4_RegisterCallback(int ID, tIPRxCallback *RxCallback, tIPErrorCallback *ErrCallback)
 {
 	if( ID < 0 || ID > 255 )	return 0;
-	if( gaIPv4_Callbacks[ID] )	return 0;
-	gaIPv4_Callbacks[ID] = Callback;
+	if( gaIPv4_Callbacks[ID].rx_cb )	return 0;
+	gaIPv4_Callbacks[ID].rx_cb = RxCallback;
+	gaIPv4_Callbacks[ID].err_cb = ErrCallback;
 	return 1;
 }
 
@@ -66,14 +70,12 @@ int IPv4_RegisterCallback(int ID, tIPCallback Callback)
  */
 int IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, tIPStackBuffer *Buffer)
 {
-	tMacAddr	to;
 	tIPv4Header	hdr;
-	 int	length;
 
-	length = IPStack_Buffer_GetLength(Buffer);
+	int length = IPStack_Buffer_GetLength(Buffer);
 	
 	// --- Resolve destination MAC address
-	to = HWCache_Resolve(Iface, &Address);
+	tMacAddr to = HWCache_Resolve(Iface, &Address);
 	if( MAC_EQU(to, cMAC_ZERO) ) {
 		// No route to host
 		Log_Notice("IPv4", "No route to host %i.%i.%i.%i",
@@ -134,7 +136,6 @@ int IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, tIPS
 void IPv4_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
 {
 	tIPv4Header	*hdr = Buffer;
-	tInterface	*iface;
 	Uint8	*data;
 	 int	dataLength;
 	 int	ret;
@@ -197,7 +198,7 @@ void IPv4_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buff
 	data = &hdr->Options[0];
 	
 	// Get Interface (allowing broadcasts)
-	iface = IPv4_GetInterface(Adapter, hdr->Destination, 1);
+	tInterface *iface = IPv4_GetInterface(Adapter, hdr->Destination, 1);
 	
 	// Firewall rules
 	if( iface ) {
@@ -255,12 +256,29 @@ void IPv4_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buff
 	}
 	
 	// Send it on
-	if( !gaIPv4_Callbacks[hdr->Protocol] ) {
+	if( !gaIPv4_Callbacks[hdr->Protocol].rx_cb ) {
 		Log_Log("IPv4", "Unknown Protocol %i", hdr->Protocol);
 		return ;
 	}
 	
-	gaIPv4_Callbacks[hdr->Protocol]( iface, &hdr->Source, dataLength, data );
+	gaIPv4_Callbacks[hdr->Protocol].rx_cb( iface, &hdr->Source, dataLength, data );
+}
+
+/*
+ * Handles an error from the ICMPv4 code, 'Buf' contains part of an IPv4 packet
+ */
+void IPv4_HandleError(tInterface *Iface, tIPErrorMode Mode, size_t Length, const void *Buf)
+{
+	if(Length < sizeof(tIPv4Header))	return;
+	const tIPv4Header*	hdr = Buf;
+	if(hdr->Version != 4)	return;
+	
+	// Get Data and Data Length
+	size_t dataLength = MIN(Length, ntohs(hdr->TotalLength)) - sizeof(tIPv4Header);
+	const void *data = &hdr->Options[0];
+	
+	if( gaIPv4_Callbacks[hdr->Protocol].err_cb )
+		gaIPv4_Callbacks[hdr->Protocol].err_cb(Iface, Mode, &hdr->Source, dataLength, data);
 }
 
 /**
@@ -272,16 +290,14 @@ void IPv4_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buff
  */
 tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast)
 {
-	tInterface	*iface = NULL, *zero_iface = NULL;
-	Uint32	netmask;
-	Uint32	addr, this;
+	tInterface	*zero_iface = NULL;
 
 	ENTER("pAdapter xAddress bBroadcast", Adapter, Address, Broadcast);	
 
-	addr = ntohl( Address.L );
+	Uint32 addr = ntohl( Address.L );
 	LOG("addr = 0x%x", addr);
 	
-	for( iface = gIP_Interfaces; iface; iface = iface->Next)
+	for( tInterface *iface = gIP_Interfaces; iface; iface = iface->Next)
 	{
 		if( iface->Adapter != Adapter )	continue;
 		if( iface->Type != 4 )	continue;
@@ -307,8 +323,8 @@ tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast)
 		if( !Broadcast )	continue;
 		
 		// Check for broadcast
-		this = ntohl( ((tIPv4*)iface->Address)->L );
-		netmask = IPv4_Netmask(iface->SubnetBits);
+		Uint32 this = ntohl( ((tIPv4*)iface->Address)->L );
+		Uint32 netmask = IPv4_Netmask(iface->SubnetBits);
 		LOG("iface addr = 0x%x, netmask = 0x%x (bits = %i)", this, netmask, iface->SubnetBits);
 
 		if( (addr & netmask) == (this & netmask) && (addr & ~netmask) == (0xFFFFFFFF & ~netmask) )
diff --git a/KernelLand/Modules/IPStack/ipv4.h b/KernelLand/Modules/IPStack/ipv4.h
index dd78788f94f76e1f951245cce8fa7a33b13e5ec6..c31b751296f968649e34e47106eb765ef30aee07 100644
--- a/KernelLand/Modules/IPStack/ipv4.h
+++ b/KernelLand/Modules/IPStack/ipv4.h
@@ -47,9 +47,11 @@ struct sIPv4Header
 #define IPV4_ETHERNET_ID	0x0800
 
 // === FUNCTIONS ===
-extern int	IPv4_RegisterCallback(int ID, tIPCallback Callback);
+extern int	IPv4_RegisterCallback(int ID, tIPRxCallback *RxCallback, tIPErrorCallback *ErrCallback);
 extern Uint16	IPv4_Checksum(const void *Buf, size_t Length);
 extern Uint32	IPv4_Netmask(int FixedBits);
 extern int	IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, tIPStackBuffer *Buffer);
 
+extern void	IPv4_HandleError(tInterface *Iface, tIPErrorMode Mode, size_t Length, const void *Buf);
+
 #endif
diff --git a/KernelLand/Modules/IPStack/ipv6.c b/KernelLand/Modules/IPStack/ipv6.c
index 2ac05747aa4f967ab21216917b7a2ed080667b63..0e6df538d51f89c0a9435698a6e90d5aeff9c3fe 100644
--- a/KernelLand/Modules/IPStack/ipv6.c
+++ b/KernelLand/Modules/IPStack/ipv6.c
@@ -14,12 +14,12 @@ extern Uint32	IPv4_Netmask(int FixedBits);
 
 // === PROTOTYPES ===
  int	IPv6_Initialise();
- int	IPv6_RegisterCallback(int ID, tIPCallback Callback);
+// int	IPv6_RegisterCallback(int ID, tIPCallback Callback);
 void	IPv6_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer);
 tInterface	*IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast);
 
 // === GLOBALS ===
-tIPCallback	gaIPv6_Callbacks[256];
+tIPRxCallback*	gaIPv6_Callbacks[256];
 
 // === CODE ===
 /**
@@ -36,7 +36,7 @@ int IPv6_Initialise()
  * \param ID	8-bit packet type ID
  * \param Callback	Callback function
  */
-int IPv6_RegisterCallback(int ID, tIPCallback Callback)
+int IPv6_RegisterCallback(int ID, tIPRxCallback* Callback)
 {
 	if( ID < 0 || ID > 255 )	return 0;
 	if( gaIPv6_Callbacks[ID] )	return 0;
diff --git a/KernelLand/Modules/IPStack/ipv6.h b/KernelLand/Modules/IPStack/ipv6.h
index 35cd74227007370244f3c8d314d260c230809b41..086dfbb5398914838fbb24585d3e4ef8adc667f1 100644
--- a/KernelLand/Modules/IPStack/ipv6.h
+++ b/KernelLand/Modules/IPStack/ipv6.h
@@ -32,12 +32,12 @@ struct sIPv6Header
 	Uint8	HopLimit;
 	tIPv6	Source;
 	tIPv6	Destination;
-	char	Data[];
+	char	Data[0];
 };
 
 #define IPV6_ETHERNET_ID	0x86DD
 
-extern int	IPv6_RegisterCallback(int ID, tIPCallback Callback);
+extern int	IPv6_RegisterCallback(int ID, tIPRxCallback* Callback);
 extern int	IPv6_SendPacket(tInterface *Iface, tIPv6 Destination, int Protocol, tIPStackBuffer *Buffer);
 
 #endif
diff --git a/KernelLand/Modules/IPStack/tcp.c b/KernelLand/Modules/IPStack/tcp.c
index bbf7951730d2d846922414730752680ca7f28818..2854f30a0ee853858f05648b85be6e9537e371a1 100644
--- a/KernelLand/Modules/IPStack/tcp.c
+++ b/KernelLand/Modules/IPStack/tcp.c
@@ -2,7 +2,7 @@
  * Acess2 IP Stack
  * - TCP Handling
  */
-#define DEBUG	1
+#define DEBUG	0
 #include "ipstack.h"
 #include "ipv4.h"
 #include "ipv6.h"
@@ -18,7 +18,7 @@
 #define TCP_WINDOW_SIZE	0x2000
 #define TCP_RECIEVE_BUFFER_SIZE	0x8000
 #define TCP_DACK_THRESHOLD	4096
-#define TCP_DACK_TIMEOUT	500
+#define TCP_DACK_TIMEOUT	100
 
 #define TCP_DEBUG	0	// Set to non-0 to enable TCP packet logging
 
@@ -28,7 +28,9 @@ void	TCP_StartConnection(tTCPConnection *Conn);
 void	TCP_SendPacket(tTCPConnection *Conn, tTCPHeader *Header, size_t DataLen, const void *Data);
 void	TCP_int_SendPacket(tInterface *Interface, const void *Dest, tTCPHeader *Header, size_t Length, const void *Data);
 void	TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
-void	TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length);
+void	TCP_IPError(tInterface *Interface, tIPErrorMode Mode, const void *Address, int Length, const void *Buffer);
+ int	TCP_INT_HandleServerPacket(tInterface *Interface, tTCPListener *Server, const void *Address, tTCPHeader *Header, size_t Length);
+ int	TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length);
 int	TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length);
 void	TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection);
 void	TCP_int_SendDelayedACK(void *ConnPtr);
@@ -66,6 +68,7 @@ tVFS_NodeType	gTCP_ServerNodeType = {
 	};
 tVFS_NodeType	gTCP_ClientNodeType = {
 	.TypeName = "TCP Client/Connection",
+	.Flags = VFS_NODETYPEFLAG_STREAM,
 	.Read  = TCP_Client_Read,
 	.Write = TCP_Client_Write,
 	.IOCtl = TCP_Client_IOCtl,
@@ -92,7 +95,7 @@ void TCP_Initialise(void)
 	giTCP_NextOutPort += rand()%128;
 	IPStack_AddFile(&gTCP_ServerFile);
 	IPStack_AddFile(&gTCP_ClientFile);
-	IPv4_RegisterCallback(IP4PROT_TCP, TCP_GetPacket);
+	IPv4_RegisterCallback(IP4PROT_TCP, TCP_GetPacket, TCP_IPError);
 	IPv6_RegisterCallback(IP4PROT_TCP, TCP_GetPacket);
 }
 
@@ -146,10 +149,22 @@ void TCP_int_SendPacket(tInterface *Interface, const void *Dest, tTCPHeader *Hea
 		IPStack_Buffer_AppendSubBuffer(buffer, Length, 0, Data, NULL, NULL);
 	IPStack_Buffer_AppendSubBuffer(buffer, sizeof(*Header), 0, Header, NULL, NULL);
 
-	LOG("Sending %i+%i to %s:%i", sizeof(*Header), Length,
+	#if TCP_DEBUG
+	Log_Log("TCP", "TCP_int_SendPacket: <Local>:%i to [%s]:%i (%i data), Flags = %s%s%s%s%s%s%s%s",
+		ntohs(Header->SourcePort),
 		IPStack_PrintAddress(Interface->Type, Dest),
-		ntohs(Header->DestPort)
+		ntohs(Header->DestPort),
+		Length,
+		(Header->Flags & TCP_FLAG_CWR) ? "CWR " : "",
+		(Header->Flags & TCP_FLAG_ECE) ? "ECE " : "",
+		(Header->Flags & TCP_FLAG_URG) ? "URG " : "",
+		(Header->Flags & TCP_FLAG_ACK) ? "ACK " : "",
+		(Header->Flags & TCP_FLAG_PSH) ? "PSH " : "",
+		(Header->Flags & TCP_FLAG_RST) ? "RST " : "",
+		(Header->Flags & TCP_FLAG_SYN) ? "SYN " : "",
+		(Header->Flags & TCP_FLAG_FIN) ? "FIN " : ""
 		);
+	#endif
 
 	Header->Checksum = 0;
 	Header->Checksum = TCP_int_CalculateChecksum(Interface->Type, Interface->Address, Dest,
@@ -168,7 +183,7 @@ void TCP_int_SendPacket(tInterface *Interface, const void *Dest, tTCPHeader *Hea
 	}
 }
 
-void TCP_int_SendRSTTo(tInterface *Interface, void *Address, size_t Length, const tTCPHeader *Header)
+void TCP_int_SendRSTTo(tInterface *Interface, const void *Address, size_t Length, const tTCPHeader *Header)
 {
 	tTCPHeader	out_hdr = {0};
 	
@@ -244,96 +259,20 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe
 		{
 			// Check that it is coming in on the same interface
 			if(conn->Interface != Interface)	continue;
-
 			// Check Source Port
-			Log_Log("TCP", "TCP_GetPacket: conn->RemotePort(%i) == hdr->SourcePort(%i)",
-				conn->RemotePort, ntohs(hdr->SourcePort));
 			if(conn->RemotePort != ntohs(hdr->SourcePort))	continue;
-
 			// Check Source IP
-			Log_Debug("TCP", "TCP_GetPacket: conn->RemoteIP(%s)",
-				IPStack_PrintAddress(conn->Interface->Type, &conn->RemoteIP));
-			Log_Debug("TCP", "                == Address(%s)",
-				IPStack_PrintAddress(conn->Interface->Type, Address));
 			if( IPStack_CompareAddress(conn->Interface->Type, &conn->RemoteIP, Address, -1) == 0 )
 				continue ;
 
 			Log_Log("TCP", "TCP_GetPacket: Matches connection %p", conn);
 			// We have a response!
-			TCP_INT_HandleConnectionPacket(conn, hdr, Length);
-
-			return;
+			if( TCP_INT_HandleConnectionPacket(conn, hdr, Length) == 0 )
+				return;
+			break ;
 		}
 
-		
-		if( hdr->Flags & TCP_FLAG_RST ) {
-			LOG("RST, ignore");
-			return ;
-		}
-		else if( hdr->Flags & TCP_FLAG_ACK ) {
-			LOG("ACK, send RST");
-			TCP_int_SendRSTTo(Interface, Address, Length, hdr);
-			return ;
-		}
-		else if( !(hdr->Flags & TCP_FLAG_SYN) ) {
-			LOG("Other, ignore");
-			return ;
-		}
-		Log_Log("TCP", "TCP_GetPacket: Opening Connection");
-		
-		// TODO: Check for halfopen max
-		
-		tTCPConnection *conn = TCP_int_CreateConnection(Interface, TCP_ST_SYN_RCVD);
-		conn->LocalPort = srv->Port;
-		conn->RemotePort = ntohs(hdr->SourcePort);
-		
-		switch(Interface->Type)
-		{
-		case 4:	conn->RemoteIP.v4 = *(tIPv4*)Address;	break;
-		case 6:	conn->RemoteIP.v6 = *(tIPv6*)Address;	break;
-		default:	ASSERTC(Interface->Type,==,4);	return;
-		}
-		
-		conn->NextSequenceRcv = ntohl( hdr->SequenceNumber ) + 1;
-		conn->HighestSequenceRcvd = conn->NextSequenceRcv;
-		conn->NextSequenceSend = rand();
-		conn->LastACKSequence = ntohl( hdr->SequenceNumber );
-		
-		conn->Node.ImplInt = srv->NextID ++;
-		
-		// Hmm... Theoretically, this lock will never have to wait,
-		// as the interface is locked to the watching thread, and this
-		// runs in the watching thread. But, it's a good idea to have
-		// it, just in case
-		// Oh, wait, there is a case where a wildcard can be used
-		// (srv->Interface == NULL) so having the lock is a good idea
-		SHORTLOCK(&srv->lConnections);
-		conn->Server = srv;
-		conn->Prev = srv->ConnectionsTail;
-		if(srv->Connections) {
-			ASSERT(srv->ConnectionsTail);
-			srv->ConnectionsTail->Next = conn;
-		}
-		else {
-			ASSERT(!srv->ConnectionsTail);
-			srv->Connections = conn;
-		}
-		srv->ConnectionsTail = conn;
-		if(!srv->NewConnections)
-			srv->NewConnections = conn;
-		VFS_MarkAvaliable( &srv->Node, 1 );
-		SHORTREL(&srv->lConnections);
-		Semaphore_Signal(&srv->WaitingConnections, 1);
-
-		// Send the SYN ACK
-		hdr->Flags |= TCP_FLAG_ACK;
-		hdr->AcknowlegementNumber = htonl(conn->NextSequenceRcv);
-		hdr->SequenceNumber = htonl(conn->NextSequenceSend);
-		hdr->DestPort = hdr->SourcePort;
-		hdr->SourcePort = htons(srv->Port);
-		hdr->DataOffset = (sizeof(tTCPHeader)/4) << 4;
-		TCP_SendPacket( conn, hdr, 0, NULL );
-		conn->NextSequenceSend ++;
+		TCP_INT_HandleServerPacket(Interface, srv, Address, hdr, Length);
 		return ;
 	}
 
@@ -348,13 +287,13 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe
 			if(conn->RemotePort != ntohs(hdr->SourcePort))	continue;
 
 			// Check Source IP
-			if(conn->Interface->Type == 6 && !IP6_EQU(conn->RemoteIP.v6, *(tIPv6*)Address))
-				continue;
-			if(conn->Interface->Type == 4 && !IP4_EQU(conn->RemoteIP.v4, *(tIPv4*)Address))
-				continue;
+			if( IPStack_CompareAddress(conn->Interface->Type, &conn->RemoteIP, Address, -1) == 0 )
+				continue ;
 
-			TCP_INT_HandleConnectionPacket(conn, hdr, Length);
-			return ;
+			// Handle or fall through
+			if( TCP_INT_HandleConnectionPacket(conn, hdr, Length) == 0 )
+				return ;
+			break;
 		}
 	}
 	
@@ -366,13 +305,116 @@ void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe
 	}
 }
 
+void TCP_IPError(tInterface *Interface, tIPErrorMode Mode, const void *Address, int Length, const void *Buffer)
+{
+	if( Length < sizeof(tTCPHeader) )	return ;
+	
+	const tTCPHeader	*hdr = Buffer;
+	
+	// TODO: Handle errors for server connections
+	
+	for( tTCPConnection *conn = gTCP_OutbountCons; conn; conn = conn->Next )
+	{
+		if(conn->Interface != Interface)
+			continue;
+		if(conn->RemotePort != ntohs(hdr->SourcePort))
+			continue;
+		if( IPStack_CompareAddress(conn->Interface->Type, &conn->RemoteIP, Address, -1) == 0 )
+			continue ;
+		
+		// Mark an error on the interface
+		VFS_MarkError(&conn->Node, 1);
+		return ;
+	}
+}
+
+/*
+ * Handle packets in LISTEN state
+ */
+int TCP_INT_HandleServerPacket(tInterface *Interface, tTCPListener *Server, const void *Address, tTCPHeader *Header, size_t Length)
+{
+	if( Header->Flags & TCP_FLAG_RST ) {
+		LOG("RST, ignore");
+		return 0;
+	}
+	else if( Header->Flags & TCP_FLAG_ACK ) {
+		LOG("ACK, send RST");
+		TCP_int_SendRSTTo(Interface, Address, Length, Header);
+		return 0;
+	}
+	else if( !(Header->Flags & TCP_FLAG_SYN) ) {
+		LOG("Other, ignore");
+		return 0;
+	}
+	
+	Log_Log("TCP", "TCP_GetPacket: Opening Connection");
+	
+	// TODO: Check security (a TCP Option)
+	// TODO: Check SEG.PRC 
+	// TODO: Check for halfopen max
+	
+	tTCPConnection *conn = TCP_int_CreateConnection(Interface, TCP_ST_SYN_RCVD);
+	conn->LocalPort = Server->Port;
+	conn->RemotePort = ntohs(Header->SourcePort);
+	
+	switch(Interface->Type)
+	{
+	case 4:	conn->RemoteIP.v4 = *(tIPv4*)Address;	break;
+	case 6:	conn->RemoteIP.v6 = *(tIPv6*)Address;	break;
+	default:	ASSERTC(Interface->Type,==,4);	return 0;
+	}
+	
+	conn->NextSequenceRcv = ntohl( Header->SequenceNumber ) + 1;
+	conn->HighestSequenceRcvd = conn->NextSequenceRcv;
+	conn->NextSequenceSend = rand();
+	conn->LastACKSequence = ntohl( Header->SequenceNumber );
+	
+	conn->Node.ImplInt = Server->NextID ++;
+	conn->Node.Size = -1;
+	
+	// Hmm... Theoretically, this lock will never have to wait,
+	// as the interface is locked to the watching thread, and this
+	// runs in the watching thread. But, it's a good idea to have
+	// it, just in case
+	// Oh, wait, there is a case where a wildcard can be used
+	// (Server->Interface == NULL) so having the lock is a good idea
+	SHORTLOCK(&Server->lConnections);
+	conn->Server = Server;
+	conn->Prev = Server->ConnectionsTail;
+	if(Server->Connections) {
+		ASSERT(Server->ConnectionsTail);
+		Server->ConnectionsTail->Next = conn;
+	}
+	else {
+		ASSERT(!Server->ConnectionsTail);
+		Server->Connections = conn;
+	}
+	Server->ConnectionsTail = conn;
+	if(!Server->NewConnections)
+		Server->NewConnections = conn;
+	VFS_MarkAvaliable( &Server->Node, 1 );
+	SHORTREL(&Server->lConnections);
+	Semaphore_Signal(&Server->WaitingConnections, 1);
+
+	// Send the SYN ACK
+	Header->Flags = TCP_FLAG_ACK|TCP_FLAG_SYN;
+	Header->AcknowlegementNumber = htonl(conn->NextSequenceRcv);
+	Header->SequenceNumber = htonl(conn->NextSequenceSend);
+	Header->DestPort = Header->SourcePort;
+	Header->SourcePort = htons(Server->Port);
+	Header->DataOffset = (sizeof(tTCPHeader)/4) << 4;
+	TCP_SendPacket( conn, Header, 0, NULL );
+	conn->NextSequenceSend ++;
+	return 0;
+}
+
 /**
  * \brief Handles a packet sent to a specific connection
  * \param Connection	TCP Connection pointer
  * \param Header	TCP Packet pointer
  * \param Length	Length of the packet
  */
-void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length)
+int TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length)
 {
 	 int	dataLen;
 	Uint32	sequence_num;
@@ -381,7 +423,11 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head
 	// TODO: Check if this needs to be here
 	if( Connection->State == TCP_ST_FINISHED ) {
 		Log_Log("TCP", "Packet ignored - connection finnished");
-		return ;
+		return 1;
+	}
+	if( Connection->State == TCP_ST_FORCE_CLOSE ) {
+		Log_Log("TCP", "Packet ignored - connection reset");
+		return 1;
 	}
 	
 	// Syncronise sequence values
@@ -432,7 +478,7 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head
 			if( Header->Flags & TCP_FLAG_ACK )
 			{	
 				Log_Log("TCP", "ACKing SYN-ACK");
-				Connection->State = TCP_ST_OPEN;
+				Connection->State = TCP_ST_ESTABLISHED;
 				VFS_MarkFull(&Connection->Node, 0);
 				TCP_INT_SendACK(Connection, "SYN-ACK");
 			}
@@ -447,19 +493,34 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head
 	
 	// SYN-ACK sent, expecting ACK
 	case TCP_ST_SYN_RCVD:
+		if( Header->Flags & TCP_FLAG_RST )
+		{
+			Log_Log("TCP", "RST Received, closing");
+			Connection->State = TCP_ST_FORCE_CLOSE;
+			VFS_MarkError(&Connection->Node, 1);
+			return 0;
+		}
 		if( Header->Flags & TCP_FLAG_ACK )
 		{
 			// TODO: Handle max half-open limit
 			Log_Log("TCP", "Connection fully opened");
-			Connection->State = TCP_ST_OPEN;
+			Connection->State = TCP_ST_ESTABLISHED;
 			VFS_MarkFull(&Connection->Node, 0);
 		}
 		break;
 		
 	// --- Established State ---
-	case TCP_ST_OPEN:
+	case TCP_ST_ESTABLISHED:
 		// - Handle State changes
 		//
+		if( Header->Flags & TCP_FLAG_RST )
+		{
+			Log_Log("TCP", "Conn %p closed, received RST");
+			// Error outstanding transactions
+			Connection->State = TCP_ST_FORCE_CLOSE;
+			VFS_MarkError(&Connection->Node, 1);
+			return 0;
+		}
 		if( Header->Flags & TCP_FLAG_FIN ) {
 			Log_Log("TCP", "Conn %p closed, recieved FIN", Connection);
 			VFS_MarkError(&Connection->Node, 1);
@@ -467,7 +528,7 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head
 			TCP_INT_SendACK(Connection, "FIN Received");
 			Connection->State = TCP_ST_CLOSE_WAIT;
 			// CLOSE WAIT requires the client to close
-			return ;
+			return 0;
 		}
 	
 		// Check for an empty packet
@@ -475,7 +536,7 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head
 			if( Header->Flags == TCP_FLAG_ACK )
 			{
 				Log_Log("TCP", "ACK only packet");
-				return ;
+				return 0;
 			}
 			// TODO: Is this right? (empty packet counts as one byte)
 			if( Connection->HighestSequenceRcvd == Connection->NextSequenceRcv )
@@ -483,7 +544,7 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head
 			Connection->NextSequenceRcv ++;
 			Log_Log("TCP", "Empty Packet, inc and ACK the current sequence number");
 			TCP_INT_SendACK(Connection, "Empty");
-			return ;
+			return 0;
 		}
 		
 		// NOTES:
@@ -610,7 +671,7 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head
 			Connection->State = TCP_ST_FIN_WAIT2;
 			Log_Debug("TCP", "Conn %p closed, sent FIN ACKed", Connection);
 			VFS_MarkError(&Connection->Node, 1);
-			return ;
+			return 0;
 		}
 		break;
 	
@@ -630,7 +691,7 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head
 			Connection->State = TCP_ST_TIME_WAIT;
 			Log_Debug("TCP", "Conn %p CLOSING -> TIME WAIT", Connection);
 			VFS_MarkError(&Connection->Node, 1);
-			return ;
+			return 0;
 		}
 		break;
 	
@@ -642,12 +703,17 @@ void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Head
 	case TCP_ST_FINISHED:
 		Log_Log("TCP", "Packets when CLOSED, ignoring");
 		break;
+	case TCP_ST_FORCE_CLOSE:
+		Log_Log("TCP", "Packets when force CLOSED, ignoring");
+		return 1;
 	
 	//default:
 	//	Log_Warning("TCP", "Unhandled TCP state %i", Connection->State);
 	//	break;
 	}
 	
+	return 0;
+	
 }
 
 /**
@@ -690,10 +756,15 @@ int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t
 void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection)
 {
 	// Calculate length of contiguous bytes
-	const int	length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv;
+	const size_t	length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv;
 	Uint32	index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE;
 	size_t	runlength = length;
-	LOG("length=%i, index=0x%x", length, index);
+	LOG("HSR=0x%x,NSR=0x%x", Connection->HighestSequenceRcvd, Connection->NextSequenceRcv);
+	if( Connection->HighestSequenceRcvd == Connection->NextSequenceRcv )
+	{
+		return ;
+	}
+	LOG("length=%u, index=0x%x", length, index);
 	for( int i = 0; i < length; i ++ )
 	{
 		 int	bit = index % 8;
@@ -854,6 +925,7 @@ tTCPConnection *TCP_int_CreateConnection(tInterface *Interface, enum eTCPConnect
 	conn->LocalPort = -1;
 	conn->RemotePort = -1;
 
+	conn->Node.Size = -1;
 	conn->Node.ReferenceCount = 1;
 	conn->Node.ImplPtr = conn;
 	conn->Node.NumACLs = 1;
@@ -867,6 +939,7 @@ tTCPConnection *TCP_int_CreateConnection(tInterface *Interface, enum eTCPConnect
 	Semaphore_Init(conn->SentBufferSpace, 0, TCP_SEND_BUFFER_SIZE, "TCP SentBuffer", conn->Name);
 	#endif
 	
+	conn->HighestSequenceRcvd = 0;
 	#if CACHE_FUTURE_PACKETS_IN_BYTES
 	// Future recieved data (ahead of the expected sequence number)
 	conn->FuturePacketData = (Uint8*)conn + sizeof(tTCPConnection);
@@ -1116,7 +1189,8 @@ tVFS_Node *TCP_Client_Init(tInterface *Interface)
 	conn->Server = NULL;
 	conn->Prev = NULL;
 	conn->Next = gTCP_OutbountCons;
-	gTCP_OutbountCons->Prev = conn;
+	if(gTCP_OutbountCons)
+		gTCP_OutbountCons->Prev = conn;
 	gTCP_OutbountCons = conn;
 	SHORTREL(&glTCP_OutbountCons);
 
@@ -1133,13 +1207,14 @@ size_t TCP_Client_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffe
 	tTCPConnection	*conn = Node->ImplPtr;
 	size_t	len;
 	
-	ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);
+	ENTER("pNode XOffset xLength pBuffer", Node, Offset, Length, Buffer);
 	LOG("conn = %p {State:%i}", conn, conn->State);
 	
 	// If the connection has been closed (state > ST_OPEN) then clear
 	// any stale data in the buffer (until it is empty (until it is empty))
-	if( conn->State > TCP_ST_OPEN )
+	if( conn->State > TCP_ST_ESTABLISHED )
 	{
+		LOG("Connection closed");
 		Mutex_Acquire( &conn->lRecievedPackets );
 		len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length );
 		Mutex_Release( &conn->lRecievedPackets );
@@ -1216,6 +1291,8 @@ void TCP_INT_SendDataPacket(tTCPConnection *Connection, size_t Length, const voi
 	
 	TCP_SendPacket( Connection, packet, Length, Data );
 	
+	// TODO: Start a retransmit time (if data is not ACKed in x seconds, send again)
+	
 	Connection->NextSequenceSend += Length;
 }
 
@@ -1235,7 +1312,7 @@ size_t TCP_Client_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void
 //	#endif
 	
 	// Don't allow a write to a closed connection
-	if( conn->State > TCP_ST_OPEN ) {
+	if( conn->State > TCP_ST_ESTABLISHED ) {
 		VFS_MarkError(Node, 1);
 		errno = 0;
 		LEAVE('i', -1);
@@ -1394,7 +1471,7 @@ void TCP_Client_Close(tVFS_Node *Node)
 	}
 	Node->ReferenceCount --;
 	
-	if( conn->State == TCP_ST_CLOSE_WAIT || conn->State == TCP_ST_OPEN )
+	if( conn->State == TCP_ST_CLOSE_WAIT || conn->State == TCP_ST_ESTABLISHED )
 	{
 		packet.SourcePort = htons(conn->LocalPort);
 		packet.DestPort = htons(conn->RemotePort);
@@ -1416,10 +1493,14 @@ void TCP_Client_Close(tVFS_Node *Node)
 		Log_Warning("TCP", "Closing connection that was never opened");
 		TCP_int_FreeTCB(conn);
 		break;
+	case TCP_ST_FORCE_CLOSE:
+		conn->State = TCP_ST_FINISHED;
+		TCP_int_FreeTCB(conn);
+		break;
 	case TCP_ST_CLOSE_WAIT:
 		conn->State = TCP_ST_LAST_ACK;
 		break;
-	case TCP_ST_OPEN:
+	case TCP_ST_ESTABLISHED:
 		conn->State = TCP_ST_FIN_WAIT1;
 		while( conn->State == TCP_ST_FIN_WAIT1 )
 			Threads_Yield();
diff --git a/KernelLand/Modules/IPStack/tcp.h b/KernelLand/Modules/IPStack/tcp.h
index 43828f185231e125b7b45904835333030243c0fa..0e260fcf9b42bc468cfad19305788275f9f8b9ff 100644
--- a/KernelLand/Modules/IPStack/tcp.h
+++ b/KernelLand/Modules/IPStack/tcp.h
@@ -94,7 +94,7 @@ enum eTCPConnectionState
 	TCP_ST_SYN_SENT,	// 1 - SYN sent by local, waiting for SYN-ACK
 	TCP_ST_SYN_RCVD,	// 2 - SYN recieved, SYN-ACK sent
 	
-	TCP_ST_OPEN,    	// 3 - Connection open
+	TCP_ST_ESTABLISHED,    	// 3 - Connection open
 	
 	// Local Close
 	TCP_ST_FIN_WAIT1,	// 4 - FIN sent, waiting for reply (ACK or FIN)
@@ -102,9 +102,10 @@ enum eTCPConnectionState
 	TCP_ST_CLOSING, 	// 6 - Waiting for ACK of FIN (FIN sent and recieved)
 	TCP_ST_TIME_WAIT,	// 7 - Waiting for timeout after local close
 	// Remote close
-	TCP_ST_CLOSE_WAIT,	// 8 - FIN recieved, waiting for user to close (error set, wait for node close)
-	TCP_ST_LAST_ACK,	// 9 - FIN sent and recieved, waiting for ACK
-	TCP_ST_FINISHED 	// 10 - Essentially closed, all packets are invalid
+	TCP_ST_FORCE_CLOSE,	// 8  - RST recieved, waiting for user close
+	TCP_ST_CLOSE_WAIT,	// 9  - FIN recieved, waiting for user to close (error set, wait for node close)
+	TCP_ST_LAST_ACK,	// 10 - FIN sent and recieved, waiting for ACK
+	TCP_ST_FINISHED 	// 11 - Essentially closed, all packets are invalid
 };
 
 struct sTCPConnection
diff --git a/KernelLand/Modules/IPStack/udp.c b/KernelLand/Modules/IPStack/udp.c
index 325a09e86c6d5a3ad82a241583d3b90a82ddd438..9037439cf80023b273974d12ae2c5a512ee9fa51 100644
--- a/KernelLand/Modules/IPStack/udp.c
+++ b/KernelLand/Modules/IPStack/udp.c
@@ -15,7 +15,7 @@
 // === PROTOTYPES ===
 void	UDP_Initialise();
 void	UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
-void	UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer);
+void	UDP_IPError(tInterface *Interface, tIPErrorMode Code, const void *Address, int Length, const void *Buffer);
 void	UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, Uint16 Port, const void *Data, size_t Length);
 // --- Client Channels
 tVFS_Node	*UDP_Channel_Init(tInterface *Interface);
@@ -33,6 +33,8 @@ Uint16	UDP_int_FinaliseChecksum(Uint16 Value);
 
 // === GLOBALS ===
 tVFS_NodeType	gUDP_NodeType = {
+	.TypeName = "UDP",
+	.Flags = VFS_NODETYPEFLAG_STREAM,
 	.Read = UDP_Channel_Read,
 	.Write = UDP_Channel_Write,
 	.IOCtl = UDP_Channel_IOCtl,
@@ -54,8 +56,7 @@ tSocketFile	gUDP_SocketFile = {NULL, "udp", UDP_Channel_Init};
 void UDP_Initialise()
 {
 	IPStack_AddFile(&gUDP_SocketFile);
-	//IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket, UDP_Unreachable);
-	IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket);
+	IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket, UDP_IPError);
 }
 
 /**
@@ -65,13 +66,15 @@ void UDP_Initialise()
 int UDP_int_ScanList(tUDPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer)
 {
 	tUDPHeader	*hdr = Buffer;
-	tUDPChannel	*chan;
-	tUDPPacket	*pack;
-	 int	len;
 	
-	for(chan = List; chan; chan = chan->Next)
+	for(tUDPChannel *chan = List; chan; chan = chan->Next)
 	{
 		// Match local endpoint
+		LOG("(%p):%i - %s/%i:%i",
+			chan->Interface, chan->LocalPort,
+			IPStack_PrintAddress(chan->Remote.AddrType, &chan->Remote.Addr), chan->RemoteMask,
+			chan->Remote.Port
+			);
 		if(chan->Interface && chan->Interface != Interface)	continue;
 		if(chan->LocalPort != ntohs(hdr->DestPort))	continue;
 		
@@ -91,8 +94,8 @@ int UDP_int_ScanList(tUDPChannel *List, tInterface *Interface, void *Address, in
 		
 		Log_Log("UDP", "Recieved packet for %p", chan);
 		// Create the cached packet
-		len = ntohs(hdr->Length);
-		pack = malloc(sizeof(tUDPPacket) + len);
+	 	int len = ntohs(hdr->Length);
+		tUDPPacket *pack = malloc(sizeof(tUDPPacket) + len);
 		pack->Next = NULL;
 		memcpy(&pack->Remote.Addr, Address, IPStack_GetAddressSize(Interface->Type));
 		pack->Remote.Port = ntohs(hdr->SourcePort);
@@ -142,7 +145,7 @@ void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe
 /**
  * \brief Handle an ICMP Unrechable Error
  */
-void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer)
+void UDP_IPError(tInterface *Interface, tIPErrorMode Code, const void *Address, int Length, const void *Buffer)
 {
 	
 }
@@ -157,7 +160,11 @@ void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, U
 {
 	tUDPHeader	hdr;
 
-	if(Channel->Interface && Channel->Interface->Type != AddrType)	return ;
+	if(Channel->Interface && Channel->Interface->Type != AddrType) {
+		LOG("Bad interface type for channel packet, IF is %i, but packet is %i",
+			Channel->Interface->Type, AddrType);
+		return ;
+	}
 	
 	// Create the packet
 	hdr.SourcePort = htons( Channel->LocalPort );
@@ -175,6 +182,7 @@ void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, U
 		IPStack_Buffer_AppendSubBuffer(buffer, Length, 0, Data, NULL, NULL);
 		IPStack_Buffer_AppendSubBuffer(buffer, sizeof(hdr), 0, &hdr, NULL, NULL);
 		// TODO: What if Channel->Interface is NULL here?
+		ASSERT(Channel->Interface);
 		IPv4_SendPacket(Channel->Interface, *(tIPv4*)Address, IP4PROT_UDP, 0, buffer);
 		break;
 	default:
@@ -189,6 +197,7 @@ tVFS_Node *UDP_Channel_Init(tInterface *Interface)
 	tUDPChannel	*new;
 	new = calloc( sizeof(tUDPChannel), 1 );
 	new->Interface = Interface;
+	new->Node.Size = -1;
 	new->Node.ImplPtr = new;
 	new->Node.NumACLs = 1;
 	new->Node.ACLs = &gVFS_ACL_EveryoneRW;
@@ -202,49 +211,59 @@ tVFS_Node *UDP_Channel_Init(tInterface *Interface)
 	return &new->Node;
 }
 
-/**
- * \brief Read from the channel file (wait for a packet)
- */
-size_t UDP_Channel_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
+tUDPPacket *UDP_Channel_WaitForPacket(tUDPChannel *chan, Uint VFSFlags)
 {
-	tUDPChannel	*chan = Node->ImplPtr;
-	tUDPPacket	*pack;
-	tUDPEndpoint	*ep;
-	 int	ofs, addrlen;
-	
-	if(chan->LocalPort == 0) {
-		Log_Notice("UDP", "Channel %p sent with no local port", chan);
-		return 0;
-	}
-	
-	while(chan->Queue == NULL)	Threads_Yield();
+	// EVIL - Yield until queue is created (avoids races)
+	while(chan->Queue == NULL)
+		Threads_Yield();
 	
 	for(;;)
 	{
-		tTime	timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
-		int rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "UDP_Channel_Read");
-		if( rv ) {
-			errno = (Flags & VFS_IOFLAG_NOBLOCK) ? EWOULDBLOCK : EINTR;
+		tTime	timeout_z = 0, *timeout = (VFSFlags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
+		int rv = VFS_SelectNode(&chan->Node, VFS_SELECT_READ, timeout, "UDP_Channel_Read");
+		if( rv == 0 ) {
+			errno = (VFSFlags & VFS_IOFLAG_NOBLOCK) ? EWOULDBLOCK : EINTR;
+			return NULL;
 		}
 		SHORTLOCK(&chan->lQueue);
 		if(chan->Queue == NULL) {
 			SHORTREL(&chan->lQueue);
 			continue;
 		}
-		pack = chan->Queue;
+		tUDPPacket *pack = chan->Queue;
 		chan->Queue = pack->Next;
 		if(!chan->Queue) {
 			chan->QueueEnd = NULL;
-			VFS_MarkAvaliable(Node, 0);	// Nothing left
+			VFS_MarkAvaliable(&chan->Node, 0);	// Nothing left
 		}
 		SHORTREL(&chan->lQueue);
-		break;
+		return pack;
+	}
+	// Unreachable
+}
+
+/**
+ * \brief Read from the channel file (wait for a packet)
+ */
+size_t UDP_Channel_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
+{
+	tUDPChannel	*chan = Node->ImplPtr;
+	
+	if(chan->LocalPort == 0) {
+		Log_Notice("UDP", "Channel %p sent with no local port", chan);
+		return 0;
+	}
+	
+	tUDPPacket	*pack = UDP_Channel_WaitForPacket(chan, Flags);
+	if( !pack ) {
+		return 0;
 	}
 
+	size_t addrlen = IPStack_GetAddressSize(pack->Remote.AddrType);
+	tUDPEndpoint *ep = Buffer;
+	size_t ofs = 4 + addrlen;
+	
 	// Check that the header fits
-	addrlen = IPStack_GetAddressSize(pack->Remote.AddrType);
-	ep = Buffer;
-	ofs = 4 + addrlen;
 	if(Length < ofs) {
 		free(pack);
 		Log_Notice("UDP", "Insuficient space for header in buffer (%i < %i)", (int)Length, ofs);
@@ -300,6 +319,8 @@ static const char *casIOCtls_Channel[] = {
 	"getset_remoteport",
 	"getset_remotemask",
 	"set_remoteaddr",
+	"sendto",
+	"recvfrom",
 	NULL
 	};
 /**
@@ -351,14 +372,21 @@ int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data)
 	
 	case 6:	// getset_remotemask (returns bool success)
 		if(!Data)	LEAVE_RET('i', chan->RemoteMask);
-		if(!CheckMem(Data, sizeof(int)))	LEAVE_RET('i', -1);
+		if(!CheckMem(Data, sizeof(int))) {
+			LOG("Data pointer invalid");
+			LEAVE_RET('i', -1);
+		}
 		if( !chan->Interface ) {
 			LOG("Can't set remote mask on NULL interface");
 			LEAVE_RET('i', -1);
 		}
-		if( *(int*)Data > IPStack_GetAddressSize(chan->Interface->Type) )
+		 int	mask = *(int*)Data;
+		 int	addr_bits = IPStack_GetAddressSize(chan->Interface->Type) * 8;
+		if( mask > addr_bits ) {
+			LOG("Mask too large (%i > max %i)", mask, addr_bits);
 			LEAVE_RET('i', -1);
-		chan->RemoteMask = *(int*)Data;
+		}
+		chan->RemoteMask = mask;
 		LEAVE('i', chan->RemoteMask);
 		return chan->RemoteMask;	
 
@@ -371,9 +399,73 @@ int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data)
 			LOG("Invalid pointer");
 			LEAVE_RET('i', -1);
 		}
+		LOG("Set remote addr %s", IPStack_PrintAddress(chan->Interface->Type, Data));
+		chan->Remote.AddrType = chan->Interface->Type;
 		memcpy(&chan->Remote.Addr, Data, IPStack_GetAddressSize(chan->Interface->Type));
 		LEAVE('i', 0);
 		return 0;
+	case 8: {	// sendto
+		if(!CheckMem(Data, 2*sizeof(void*)+2)) {
+			LOG("Data pointer invalid");
+			LEAVE_RET('i', -1);
+		}
+		const struct sSendToArgs {
+			const tUDPEndpoint* ep;
+			const void* buf;
+			const Uint16 buflen;
+		} info = *(const struct sSendToArgs*)Data;
+		LOG("sendto(buf=%p + %u, ep=%p)", info.buf, info.buflen, info.ep);
+		if(!CheckMem(info.ep, 2+2) || !CheckMem(info.ep, 2+2+IPStack_GetAddressSize(info.ep->AddrType)) ) {
+			LEAVE_RET('i', -1);
+		}
+		if(!CheckMem(info.buf, info.buflen)) {
+			LEAVE_RET('i', -1);
+		}
+		
+		UDP_SendPacketTo(chan, info.ep->AddrType, &info.ep->Addr, info.ep->Port,
+			info.buf, (size_t)info.buflen);
+		
+		LEAVE_RET('i', info.buflen); }
+	case 9: {	// recvfrom
+		if(!CheckMem(Data, 2*sizeof(void*)+2)) {
+			LOG("Data pointer invalid");
+			LEAVE_RET('i', -1);
+		}
+		const struct sRecvFromArgs {
+			tUDPEndpoint* ep;
+			void* buf;
+			Uint16 buflen;
+		} info = *(const struct sRecvFromArgs*)Data;
+		LOG("recvfrom(buf=%p + %u, ep=%p)", info.buf, info.buflen, info.ep);
+		if(!CheckMem(info.ep, 2+2)) {
+			LEAVE_RET('i', -1);
+		}
+		if(!CheckMem(info.buf, info.buflen)) {
+			LEAVE_RET('i', -1);
+		}
+		
+		tUDPPacket	*pack = UDP_Channel_WaitForPacket(chan, 0);
+		if( pack == NULL ) {
+			LOG("No packet");
+			LEAVE_RET('i', 0);
+		}
+		
+		size_t	addrsize = IPStack_GetAddressSize(pack->Remote.AddrType);
+		if( !CheckMem(info.ep, 2+2+addrsize) ) {
+			LOG("Insufficient space for source address");
+			free(pack);
+			LEAVE_RET('i', -1);
+		}
+		info.ep->Port = pack->Remote.Port;
+		info.ep->AddrType = pack->Remote.AddrType;
+		memcpy(&info.ep->Addr, &pack->Remote.Addr, addrsize);
+		
+		size_t	retlen = (info.buflen < pack->Length ? info.buflen : pack->Length);
+		memcpy(info.buf, pack->Data, retlen);
+
+		free(pack);
+	
+		LEAVE_RET('i', retlen); }
 	}
 	LEAVE_RET('i', 0);
 }
diff --git a/KernelLand/Modules/Input/Keyboard/main.c b/KernelLand/Modules/Input/Keyboard/main.c
index 377ae309402c209f76b3f2a5a0c293eb7959caec..32688df28cc4a082c57017e7cf1a3c7777b54304 100644
--- a/KernelLand/Modules/Input/Keyboard/main.c
+++ b/KernelLand/Modules/Input/Keyboard/main.c
@@ -117,19 +117,16 @@ int Keyboard_IOCtl(tVFS_Node *Node, int Id, void *Data)
  */
 tKeyboard *Keyboard_CreateInstance(int MaxSym, const char *Name)
 {
-	tKeyboard	*ret;
-	 int	sym_bitmap_size = (MaxSym + 7)/8;
-	 int	string_size = strlen(Name) + 1;
+	size_t	sym_bitmap_size = (MaxSym + 7)/8;
+	size_t	string_size = strlen(Name) + 1;
 
-	ret = malloc( sizeof(tKeyboard) + sym_bitmap_size + string_size );
+	tKeyboard *ret = calloc( 1, sizeof(tKeyboard) + sym_bitmap_size + string_size );
 	if( !ret ) {
 		return NULL;
 	}
-	// Clear
-	memset(ret, 0, sizeof(tKeyboard) + sym_bitmap_size );
 	// Set name
-	ret->Name = (char*)ret + sizeof(tKeyboard) + sym_bitmap_size;
-	memcpy(ret->Name, Name, string_size);
+	ret->Name = (char*)( &ret->KeyStates[sym_bitmap_size] );
+	strcpy(ret->Name, Name);
 	// Set node and default keymap
 	ret->Node = &gKB_DevInfo.RootNode;
 	ret->Keymap = &gKeymap_KBDUS;
diff --git a/KernelLand/Modules/Input/PS2KbMouse/8042.c b/KernelLand/Modules/Input/PS2KbMouse/8042.c
index f71fff7ffe263041a322f7266908aa5247bcd881..d76322cbbc81b8b239e7c8bf68cbba8d62d5fe5a 100644
--- a/KernelLand/Modules/Input/PS2KbMouse/8042.c
+++ b/KernelLand/Modules/Input/PS2KbMouse/8042.c
@@ -24,10 +24,9 @@ void KBC8042_Init(void)
 	IRQ_AddHandler(12, KBC8042_MouseHandler, NULL);	// Set IRQ
 	
 	{
-		Uint8	temp;
 		// Attempt to get around a strange bug in Bochs/Qemu by toggling
 		// the controller on and off
-		temp = inb(0x61);
+		Uint8 temp = inb(0x61);
 		outb(0x61, temp | 0x80);
 		outb(0x61, temp & 0x7F);
 		inb(0x60);	// Clear keyboard buffer
diff --git a/KernelLand/Modules/Interfaces/EDI/Makefile b/KernelLand/Modules/Interfaces/EDI/Makefile
deleted file mode 100644
index a93a8b7c2a78ce84f0ab01bb683ba0156894b2bb..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-# 
-# EDI - Extensible Driver Interface
-# 
-# Acess Interface
-
-
-OBJ = main.o edi.o
-NAME = EDI
-
--include ../Makefile.tpl
diff --git a/KernelLand/Modules/Interfaces/EDI/edi/acess-edi.h b/KernelLand/Modules/Interfaces/EDI/edi/acess-edi.h
deleted file mode 100644
index 4071388642a58ac1656435cd4e70467ef32debbf..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi/acess-edi.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*! \file acess-edi.h
- * \brief Acess Specific EDI Objects
- * 
- * Contains documentation and information for
- * - Timers
- */
-
-/* Copyright (c)  2006  John Hodge
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts.  A copy of the license is included in the file entitled "COPYING". */
-
-#ifndef ACESS_EDI_H
-#define ACESS_EDI_H
-
-#include "edi_objects.h"
-
-/// \brief Name of Acess EDI Time Class
-#define	ACESS_TIMER_CLASS	"ACESSEDI-TIMER"
-
-#ifndef IMPLEMENTING_EDI
-/*! \brief int32_t ACESSEDI-TIMER.init_timer(uint32_t Delay, void (*Callback)(int), int Arg);
- *
- * Takes a timer pointer and intialises the timer object to fire after \a Delay ms
- * When the timer fires, \a Callback is called with \a Arg passed to it.
- */
-EDI_DEFVAR int32_t (*init_timer)(object_pointer port_object, uint32_t Delay, void (*fcn)(int), int arg);
-
-/*! \brief void ACESSEDI-TIMER.disable_timer();
- * 
- * Disables the timer and prevents it from firing
- * After this has been called, the timer can then be initialised again.
- */
-EDI_DEFVAR void (*disable_timer)(object_pointer port_object);
-
-
-#endif // defined(IMPLEMENTING_EDI)
-
-#endif
diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi.h b/KernelLand/Modules/Interfaces/EDI/edi/edi.h
deleted file mode 100644
index 273f7a3dc35e88166c7ee070322a88f95c1c9779..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi/edi.h
+++ /dev/null
@@ -1,114 +0,0 @@
-#ifndef EDI_H
-
-/* Copyright (c)  2006  Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts.  A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_H
-/*! \file edi.h
- * \brief The unitive EDI header to include others, start EDI, and stop EDI.
- *
- * Data structures and algorithms this header represents:
- *	DATA STRUCTURE: CLASS QUOTAS - The runtime and the driver have the right to set a quota on how many objects of a given class
- * owned by that party the other may construct.  These quotas are kept internally by the driver or runtime, are optional and are
- * exposed to the other party via the quota() function (for quotas of runtime-owned classes) and the k_quota() function pointer given
- * to the runtime by the driver.
- *
- *	ALGORITHMS: INITIALIZATION AND SHUTDOWN - On initialization of the runtime's EDI environment for this driver it calls the
- * driver's driver_init() routine (which must match driver_init_t) to initialize the driver with a list of EDI objects the runtime
- * thinks the driver should run with.  The driver then initializes.  This can include calling edi_negotiate_resources() to try and
- * obtain more or different objects.  Eventually driver_init() returns an edi_initialization_t structure containing its quota
- * function and the list of classes belonging to the driver which the runtime can construct.  Either the driver or the runtime can
- * shut down EDI by calling edi_shutdown(), which in turn calls the driver's driver_finish() routine.  On shutdown all objects, of
- * classes belonging to both the runtime and driver, are destroyed. */
-
-#include "edi_objects.h"
-#include "edi_dma_streams.h"
-#include "edi_pthreads.h"
-#include "edi_port_io.h"
-#include "edi_memory_mapping.h"
-#include "edi_devices.h"
-#include "edi_interrupts.h"
-
-/*! \brief A pointer to a function the runtime can call if it fails to construct one of the driver's classes to find out what the
- * runtime's quota is for that class.
- *
- * A pointer to a function which takes an edi_string_t as a parameter and returns in int32_t.  This function follows the same
- * semantics as the quota() function, returning the number of objects of the given class that can be constructed, -1 for infinity or
- * -2 for an erroneous class name.  It is used to tell the runtime the location of such a function in the driver so that the runtime
- * can check quotas on driver-owned classes. */
-typedef int32_t (*k_quota_t)(edi_string_t resource_class);
-/*!\struct edi_initialization_t
- * \brief Structure containing driver classes available to the runtime and the driver's quota function after the driver has initialized. 
- *
- * Structure containing driver classes available to runtime, the driver's quota function and the driver's name provided to the runtime
- * after the driver has initialized.  driver_bus, vendor_id, and device_id are all optional fields which coders should consider
- * supplementary information.  Kernels can require these fields if they so please, but doing so for devices which don't run on a Vendor
- * ID/Product ID supporting bus is rather unwise. */
-typedef struct {
-	/*!\brief The number of driver classes in the driver_classes array. */
-	int32_t	num_driver_classes;
-	/*!\brief An array of declarations of driver classes available to the runtime. 
-	 *
-	 * This array should not necessarily contain the entire list of EDI classes implemented by the driver.  Instead, it should
-	 * contain a list of those classes which the driver has correctly initialized itself to provide instances of with full
-	 * functionality. */
-	edi_class_declaration_t *driver_classes;
-	/*!\brief The driver's quota function. */
-	k_quota_t k_quota;
-	/*!\brief The driver's name. */
-	edi_string_t driver_name;
-	/*!\brief The bus of the device this driver wants to drive, if applicable.
-	 *
-	 * The driver does not have to supply this field, and can also supply "MULTIPLE BUSES" here to indicate that it drives devices
-	 * on multiple buses. */
-	edi_string_t driver_bus;
-	/*!\brief The driver's vendor ID, if applicable.
-	 *
-	 * The driver does not need to supply this field, and should supply -1 to indicate that it does not wish to. */
-	int16_t vendor_id;
-	/*!\brief The driver's device ID, if applicable.
-	 *
-	 * The driver does not need to supply this field, but can supply it along with vendor_id.  If either vendor_id or this field are
-	 * set to -1 the runtime should consider this field not supplied. */
-	int16_t driver_id;
-} edi_initialization_t;	
-/*!\brief A pointer to a driver's initialization function.
- *
- * The protocol for the driver's initialization function.  The runtime gives the driver a set of EDI objects representing the
- * resources it thinks the driver should run with.  This function returns an edi_initialization_t structure containing declarations
- * of the EDI classes the driver can make available to the runtime after initialization.  If any member of that structure contains 0
- * or NULL, it is considered invalid and the runtime should destroy the driver without calling its driver_finish() routine. */
-typedef edi_initialization_t (*driver_init_t)(int32_t num_resources,edi_object_metadata_t *resources);
-/*!\brief Requests more resources from the runtime.  Can be called during driver initialization.
- *
- * Called to negotiate with the runtime for the right to create further EDI objects/obtain further resources owned by the runtime.
- * When the driver calls this routine, the runtime decides whether to grant more resources.  If yes, this call returns true, and the
- * driver can proceed to try and create the objects it desires, in addition to destroying EDI objects it doesn't want.  Otherwise,
- * it returns false.
- * The driver must deal with whatever value this routine returns. */
-bool edi_negotiate_resources();
-
-/*! \brief Returns the driver's quota of objects for a given runtime-owned class. 
- *
- * This function takes an edi_string_t with the name of a runtime-owned class in it and returns the number of objects of that class
- * which drivers can construct, -1 for infinity, or -2 for an erroneous class name. */
-int32_t quota(edi_string_t resource_class);
-/*! \brief Sends a string to the operating systems debug output or logging facilities. */
-void edi_debug_write(uint32_t debug_string_length, char *debug_string);
-/*! \brief This call destroys all objects and shuts down the entire EDI environment of the driver. 
- *
- * This function shuts down EDI as described in INITIALIZATION AND SHUTDOWN above.  All objects are destroyed, EDI functions can no
- * longer be successfully called, etc.  This function only succeeds when EDI has already been initialized, so it returns -1 when EDI
- * hasn't been, 1 on success, or 0 for all other errors. */
-int32_t shutdown_edi(void);
-
-/*!\brief A pointer to the driver's finishing/shutdown function.
- *
- * The protocol for the driver's shutting down.  This function should do anything the driver wants done before it dies. */
-typedef void (*driver_finish_t)();
-
-#endif
diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_devices.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_devices.h
deleted file mode 100644
index 245e01f3ba4bbb16526d532936a1aa62ed7254b7..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi/edi_devices.h
+++ /dev/null
@@ -1,135 +0,0 @@
-#ifndef EDI_DEVICES_H
-
-/* Copyright (c)  2006  Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts.  A copy of the license is included in the file entitled "COPYING". */
-
-/* Edited by thePowersGang (John Hodge) June 2009
- * - Add #ifdef EDI_MAIN_FILE
- */
-
-#define EDI_DEVICES_H
-
-/*! \file edi_devices.h
- * \brief Declaration and description of simple classes for implementation by EDI drivers to represent hardware devices.
- *
- * Data structures and algorithms this header represents:
- *
- *	DATA STRUCTURE AND ALGORITHM: BASIC DEVICES - There are two functions, select() for waiting on devices and ioctl() for
- * controlling them, common to many POSIX devices.  Implementations of EDI-CHARACTER-DEVICE or EDI-BLOCK-DEVICE may implement either of
- * these or both, and users of such objects much query for the methods to see if they're supported.  Obviously, runtime or driver
- * developers don't *need* to support these.
- *
- *	DATA STRUCTURE AND ALGORITHM: CHARACTER DEVICES - The class EDI-CHARACTER-DEVICE provides a very basic interface to character
- * devices, which read and write streams of characters.  As such, this class only provides read() and write().  The calls attempt a
- * likeness to POSIX.
- *
- *	DATA STRUCTURE AND ALGORITHM: BLOCK DEVICES - The class EDI-BLOCK-DEVICE provides a very basic interface to block devices, which
- * can read(), write() and seek() to blocks of a specific size in an array of blocks with a specific size.  Its declarations and
- * semantics should behave like those of most POSIX operating systems.
- *
- * Note that EDI runtimes should not implement these classes.  Their declarations are provided for drivers to implement. */
-
-#include "edi_objects.h"
-
-/* Methods common to all EDI device classes specified in this header. */
-
-/*!\brief EAGAIN returned by functions for block and character devices.
- *
- * Means that the amount of data the device has ready is less than count. */
-#define EAGAIN -1
-/*!\brief EBADOBJ returned by functions for block and character devices.
- *
- * Means that the object passed as the method's this point was not a valid object of the needed class. */
-#define EBADOBJ -2
-/*!\brief EINVAL returned by functions for block and character devices.
- *
- * Means that the method got passed invalid parameters. */
-#ifdef EINVAL
-# undef EINVAL
-#endif
-#define EINVAL -3
-
-/*!\brief select() type to wait until device is writable. */
-#define EDI_SELECT_WRITABLE 0
-/*!\brief select() type to wait until device is readable. */
-#define EDI_SELECT_READABLE 1
-
-/*!\brief Argument to seek().  Sets the block offset (ie: the "current block" index) to the given whence value. */
-#define EDI_SEEK_SET 0
-/*!\brief Argument to seek().  Sets the block offset (ie: the "current block" index) to its current value + whence. */
-#define EDI_SEEK_CURRENT 1
-
-#ifdef EDI_MAIN_FILE
-/*!\brief Arguments to EDI's basic select() function. */
-edi_variable_declaration_t select_arguments[2] = {{"pointer void","device",1},
-						 {"unsigned int32_t","select_type",1}};
-/*!\brief Declaration of EDI's basic select() function. 
- *
- * Contrary to the POSIX version, this select() puts its error codes in its return value. */
-edi_function_declaration_t select_declaration = {"int32_t","edi_device_select",0,2,select_arguments,NULL};
-#else
-extern edi_function_declaration_t select_declaration;	// Declare for non main files
-#endif
-
-#ifdef EDI_MAIN_FILE
-/*!\brief Arguments to EDI's basic ioctl() function. */
-edi_variable_declaration_t ioctl_arguments[3] = {{"pointer void","device",1},{"int32_t","request",1},{"pointer void","argp",1}};
-/*!\brief Declaration of EDI's basic ioctl() function. 
- *
- * Contrary to the POSIX version, this ioctl() puts its error codes in its return value. */
-edi_function_declaration_t ioctl_declaration = {"int32_t","edi_device_ioctl",0,3,ioctl_arguments,NULL};
-#else
-extern edi_class_declaration_t ioctl_declaration;	// Declare for non main files
-#endif
-
-#ifdef EDI_MAIN_FILE
-/*!\brief Declaration of the arguments EDI-CHARACTER-DEVICE's read() and write() methods. */
-edi_variable_declaration_t chardev_read_write_arguments[3] = {{"pointer void","chardev",1},
-							      {"pointer void","buffer",1},
-							      {"unsigned int32_t","char_count",1}};
-/*!\brief Declarations of the methods of EDI-CHARACTER-DEVICE, read() and write().
- *
- * The code pointers of these function declarations are all given as NULL.  Driver developers implementing EDI-CHARACTER-DEVICE should
- * fill in these entries with pointers to their own functions. */
-EDI_DEFVAR edi_function_declaration_t chardev_methods[2]= {{"int32_t","edi_chardev_read",0,3,chardev_read_write_arguments,NULL},
-						{"int32_t","edi_chardev_write",0,3,chardev_read_write_arguments,NULL}};
-/*!\brief Declaration of the EDI-CHARACTER-DEVICE class.
- *
- * Driver developers implementing this class should fill in their own values for constructor, destructor, and possibly even parent
- * before passing the filled-in structure to the EDI runtime. */
-EDI_DEFVAR edi_class_declaration_t chardev_class = {"EDI-CHARACTER-DEVICE",0,2,chardev_methods,NULL,NULL,NULL};
-#else
-extern edi_class_declaration_t chardev_class;	// Declare for non main files
-#endif
-
-#ifdef EDI_MAIN_FILE
-/*!\brief Arguments to EDI-BLOCK-DEVICE's read() and write() methods. */
-edi_variable_declaration_t blockdev_read_write_arguments[3] = {{"pointer void","blockdev",1},
-							       {"pointer void","buffer",1},
-							       {"unsigned int32_t","blocks",1}};
-/*!\brief Arguments to EDI-BLOCK-DEVICE's seek() method. */
-edi_variable_declaration_t blockdev_seek_arguments[3] = {{"pointer void","blockdev",1},
-							 {"int32_t","offset",1},
-							 {"int32_t","whence",1}};
-/*!\brief Declaration of the methods of EDI-BLOCK-DEVICE, read(), write(), seek(), and get_block_size(). 
- *
- * The code pointers of these function declarations are all given as NULL.  Driver developers implementing EDI-BLOCK-DEVICE should fill
- * these entries in with pointers to their own functions. */
-edi_function_declaration_t blockdev_methods[4] = {{"int32_t","edi_blockdev_read",0,3,blockdev_read_write_arguments,NULL},
-						  {"int32_t","edi_blockdev_write",0,3,blockdev_read_write_arguments,NULL},
-						  {"int32_t","edi_blockdev_seek",0,3,blockdev_seek_arguments,NULL},
-						  {"unsigned int32_t","edi_blockdev_get_block_size",0,0,NULL,NULL}};
-/*!\brief Declaration of the EDI-BLOCK-DEVICE class.
- *
- * Driver developers implementing this class should fill in their own values for constructor, destructor, and possibly even parent
- * before passing the filled-in structure to the EDI runtime. */
-edi_class_declaration_t blockdev_class = {"EDI-BLOCK-DEVICE",0,4,blockdev_methods,NULL,NULL,NULL};
-#else
-extern edi_class_declaration_t blockdev_class;	// Declare for non main files
-#endif
-
-#endif
diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_dma_streams.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_dma_streams.h
deleted file mode 100644
index 8ab80ccbff472552c5c7de2b6f4efdf7aaa9e074..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi/edi_dma_streams.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef EDI_DMA_STREAMS_H
-
-/* Copyright (c)  2006  Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts.  A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_DMA_STREAMS_H
-
-/*! \file edi_dma_streams.h 
- * \brief EDI's stream subclass for handling Direct Memory Access hardware.
- *
- * Data structures and algorithms this header represents:
- *
- *	DATA STRUCTURE: DMA STREAMS - DMA streams are objects of the class EDI-STREAM-DMA used to pass data between a buffer of
- * memory and the computer's DMA hardware.  It is the responsibility of the object to allocate memory for its stream memory buffer
- * which can be used with DMA hardware and to program the DMA hardware for transmissions.  DMA streams can be bidirectional if the
- * correct DMA mode is used. */
-
-#include "edi_objects.h"
-
-#define DMA_STREAM_CLASS	"EDI-STREAM-DMA"
-
-/*! \brief The name of the EDI DMA stream class.
- *
- * An edi_string_t with the class name "EDI-STREAM-DMA" in it. */
-#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
-const edi_string_t dma_stream_class = DMA_STREAM_CLASS;
-#else
-extern const edi_string_t dma_stream_class;
-#endif
-
-#ifndef IMPLEMENTING_EDI
-/*! \brief int32_t EDI-STREAM-DMA.init_dma_stream(unsigned int32_t channel,unsigned int32_t mode,unsigned int32_t buffer_pages);
- *
- * Pointer to the init_dma_stream() method of class EDI-STREAM-DMA, which initializes a DMA stream with a DMA channel, DMA mode, and
- * the number of DMA-accessible memory pages to keep as a buffer.  It will only work once per stream object.  It's possible return
- * values are 1 for sucess, -1 for invalid DMA channel, -2 for invalid DMA mode, -3 for inability to allocate enough buffer pages and
- * 0 for all other errors. */
-EDI_DEFVAR  int32_t (*init_dma_stream)(object_pointer stream, uint32_t channel, uint32_t mode, uint32_t buffer_pages);
-/*! \brief int32_t EDI-STREAM-DMA.transmit(data_pointer *anchor,unsigned int32 num_bytes,bool sending);
- *
- * Pointer to the dma_stream_transmit() method of class EDI-STREAM-DMA, which transmits the given number of bytes of data through
- * the DMA stream to/from the given anchor (either source or destination), in the given direction.  It returns 1 on success, -1 on
- * an uninitialized or invalid DMA stream object, -2 when the anchor was NULL or otherwise invalid, -3 if the DMA stream can't
- * transmit in the given direction, and 0 for all other errors. */
-EDI_DEFVAR int32_t (*dma_stream_transmit)(object_pointer stream, data_pointer anchor, uint32_t num_bytes, bool sending);
-#endif
-
-#endif
diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_interrupts.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_interrupts.h
deleted file mode 100644
index ef2ffc9f0887e5eb627658acede34fac9776e3fb..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi/edi_interrupts.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef EDI_INTERRUPTS_H
-
-/* Copyright (c)  2006  Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts.  A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_INTERRUPTS_H
-
-/*! \file edi_interrupts.h
- * \brief Declaration and description of EDI's interrupt handling class.
- *
- * Data structures and algorithms this header represents:
- *	DATA STRUCTURE AND ALGORITHM: INTERRUPT OBJECTS - The class EDI-INTERRUPT encapsulates the handling of machine interrupts.
- * It is initialized with an interrupt number to handle and a handler routine to call when that interrupt occurs.  Only a couple of
- * guarantees are made to the driver regarding the runtime's implementation of interrupt handling: 1) That the driver's handler is
- * called for every time the interrupt associated with a valid and initialized interrupt object occurs, in the order of the
- * occurences, 2) That the runtime handle the architecture-specific (general to the entire machine, not just this device)
- * end-of-interrupt code when the driver is called without first returning from the machine interrupt.  Note that the runtime hands
- * out interrupt numbers at its own discretion and policy. */
-
-#include "edi_objects.h"
-
-/*! \brief Macro constant containing the name of the interrupt class
- */
-#define INTERRUPTS_CLASS	"EDI-INTERRUPT"
-/*! \brief The name of EDI's interrupt-handling class.
- *
- * An edi_string_t holding the name of the runtime-implemented interrupt object class.  It's value is "EDI-INTERRUPT". */
-#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
-const edi_string_t interrupts_class = INTERRUPTS_CLASS;
-#else
-extern const edi_string_t interrupts_class;
-#endif
-
-/*! \brief A pointer to an interrupt handling function.
- *
- * A pointer to a function called to handle interrupts.  Its unsigned int32_t parameter is the interrupt number that is being
- * handled. */
-typedef void (*interrupt_handler_t)(uint32_t interrupt_number);
-
-#ifndef IMPLEMENTING_EDI
-/*! \brief Initializes an interrupt object with an interrupt number and a pointer to a handler function.
- *
- * A pointer to the init_interrupt() method of class EDI-INTERRUPT.  This method initializes a newly-created interrupt object with an
- * interrupt number and a pointer to the driver's handler of type interrupt_handler_t.  It can only be called once per object, and
- * returns 1 on success, fails with -1 when the interrupt number is invalid or unacceptable to the runtime, fails with -2 when the
- * pointer to the driver's interrupt handler is invalid, and fails with -3 for all other errors. */
-EDI_DEFVAR int32_t (*init_interrupt)(object_pointer interrupt, uint32_t interrupt_number, interrupt_handler_t handler);
-/*! \brief Get this interrupt object's interrupt number. */
-EDI_DEFVAR uint32_t (*interrupt_get_irq)(object_pointer interrupt);
-/*! \brief Set a new handler for this interrupt object. */
-EDI_DEFVAR void (*interrupt_set_handler)(object_pointer interrupt, interrupt_handler_t handler);
-/*! \brief Return from this interrupt, letting the runtime run any necessary End-Of-Interrupt code.
- *
- * A pointer to the interrupt_return() method of class EDI-INTERRUPT.  This method returns from the interrupt designated by the
- * calling interrupt object.  If there is a machine-wide end-of-interrupt procedure and the driver was called during the handling of
- * the machine interrupt (as opposed to delaying the handling and letting the runtime EOI), the runtime runs it during this method.
- * This method has no return value, since once it's called control leaves the calling thread. */
-EDI_DEFVAR void (*interrupt_return)(object_pointer interrupt);
-#endif
-
-#endif
diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_memory_mapping.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_memory_mapping.h
deleted file mode 100644
index ba8dff357b424ac9fc40b60342af4046ec65dcb9..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi/edi_memory_mapping.h
+++ /dev/null
@@ -1,86 +0,0 @@
-#ifndef EDI_MEMORY_MAPPING_H
-
-/* Copyright (c)  2006  Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts.  A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_MEMORY_MAPPING_H
-
-/*! \file edi_memory_mapping.h
- * \brief Declaration and description of EDI's class for mapping physical pages into the driver's address space.
- *
- * Data structures and algorithms this header represents:
- *	ALGORITHM: MEMORY MAPPINGS - Memory mapping objects of the class EDI-MEMORY-MAPPING are used to give virtual (driver-visible)
- * addresses to sections of physical memory.  These can either be memory mappings belonging to hardware devices or plain RAM which
- * the driver wants page-aligned.  A memory mapping object is initialized with the physical address for the memory mapping and the
- * number of pages the mapping takes up, or simply the desired length of the a physically contiguous buffer in pages.  The class's
- * two methods map the section of memory into and out of the driver's virtual address space. */
-
-#include "edi_objects.h"
-
-/*! \brief The name of EDI's memory mapping class.
- *
- * An edi_string_t with the name of the memory mapping class, "EDI-MEMORY-MAPPING". */
-#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
-const edi_string_t memory_mapping_class = "EDI-MEMORY-MAPPING";
-#else
-extern const edi_string_t memory_mapping_class;
-#endif
-
-/*! \brief Flag representing Strong Uncacheable caching method. */
-#define CACHING_STRONG_UNCACHEABLE 0
-/*! \brief Flag representing Uncacheable caching method. */
-#define CACHING_UNCACHEABLE 1
-/*! \brief Flag representing Write combining caching method. */
-#define CACHING_WRITE_COMBINING 2
-/*! \brief Flag representing Write Through caching method. */
-#define CACHING_WRITE_THROUGH 3
-/*! \brief Flag representing Write Back caching method. */
-#define CACHING_WRITE_BACK 3
-/*! \brief Flag representing Write Protected caching method. */
-#define CACHING_WRITE_PROTECTED 3
-
-#ifndef IMPLEMENTING_EDI
-/*! \brief Initialize an EDI-MEMORY-MAPPING object with a physical address range.
- *
- * This method takes the start_physical_address of a memory mapping and the number of pages in that mapping and uses these arguments
- * to initialize an EDI-MEMORY-MAPPING object.  It can only be called once per object.  It returns 1 when successful, -1 when an
- * invalid physical address is given (one that the runtime knows is neither a physical memory mapping belonging to a device nor
- * normal RAM), -2 when the number of pages requested is bad (for the same reasons as the starting address can be bad), and 0 for
- * all other errors. 
- *
- * Note that this method can't be invoked on an object which has already initialized via init_memory_mapping_with_pages(). */
-EDI_DEFVAR int32_t (*init_memory_mapping_with_address)(object_pointer mapping, data_pointer start_physical_address, uint32_t pages);
-/*! \brief Initialize an EDI-MEMORY-MAPPING object by requesting a number of new physical pages.
- *
- * This method takes a desired number of physical pages for a memory mapping, and uses that number to initialize an
- * EDI-MEMORY-MAPPING object by creating a buffer of contiguous physical pages.  It can only be called once per object.  It returns
- * 1 when successful, -1 when the request for pages cannot be fulfilled, and 0 for all other errors.
- *
- * Note that this method cannot be called if init_memory_mapping_with_address() has already been used on the given object. */
-EDI_DEFVAR int32_t (*init_memory_mapping_with_pages)(object_pointer mapping, uint32_t pages);
-/*! \brief Map the memory-mapping into this driver's visible address space.
- *
- * This asks the runtime to map a given memory mapping into the driver's virtual address space.  Its parameter is the address of a
- * data_pointer to place the virtual address of the mapping into.  This method returns 1 on success, -1 on an invalid argument, -2
- * for an uninitialized object, and 0 for all other errors. */
-EDI_DEFVAR int32_t (*map_in_mapping)(object_pointer mapping, data_pointer *address_mapped_to);
-/*! \brief Unmap the memory mapping from this driver's visible address space.
- *
- * This method tries to map the given memory mapping out of the driver's virtual address space.  It returns 1 for success, -1
- * for an uninitialized memory mapping object, -2 if the mapping isn't mapped into the driver's address space already, and 0
- * for all other errors. */
-EDI_DEFVAR int32_t (*map_out_mapping)(object_pointer mapping);
-
-/*! \brief Set the caching flags for a memory mapping. */
-EDI_DEFVAR void (*mapping_set_caching_method)(object_pointer mapping, uint32_t caching_method);
-/*! \brief Get the current caching method for a memory mapping. */
-EDI_DEFVAR uint32_t (*mapping_get_caching_method)(object_pointer mapping);
-/*! \brief Flush write-combining buffers on CPU to make sure changes to memory mapping actually get written.  Only applies to a Write Combining caching method (I think.).*/
-EDI_DEFVAR void (*flush_write_combining_mapping)(object_pointer mapping);
-#endif
-
-#endif
diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_objects.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_objects.h
deleted file mode 100644
index 0e479517b74903e9902de8eeb9ecbd7d44198bc0..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi/edi_objects.h
+++ /dev/null
@@ -1,257 +0,0 @@
-#ifndef EDI_OBJECTS_H
-
-/* Copyright (c)  2006  Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts.  A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_OBJECTS_H
-
-/*! \file edi_objects.h
- * \brief The header file for basic EDI types and the object system.
- *
- * This file contains declarations of EDI's primitive data types, as well as structures and functions for with the object system.
- * It represents these data structures and algorithms:
- * 
- * 	DATA STRUCTURE: THE CLASS LIST - EDI implementing runtime's must keep an internal list of classes implemented by the runtime
- * 	and separate lists of classes implemented by each driver.  Whoever implements a class is said to "own" that class.  The
- * 	internal format of this list is up to the runtime coders, but it must be possible to recreate the original list of
- * 	edi_class_declaration structures the driver declared to the runtime from it.  This list is declared to the runtime in an
- * 	initialization function in the header edi.h.  The object_class member of an edi_object_metadata structure must point to that
- * 	object's class's entry in this list.
- * 	
- * 	ALGORITHM AND DATA STRUCTURE: CLASSES AND INHERITANCE - Classes are described using edi_class_declaration_t structures and
- * 	follow very simple rules.  All data is private and EDI provides no way to access instance data, so there are no member
- * 	variable declarations.  However, if the data isn't memory-protected (for example, driver data on the driver heap) EDI allows
- * 	the possibility of pointer access to data, since runtime and driver coders could make use of that behavior.  Classes may have
- * 	one ancestor by declaring so in their class declaration structure, and if child methods are different then parent methods
- * 	the children always override their parents.  An EDI runtime must also be able to check the existence and ownership of a given
- * 	class given its name in an edi_string_t.
- * 	
- * 	ALGORITHM: OBJECT CREATION AND DESTRUCTION - An EDI runtime should be able to call the constructor of a named class, put the
- * 	resulting object_pointer into an edi_object_metadata_t and return that structure.  The runtime should also be able to call an
- * 	object's class's destructor when given a pointer to a valid edi_metadata_t for an already-existing object.  Data equivalent
- * 	to an edi_object_metadata_t should also be tracked by the runtime for every object in existence in case of sudden EDI shutdown
- * 	(see edi.h).
- *
- * 	ALGORITHM: RUNTIME TYPE INFORMATION - When passed the data_pointer member of an edi_object_metadata_t to a valid object, an
- * 	EDI runtime must be able to return an edi_string_t containing the name of that object's class and to return function_pointers
- * 	to methods when the required information to find the correct method is given by calling a class's method getting function.*/
-
-/* If the EDI headers are linked with the standard C library, they use its type definitions.  Otherwise, equivalent definitions are
- * made.*/
-#if __STDC_VERSION__ == 199901L
-# include <stdbool.h>
-# include <stdint.h>
-#else
-# ifndef NULL
-#  define	NULL	((void*)0)
-# endif
-typedef unsigned char bool;
-# define true 1
-# define false 0
-typedef char int8_t;
-typedef short int16_t;
-typedef long int32_t;
-typedef long long int64_t;
-typedef unsigned char	uint8_t;
-typedef unsigned short	uint16_t;
-typedef unsigned long	uint32_t;
-typedef unsigned long long	uint64_t;
-#endif
-
-/*! \brief Define a variable in the header
- */
-#ifdef EDI_MAIN_FILE
-# define EDI_DEFVAR
-#else
-# define EDI_DEFVAR	extern
-#endif
-
-/*! \brief A pointer to the in-memory instance of an object.
- *
- * This type is sized just like a general C pointer type (whatever*) for the target architecture.  It's passed as a first parameter
- * to all methods, thus allowing EDI classes to be implemented as C++ classes and providing some protection from confusing objects
- * with normal pointers.  Equivalent to a C++ this pointer or an Object Pascal Self argument. */
-typedef void *object_pointer;
-/*! \brief A basic pointer type pointing to arbitrary data in an arbitrary location. */
-typedef void *data_pointer;
-/*! \brief A basic function pointer type.
- *
- * A pointer to a piece of code which can be called and return to its caller, used to distinguish between pointers to code and
- * pointers to data.  Its size is hardware-dependent. */
-typedef void (*function_pointer)(void);
-/*! \brief The length of an EDI string without its null character. */
-#define EDI_STRING_LENGTH 31
-/*! \brief A type representing a 31-character long string with a terminating NULL character at the end.  All of EDI uses this type
- * for strings.
- *
- * A null-terminated string type which stores characters in int8s.  It allows for 31 characters in each string, with the final
- * character being the NULL terminator.  Functions which use this type must check that its final character is NULL, a string which
- * doesn't not have this property is invalid and insecure.  I (the author of EDI) know and understand that this form of a string
- * suffers from C programmer's disease, but I can't use anything else without either making string use far buggier or dragging
- * everyone onto a better language than C.  */
-typedef int8_t edi_string_t[0x20];
-/*! \brief A type representing a pointer form of #edi_string_t suitable for function returns
- */
-typedef int8_t *edi_string_ptr_t;
-
-/*! \var EDI_BASE_TYPES
- * \brief A constant array of edi_string_t's holding every available EDI primitive type. */
-/*! \var EDI_TYPE_MODIFIERS
- * \brief A constant array of edi_string_t's holding available modifiers for EDI primitive types. */
-#ifdef IMPLEMENTING_EDI
- const edi_string_t EDI_BASE_TYPES[9] = {"void","bool","int8_t","int16_t","int32_t","int64_t","function_pointer","intreg","edi_string_t"};
- const edi_string_t EDI_TYPE_MODIFIERS[2] = {"pointer","unsigned"};
-#else
- //extern const edi_string_t EDI_BASE_TYPES[9] = {"void","bool","int8_t","int16_t","int32_t","int64_t","function_pointer","intreg", "edi_string_t"};
- //extern const edi_string_t EDI_TYPE_MODIFIERS[2] = {"pointer","unsigned"};
- extern const edi_string_t EDI_BASE_TYPES[9];
- extern const edi_string_t EDI_TYPE_MODIFIERS[2];
-#endif
-
-/*! \struct edi_object_metadata_t
- * \brief A packed structure holding all data to identify an object to the EDI object system. */
-typedef struct {
-	/*! \brief Points to the instance data of the object represented by this structure.
-	 *
-	 * An object_pointer to the object this structure refers to.  The this pointer, so to speak. */
-	object_pointer object;
-	/*! \brief Points the internal record kept by the runtime describing the object's class.
-	 *
-	 * Points to wherever the runtime has stored the class data this object was built from.  The class data doesn't need to be
-	 * readable to the driver, and so this pointer can point to an arbitrary runtime-reachable location. */
-	data_pointer object_class;
-} edi_object_metadata_t;
-
-/*! \struct edi_variable_declaration_t
- * \brief The data structure used to describe a variable declaration to the EDI object system.
- *
- * The data structure used to describe a variable declaration to the EDI object system.  The context of the declaration depends on
- * where the data structure appears, ie: alone, in a class declaration, in a parameter list, etc. */
-typedef struct {
-	/*! \brief The type of the declared variable. 
-	 *
-	 * The type of the variable, which must be a valid EDI primitive type as specified in the constant EDI_BASE_TYPES and
-	 * possibly modified by a modifier specified in the constant EDI_TYPE_MODIFIERS. */
-	edi_string_t type;
-	/*! \brief The name of the declared variable. */
-	edi_string_t name;
-	/*! \brief Number of array entries if this variable is an array declaration. 
-	 *
-	 * An int32_t specifying the number of variables of 'type' in the array 'name'.  For a single variable this value should
-	 * simply be set to 1, for values greater than 1 a packed array of contiguous variables is being declared, and a value of 0
-	 * is invalid. */
-	int32_t array_length;
-} edi_variable_declaration_t;
-
-/*! \struct edi_function_declaration_t
- * \brief The data structure used to declare a function to the EDI object system. */
-typedef struct {
-	/*! \brief The return type of the function.  The same type rules which govern variable definitions apply here. */
-	edi_string_t return_type;
-	/*! \brief The name of the declared function. */
-	edi_string_t name;
-	/*! \brief The version number of the function, used to tell different implementations of the same function apart. */
-	uint32_t version;
-	/*! \brief The number of arguments passed to the function.
-	 *
-	 * The number of entries in the member arguments that the object system should care about.  Caring about less misses
-	 * parameters to functions, caring about more results in buffer overflows. */
-	uint32_t num_arguments;
-	/*! \brief An array of the declared function's arguments.
-	 *
-	 * A pointer to an array num_arguments long containing edi_variable_declaration_t's for each argument to the declared
-	 * function.*/
-	edi_variable_declaration_t *arguments;
-	/*!\brief A pointer to the declared function's code in memory. */
-	function_pointer code;
-} edi_function_declaration_t;
-
-/*! \brief A pointer to a function for constructing instances of a class.
- *
- * A pointer to a function which takes no parameters and returns an object_pointer pointing to the newly made instance of a class.
- * It is the constructor's responsibility to allocate memory for the new object.  Each EDI class needs one of these. */
-typedef object_pointer (*edi_constructor_t)(void);
-/*! \brief A pointer to a function for destroying instances of a class.
- *
- * A pointer to a function which takes an object_pointer as a parameter and returns void.  This is the destructor counterpart to a
- * class's edi_constructor_t, it destroys the object pointed to by its parameter and frees the object's memory.  Every class must
- * have one */
-typedef void (*edi_destructor_t)(object_pointer);
-
-/*! \brief Information the driver must give the runtime about its classes so EDI can construct them and call their methods.
- *
- * A structure used to declare a class to an EDI runtime so instances of it can be constructed by the EDI object system. */
-typedef struct {
-	/*! \brief The name of the class declared by the structure. */
-	edi_string_t name;
-	/*! \brief The version of the class.  This number is used to tell identically named but differently
-	 * implemented classes apart.*/
-	uint32_t version;
-	/*! \brief The number of methods in the 'methods' function declaration array. */
-	uint32_t num_methods;
-	/*! \brief An array of edi_function_declaration_t declaring the methods of this class. */
-	edi_function_declaration_t *methods;
-	/*! \brief Allocates the memory for a new object of the declared class and constructs the object.  Absolutely required.*/
-	edi_constructor_t constructor;
-	/*! \brief Destroys the given object of the declared class and frees its memory. Absolutely required. */
-	edi_destructor_t destructor;
-	/*! \brief A pointer to another EDI class declaration structure specifying the declared class's parent class. 
-	 *
-	 * Points to a parent class declared in another class declaration.  It can be NULL to mean this class has no parent. */
-	struct edi_class_declaration_t *parent;
-} edi_class_declaration_t;
-
-/*! \brief Checks the existence of the named class.
- *
- * This checks for the existence on THE CLASS LIST of the class named by its edi_string_t parameter and returns a signed int32_t.  If
- * the class isn't found (ie: it doesn't exist as far as EDI is concerned) -1 is returned, if the class is owned by the driver
- * (implemented by the driver and declared to the runtime by the driver) 0, and if the class is owned by the runtime (implemented by
- * the runtime) 1. */
-int32_t check_class_existence(edi_string_t class_name);
-/*! \brief Constructs an object of the named class and returns its object_pointer and a data_pointer to its class data.
- *
- * Given a valid class name in an edi_string_t this function constructs the specified class and returns an edi_metadata_t describing
- * the new object as detailed in OBJECT CREATION AND DESTRUCTION.  If the construction fails it returns a structure full of NULL
- * pointers. */
-edi_object_metadata_t construct_object(edi_string_t class_name);
-/*! \brief Destroys the given object using its class data.
- *
- * As specified in OBJECT CREATION AND DESTRUCTION this function should destroy an object when given its valid edi_metadata_t.  The
- * destruction is accomplished by calling the class's destructor. */
-void destroy_object(edi_object_metadata_t object);
-/*! \brief Obtains a function pointer to a named method of a given class. 
- *
- * When given a valid data_pointer object_class from an edi_object_metadata_t and an edi_string_t representing the name of the
- * desired method retrieves a function_pointer to the method's machine code in memory.  If the desired method isn't found, NULL is
- * returned. */
-function_pointer get_method_by_name(data_pointer object_class,edi_string_t method_name);
-/*! \brief Obtains a function pointer to a method given by a declaration of the given class if the class's method matches the
- * declaration. 
- *
- * Works just like get_method_by_name(), but by giving an edi_function_declaration_t for the desired method instead of just its name.
- * Performs detailed checking against THE CLASS LIST to make sure that the method returned exactly matches the declaration passed
- * in. */
-function_pointer get_method_by_declaration(data_pointer object_class,edi_function_declaration_t declaration);
-
-/* Runtime typing information. */
-/*! \brief Returns the name of the class specified by a pointer to class data. 
- *
- * Given the data_pointer to an object's class data as stored in an edi_object_metadata_t retrieves the name of the object's class
- * and returns it in an edi_string_t. */
-edi_string_ptr_t get_object_class(data_pointer object_class);
-/*! \brief Returns the name of a class's parent class.
- *
- * When given an edi_string_t with a class name in it, returns another edi_string_t containing the name of the class's parent, or an
- * empty string. */
-edi_string_ptr_t get_class_parent(edi_string_t some_class);
-/*! \brief Returns the internal class data of a named class (if it exists) or NULL.
- *
- * When given an edi_string_t with a valid class name in it, returns a pointer to the runtime's internal class data for that class.
- * Otherwise, it returns NULL. */
-data_pointer get_internal_class(edi_string_t some_class);
-
-#endif 
diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_port_io.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_port_io.h
deleted file mode 100644
index a2a1773d6252197f2b165441802ccc928614a769..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi/edi_port_io.h
+++ /dev/null
@@ -1,90 +0,0 @@
-#ifndef EDI_PORT_IO_H
-
-/* Copyright (c)  2006  Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts.  A copy of the license is included in the file entitled "COPYING". */
-
-/* Modified by thePowersGang (John Hodge)
- * - Surround variable definitions with an #ifdef IMPLEMENTING_EDI
- */
-
-#define EDI_PORT_IO_H
-
-/*! \file edi_port_io.h
- * \brief Declaration and description of EDI's port I/O class.
- *
- * Data structures and algorithms this header represents:
- *
- *	DATA STRUCTURE AND ALGORITHM: PORT I/O OBJECTS - A class named "EDI-IO-PORT" is defined as an encapsulation of the port I/O
- * used on some machine architectures.  Each object of this class represents a single I/O port which can be read from and written to
- * in various sizes.  Each port can be held by one object only at a time. */
-
-#include "edi_objects.h"
-
-/*! \brief Macro to create methods for reading from ports.
- *
- * This macro creates four similar methods, differing in the size of the type they read from the I/O port held by the object.  Their
- * parameter is a pointer to the output type, which is filled with the value read from the I/O port.  They return 1 for success, -1
- * for an uninitialized I/O port object, and 0 for other errors. */
-#define port_read_method(type,name) int32_t (*name)(object_pointer port_object, type *out)
-/*! \brief Macro to create methods for writing to ports.
- *
- * This macro creates four more similar methods, differing in the size of the type they write to the I/O port held by the object.
- * Their parameter is the value to write to the port.  They return 1 for success, -1 for an uninitialized I/O port object and 0 for
- * other errors. */
-#define port_write_method(type,name) int32_t (*name)(object_pointer port_object, type in)
-
-/*! \brief Name of EDI I/O port class. (Constant)
- *
- * A CPP constant with the value of #io_port_class */
-#define	IO_PORT_CLASS	"EDI-IO-PORT"
-/*! \brief Name of EDI I/O port class.
- *
- * An edi_string_t containing the class name "EDI-IO-PORT". */
-#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI)
-const edi_string_t io_port_class = IO_PORT_CLASS;
-#else
-extern const edi_string_t io_port_class;
-#endif
-
-#ifndef IMPLEMENTING_EDI
-/*! \brief int32_t EDI-IO-PORT.init_io_port(unsigned int16_t port);
- *
- * This method takes an unsigned int16_t representing a particular I/O port and initializes the invoked EDI-IO-PORT object with it.
- * The method returns 1 if successful, -1 if the I/O port could not be obtained for the object, and 0 for all other errors. */
-EDI_DEFVAR int32_t (*init_io_port)(object_pointer port_object, uint16_t port);
-/*! \brief Get the port number from a port object. */
-EDI_DEFVAR uint16_t (*get_port_number)(object_pointer port);
-/*! \brief Method created by port_read_method() in order to read bytes (int8s) from I/O ports. */
-EDI_DEFVAR int32_t (*read_byte_io_port)(object_pointer port_object, int8_t *out);
-/*! \brief Method created by port_read_method() in order to read words (int16s) from I/O ports. */
-EDI_DEFVAR int32_t (*read_word_io_port)(object_pointer port_object, int16_t *out);
-/*! \brief Method created by port_read_method() in order to read longwords (int32s) from I/O ports. */
-EDI_DEFVAR int32_t (*read_long_io_port)(object_pointer port_object, int32_t *out);
-/*! \brief Method created by port_read_method() in order to read long longwords (int64s) from I/O ports. */
-EDI_DEFVAR int32_t (*read_longlong_io_port)(object_pointer port_object,int64_t *out);
-/*! \brief Method of EDI-IO-PORT to read long strings of data from I/O ports.
- *
- * Reads arbitrarily long strings of data from the given I/O port.  Returns 1 for success, -1 for an uninitialized port object, -2
- * for a bad pointer to the destination buffer, and 0 for all other errors. */
-EDI_DEFVAR int32_t (*read_string_io_port)(object_pointer port_object, uint32_t data_length, uint8_t *out);
-/*! \brief Method created by port_write_method() in order to write bytes (int8s) to I/O ports. */
-EDI_DEFVAR int32_t (*write_byte_io_port)(object_pointer port_object, int8_t in);
-/*! \brief Method created by port_write_method() in order to write words (int16s) to I/O ports. */
-EDI_DEFVAR int32_t (*write_word_io_port)(object_pointer port_object, int16_t in);
-/*! \brief Method created by port_write_method() in order to write longwords (int32s) to I/O ports. */
-EDI_DEFVAR int32_t (*write_long_io_port)(object_pointer port_object, int32_t in);
-/*! \brief Method created by port_write_method() in order to write long longwords (int64s) to I/O ports. */
-EDI_DEFVAR int32_t (*write_longlong_io_port)(object_pointer port_object, int64_t in);
-/*! \brief Method of EDI-IO-PORT to write long strings of data to I/O ports.
- *
- * Writes arbitrarily long strings of data to the given I/O port.  Returns 1 for success, -1 for an uninitialized port object, -2
- * for a bad pointer to the source buffer, and 0 for all other errors. */
-EDI_DEFVAR int32_t (*write_string_io_port)(object_pointer port_object, uint32_t data_length, uint8_t *in);
-
-#endif // defined(IMPLEMENTING_EDI)
-
-#endif
diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_pthreads.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_pthreads.h
deleted file mode 100644
index c21fa75195a7820167c79f0fd761416f92173d28..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi/edi_pthreads.h
+++ /dev/null
@@ -1,175 +0,0 @@
-#ifndef EDI_PTHREADS
-
-/* Copyright (c)  2006  Eli Gottlieb.
- * Permission is granted to copy, distribute and/or modify this document
- * under the terms of the GNU Free Documentation License, Version 1.2
- * or any later version published by the Free Software Foundation;
- * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
- * Texts.  A copy of the license is included in the file entitled "COPYING". */
-
-#define EDI_PTHREADS
-/*!\file edi_pthreads.h
- * \brief A basic subset of POSIX Threads functionality, providing threading and thread synchronization.
- *
- * A very basic POSIX Threads interface.  Note that pthreads are not a class, because none of these calls really gels with
- * object-oriented programming.  Also, if drivers aren't processes or threads under the implementing operating system a small
- * threading system must be implemented in-runtime just to multiplex the pthreads of EDI drivers.  Sorry about that.
- *
- * Data structures and algorithms this header represents:
- *
- * 	ALGORITHM AND DATA STRUCTURE: POSIX Threading - The runtime must provide enough of a POSIX threading interface to implement
- * the calls described here.  The actual multithreading must be performed by the runtime, and the runtime can implement that
- * multithreading however it likes as long as the given POSIX Threads subset works.  There is, however, a caveat: since the runtime
- * calls the driver like it would a library, the driver must perceive all calls made to it by the runtime as running under one thread.
- * From this thread the driver can create others.  Such behavior is a quirk of EDI, and does not come from the POSIX standard.
- * However, it is necessary to provide the driver with a thread for its own main codepaths.  For further details on a given POSIX
- * Threading routine, consult its Unix manual page. */
-
-#include "edi_objects.h"
-
-/* Placeholder type definitions.  Users of the PThreads interface only ever need to define pointers to these types. */
-/*!\brief Opaque POSIX Threading thread attribute type. */
-typedef void pthread_attr_t;
-/*!\brief Opaque POSIX Threading mutex (mutual exclusion semaphore) type. */
-typedef void pthread_mutex_t;
-/*!\brief Opaque POSIX Threading mutex attribute type. */
-typedef void pthread_mutex_attr_t;
-
-/*!\struct sched_param
- * \brief POSIX Threading scheduler parameters for a thread. */
-typedef struct {
-	/*!\brief The priority of the thread. */
-	int32_t sched_priority;
-} sched_param;
-
-/*!\brief POSIX Threading thread identifier. */
-typedef uint32_t pthread_t;
-/*!\brief POSIX Threading thread function type.
- *
- * A function pointer to a thread function, with the required signature of a thread function.  A thread function takes one untyped
- * pointer as an argument and returns an untyped pointer.  Such a function is a thread's main routine: it's started with the thread,
- * and the thread exits if it returns. */
-typedef void *(*pthread_function_t)(void*);
-
-/*!\brief Insufficient resources. */
-#define EAGAIN -1
-/*!\brief Invalid parameter. */
-#define EINVAL -2
-/*!\brief Permission denied. */
-#define EPERM -3
-/*!\brief Operation not supported. */
-#define ENOTSUP -4
-/*!\brief Priority scheduling for POSIX/multiple schedulers is not implemented. */
-#define ENOSYS -5
-/*!\brief Out of memory. */
-#define ENOMEM -6
-/*!\brief Deadlock.  Crap. */
-#define EDEADLK -7
-/*!\brief Busy.  Mutex already locked. */
-#define EBUSY -8
-
-/*!\brief Scheduling policy for regular, non-realtime scheduling.  The default. */
-#define SCHED_OTHER 0
-/*!\brief Real-time, first-in first-out scheduling policy.  Requires special (superuser, where such a thing exists) permissions. */
-#define SCHED_FIFO 1
-/*!\brief Real-time, round-robin scheduling policy.  Requires special (superuser, where such a thing exists) permissions. */
-#define SCHED_RR 0
-
-/*!\brief Creates a new thread with the given attributes, thread function and arguments, giving back the thread ID of the new
- * thread.
- *
- * pthread_create() creates a new thread of control that executes concurrently with the calling thread.  The new thread applies the
- * function start_routine, passing it arg as its argument.  The attr argument specifies thread attributes to apply to the new thread;
- * it can also be NULL for the default thread attributes (joinable with default scheduling policy).  On success this function returns
- * 0 and places the identifier of the new thread into thread_id.  On an error, pthread_create() can return EAGAIN if insufficient
- * runtime resources are available to create the requested thread, EINVAL a value specified by attributes is invalid, or EPERM if the
- * caller doesn't have permissions to set the given attributes.
- *
- * For further information: man 3 pthread_create */
-int32_t pthread_create(pthread_t *thread_id, const pthread_attr_t *attributes, pthread_function_t thread_function, void *arguments);
-/*!\brief Terminates the execution of the calling thread.  The thread's exit code with by status, and this routine never returns. */
-void pthread_exit(void *status);
-/*!\brief Returns the thread identifier of the calling thread. */
-pthread_t pthread_self();
-/*!\brief Compares two thread identifiers.
- *
- * Determines of the given two thread identifiers refer to the same thread.  If so, returns non-zero.  Otherwise, 0 is returned. */
-int32_t pthread_equal(pthread_t thread1, pthread_t thread2);
-/*!\brief Used by the calling thread to relinquish use of the processor.  The thread then waits in the run queue to be scheduled
- * again. */
-void pthread_yield();
-
-/*!\brief Gets the scheduling policy of the given attributes.
- *
- * Places the scheduling policy for attributes into policy.  Returns 0 on success, EINVAL if attributes was invalid, and ENOSYS if
- * priority scheduling/multiple scheduler support is not implemented. */
-int32_t pthread_attr_getschedpolicy(const pthread_attr_t *attributes, int32_t *policy);
-/*!\brief Sets the scheduling policy of the given attributes.
- *
- * Requests a switch of scheduling policy to policy for the given attributes.  Can return 0 for success, EINVAL if the given policy
- * is not one of SCHED_OTHER, SCHED_FIFO or SCHED_RR or ENOTSUP if policy is either SCHED_FIFO or SCHED_RR and the driver is not
- * running with correct privileges. */
-int32_t pthread_attr_setschedpolicy(pthread_attr_t *attributes, int32_t policy);
-
-/*!\brief Gets the scheduling paramaters (priority) from the given attributes.
- *
- * On success, stores scheduling parameters in param from attributes, and returns 0.  Otherwise, returns non-zero error code, such
- * as EINVAL if the attributes object is invalid. */
-int32_t pthread_attr_getschedparam(const pthread_attr_t *attributes, sched_param *param);
-/*!\brief Sets the scheduling parameters (priority) of the given attributes.
- *
- * Requests that the runtime set the scheduling parameters (priority) of attributes from param. Returns 0 for success, EINVAL for an
- * invalid attributes object, ENOSYS when multiple schedulers/priority scheduling is not implemented, and ENOTSUP when the value of
- * param isn't supported/allowed. */
-int32_t pthread_attr_setschedparam(pthread_attr_t *attributes, const sched_param *param);
-
-/*!\brief The thread obtains its scheduling properties explicitly from its attributes structure. */
-#define PTHREAD_EXPLICIT_SCHED 1
-/*!\brief The thread inherits its scheduling properties from its parent thread. */
-#define PTHREAD_INHERIT_SCHED 0
-
-/*!\brief Returns the inheritsched attribute of the given attributes.
- *
- * On success, returns 0 and places the inheritsched attribute from attributes into inherit.  This attribute specifies where the
- * thread's scheduling properites shall come from, and can be set to PTHREAD_EXPLICIT_SCHED or PTHREAD_INHERIT_SCHED.  On failure it
- * returns EINVAL if attributes was invalid or ENOSYS if multiple schedulers/priority scheduling isn't implemented. */
-int32_t pthread_attr_getinheritsched(const pthread_attr_t *attributes, int32_t *inherit);
-/*!\brief Sets the inheritsched attribute of the given attributes.
- *
- * On success, places inherit into the inheritsched attribute of attributes and returns 0.  inherit must either contain
- * PTHREAD_EXPLICIT_SCHED or PTHREAD_INHERIT_SCHED.  On failure, this routine returns EINVAL if attributes is invalid, ENOSYS when
- * multiple schedulers/priority scheduling isn't implemented, and ENOSUP if the inheritsched attribute isn't supported. */
-int32_t pthread_attr_setinheritsched(pthread_attr_t *attributes, int32_t inherit);
-
-/*!\brief Creates a new POSIX Threads mutex, which will initially be unlocked.
- *
- * Creates a new mutex with the given attributes.  If attributes is NULL, the default attributes will be used.  The mutex starts out
- * unlocked.  On success, the new mutex resides in the mutex structure pointed to by mutex, and this routine routines 0.  On failure,
- * it returns EAGAIN if the system lacked sufficient non-memory resources to initialize the mutex, EBUSY if the given mutex is
- * already initialized and in use, EINVAL if either parameter is invalid, and ENOMEM if the system lacks the memory for a new
- * mutex.  Note: All EDI mutexes are created with the default attributes, and are of type PTHREAD_MUTEX_ERRORCHECK.  This means
- * undefined behavior can never result from an badly placed function call. */
-int32_t pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutex_attr_t *attributes);
-/*!\brief Locks the given mutex.
- *
- * Locks the given mutex.  If the mutex is already locked, blocks the calling thread until it can acquire the lock.  When this
- * routine returns successfully, it will return 0 and the calling thread will own the lock of the mutex.  If the call fails, it can
- * return EINVAL when mutex is invalid or EDEADLK if the calling thread already owns the mutex. */
-int32_t pthread_mutex_lock(pthread_mutex_t *mutex);
-/*!\brief Unlocks the given mutex.
- *
- * Unlocks the given mutex, returning 0 on success.  On failure, it can return EINVAL when mutex is invalid or EPERM when the
- * calling thread doesn't own the mutex. */
-int32_t pthread_mutex_unlock(pthread_mutex_t *mutex);
-/*!\brief Tries to lock the given mutex, returning immediately even if the mutex is already locked.
- *
- * Attempts to lock the given mutex, but returns immediately if it can't acquire a lock.  Returns 0 when it has acquired a lock,
- * EBUSY if the mutex is already locked, or EINVAL if mutex is invalid. */
-int32_t pthread_mutex_trylock(pthread_mutex_t *mutex);
-/*!\brief Destroys the given mutex, or at least the internal structure of it. 
- *
- * Deletes the given mutex, making mutex invalid until it should be initialized by pthread_mutex_init().  Returns 0 on success,
- * EINVAL when mutex is invalid, or EBUSY when mutex is locked or referenced by another thread. */
-int32_t pthread_mutex_destroy (pthread_mutex_t *mutex);
-
-#endif
diff --git a/KernelLand/Modules/Interfaces/EDI/edi/helpers.h b/KernelLand/Modules/Interfaces/EDI/edi/helpers.h
deleted file mode 100644
index d7bc138898644c49cc4d9ea51ec0922cd99fbea4..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi/helpers.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef HELPERS_H
-
-#define HELPERS_H
-
-#include <edi.h>
-
-// Locally Defined
-bool edi_string_equal(edi_string_t x,edi_string_t y);
-bool descends_from(data_pointer object_class,edi_string_t desired_class);
-data_pointer get_actual_class(edi_string_t ancestor,int32_t num_objects,edi_object_metadata_t *objects);
-
-// Local Copy/set
-void *memcpyd(void *dest, void *src, unsigned int count);
-
-// Implementation Defined Common functions
-void *memcpy(void *dest, void *src, unsigned int count);
-void *memmove(void *dest, void *src, unsigned int count);
-void *realloc(void *ptr, unsigned int size);
-
-#endif
diff --git a/KernelLand/Modules/Interfaces/EDI/edi_int.inc.c b/KernelLand/Modules/Interfaces/EDI/edi_int.inc.c
deleted file mode 100644
index 0ab3359e9de40573b072f5cee18f0eeefd5b123b..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi_int.inc.c
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * AcessOS EDI Interface
- * - IRQ Class
- * 
- * By John Hodge (thePowersGang)
- * 
- * This file has been released into the public domain.
- * You are free to use it as you wish.
- */
-#include "edi/edi.h"
-
-// === TYPES ===
-typedef struct {
-	uint16_t	State;	// 0: Unallocated, 1: Allocated, 2: Initialised, (Bit 0x8000 set if in heap)
-	uint16_t	Num;
-	interrupt_handler_t	Handler;
-} tEdiIRQ;
-
-// === PROTOTYPES ===
-void EDI_Int_IRQ_Handler(tRegs *Regs);
-
-// === GLOBALS ===
-tEdiIRQ	gEdi_IRQObjects[16];
-
-// === FUNCTIONS ===
-/**
- * \fn object_pointer Edi_Int_IRQ_Construct(void)
- * \brief Creates a new IRQ Object
- * \return	Pointer to object
- */
-object_pointer Edi_Int_IRQ_Construct(void)
-{
-	 int	i;
-	// Search for a free irq
-	for( i = 0; i < 16; i ++ )
-	{
-		if(gEdi_IRQObjects[i].State)	continue;
-		gEdi_IRQObjects[i].State = 1;
-		gEdi_IRQObjects[i].Num = 0;
-		gEdi_IRQObjects[i].Handler = NULL;
-		return &gEdi_IRQObjects[i];
-	}
-	return NULL;
-}
-
-/**
- * \fn void Edi_Int_IRQ_Destruct(object_pointer Object)
- * \brief Destruct an IRQ Object
- * \param Object	Object to destroy
- */
-void Edi_Int_IRQ_Destruct(object_pointer Object)
-{
-	tEdiIRQ	*obj;
-	
-	VALIDATE_PTR(Object,);
-	obj = GET_DATA(Object);
-	
-	if( !obj->State )	return;
-	
-	if( obj->Handler )
-		irq_uninstall_handler( obj->Num );
-	
-	if( obj->State & 0x8000 ) {	// If in heap, free
-		free(Object);
-	} else {	// Otherwise, mark as unallocated
-		obj->State = 0;
-	}
-}
-
-/**
- * \fn int32_t	Edi_Int_IRQ_InitInt(object_pointer Object, uint16_t Num, interrupt_handler_t Handler)
- * \brief Initialises an IRQ
- * \param Object	Object Pointer (this)
- * \param Num	IRQ Number to use
- * \param Handler	Callback for IRQ
- */
-int32_t	Edi_Int_IRQ_InitInt(object_pointer Object, uint16_t Num, interrupt_handler_t Handler)
-{
-	tEdiIRQ	*obj;
-	
-	//LogF("Edi_Int_IRQ_InitInt: (Object=0x%x, Num=%i, Handler=0x%x)\n", Object, Num, Handler);
-	
-	VALIDATE_PTR(Object,0);
-	obj = GET_DATA(Object);
-	
-	if( !obj->State )	return 0;
-	
-	if(Num > 15)	return 0;
-	
-	// Install the IRQ if a handler is passed
-	if(Handler) {
-		if( !irq_install_handler(Num, Edi_Int_IRQ_Handler) )
-			return 0;
-		obj->Handler = Handler;
-	}
-	
-	obj->Num = Num;
-	obj->State &= ~0x3FFF;
-	obj->State |= 2;	// Set initialised flag
-	return 1;
-}
-
-/**
- * \fn uint16_t Edi_Int_IRQ_GetInt(object_pointer Object)
- * \brief Returns the irq number associated with the object
- * \param Object	IRQ Object to get number from
- * \return IRQ Number
- */
-uint16_t Edi_Int_IRQ_GetInt(object_pointer Object)
-{
-	tEdiIRQ	*obj;
-	
-	VALIDATE_PTR(Object,0);
-	obj = GET_DATA(Object);
-	
-	if( !obj->State )	return 0;
-	return obj->Num;
-}
-
-/**
- * \fn void EDI_Int_IRQ_SetHandler(object_pointer Object, interrupt_handler_t Handler)
- * \brief Set the IRQ handler for an IRQ object
- * \param Object	IRQ Object to alter
- * \param Handler	Function to use as handler
- */
-void EDI_Int_IRQ_SetHandler(object_pointer Object, interrupt_handler_t Handler)
-{
-	tEdiIRQ	*obj;
-	
-	// Get Data Pointer
-	VALIDATE_PTR(Object,);
-	obj = GET_DATA(Object);
-	
-	// Sanity Check arguments
-	if( !obj->State )	return ;
-	
-	// Only register the mediator if it is not already
-	if( Handler && !obj->Handler )
-		if( !irq_install_handler(obj->Num, Edi_Int_IRQ_Handler) )
-			return ;
-	obj->Handler = Handler;
-}
-
-/**
- * \fn void EDI_Int_IRQ_Return(object_pointer Object)
- * \brief Return from interrupt
- * \param Object	IRQ Object
- * \note Due to the structure of acess interrupts, this is a dummy
- */
-void EDI_Int_IRQ_Return(object_pointer Object)
-{
-}
-
-/**
- * \fn void Edi_Int_IRQ_Handler(struct regs *Regs)
- * \brief EDI IRQ Handler - Calls the handler 
- * \param Regs	Register state at IRQ call
- */
-void Edi_Int_IRQ_Handler(struct regs *Regs)
-{
-	 int	i;
-	for( i = 0; i < 16; i ++ )
-	{
-		if(!gEdi_IRQObjects[i].State)	continue;	// Unused, Skip
-		if(gEdi_IRQObjects[i].Num != Regs->int_no)	continue;	// Another IRQ, Skip
-		if(!gEdi_IRQObjects[i].Handler)	continue;	// No Handler, Skip
-		gEdi_IRQObjects[i].Handler( Regs->int_no );	// Call Handler
-		return;
-	}
-}
-
-
-// === CLASS DECLARATION ===
-static edi_function_declaration_t	scEdi_Int_Functions_IRQ[] = {
-		{"int32_t", "init_interrupt", 1, 3, NULL, //scEdi_Int_Variables_IO[0],
-			(function_pointer)Edi_Int_IRQ_InitInt
-			},
-		{"uint32_t", "interrupt_get_irq", 1, 1, NULL, //scEdi_Int_Variables_IO[1],
-			(function_pointer)Edi_Int_IRQ_GetInt
-			},
-		{"void", "interrupt_set_handler", 1, 2, NULL, //scEdi_Int_Variables_IO[2],
-			(function_pointer)Edi_Int_IRQ_GetInt
-			},
-		{"void", "interrupt_return", 1, 1, NULL, //scEdi_Int_Variables_IO[3],
-			(function_pointer)Edi_Int_IRQ_GetInt
-			}
-	};
-static edi_class_declaration_t	scEdi_Int_Class_IRQ = 
-	{
-		INTERRUPTS_CLASS, 1, 12,
-		scEdi_Int_Functions_IRQ,
-		Edi_Int_IRQ_Construct,
-		Edi_Int_IRQ_Destruct,
-		NULL
-	};
diff --git a/KernelLand/Modules/Interfaces/EDI/edi_io.inc.c b/KernelLand/Modules/Interfaces/EDI/edi_io.inc.c
deleted file mode 100644
index 8a6ef22ef60a40885249b49ddee0c4cf6e8fe027..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/edi_io.inc.c
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * AcessOS EDI Interface
- * - IO Port Class
- * 
- * By John Hodge (thePowersGang)
- * 
- * This file has been released into the public domain.
- * You are free to use it as you wish.
- */
-#include "edi/edi.h"
-
-// === TYPES ===
-typedef struct {
-	uint16_t	State;	// 0: Unallocated, 1: Allocated, 2: Initialised, (Bit 0x8000 set if in heap)
-	uint16_t	Num;
-} tEdiPort;
-
-// === GLOBALS ===
-#define	NUM_PREALLOC_PORTS	128
-tEdiPort	gEdi_PortObjects[NUM_PREALLOC_PORTS];
-
-// === FUNCTIONS ===
-/**
- * \fn object_pointer Edi_Int_IO_Construct(void)
- * \brief Creates a new IO Port Object
- * \return	Pointer to object
- */
-object_pointer Edi_Int_IO_Construct(void)
-{
-	tEdiPort	*ret;
-	 int	i;
-	// Search for a free preallocated port
-	for( i = 0; i < NUM_PREALLOC_PORTS; i ++ )
-	{
-		if(gEdi_PortObjects[i].State)	continue;
-		gEdi_PortObjects[i].State = 1;
-		gEdi_PortObjects[i].Num = 0;
-		return &gEdi_PortObjects[i];
-	}
-	// Else, use heap space
-	ret = malloc( sizeof(tEdiPort) );
-	ret->State = 0x8001;
-	ret->Num = 0;
-	return ret;
-}
-
-/**
- * \fn void Edi_Int_IO_Destruct(object_pointer Object)
- * \brief Destruct an IO Port Object
- * \param Object	Object to destroy
- */
-void Edi_Int_IO_Destruct(object_pointer Object)
-{
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object,);
-	obj = GET_DATA(Object);
-	
-	if(obj->State & 0x8000) {	// If in heap, free
-		free(Object);
-	} else {	// Otherwise, mark as unallocated
-		obj->State = 0;
-	}
-}
-
-/**
- * \fn int32_t	Edi_Int_IO_InitPort(object_pointer Object, uint16_t Port)
- * \brief Initialises an IO Port
- * \param Object	Object Pointer (this)
- * \param Port	Port Number to use
- */
-int32_t	Edi_Int_IO_InitPort(object_pointer Object, uint16_t Port)
-{
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	
-	if( !obj->State )	return 0;
-	obj->Num = Port;
-	obj->State &= ~0x3FFF;
-	obj->State |= 2;	// Set initialised flag
-	return 1;
-}
-
-/**
- * \fn uint16_t Edi_Int_IO_GetPortNum(object_pointer Object)
- * \brief Returns the port number associated with the object
- * \param Object	Port Object to get number from
- * \return Port Number
- */
-uint16_t Edi_Int_IO_GetPortNum(object_pointer Object)
-{
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	// Check if valid
-	if( !obj->State )	return 0;
-	// Return Port No
-	return obj->Num;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_ReadByte(object_pointer Object, uint8_t *out)
- * \brief Read a byte from an IO port
- * \param Object	Port Object
- * \param out	Pointer to put read data
- */
-int32_t Edi_Int_IO_ReadByte(object_pointer Object, uint8_t *out)
-{
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	
-	if( !obj->State )	return 0;
-	if( obj->State & 1 )	return -1;	// Unintialised
-	
-	__asm__ __volatile__ ( "inb %%dx, %%al" : "=a" (*out) : "d" ( obj->Num ) );
-	
-	return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_ReadWord(object_pointer Object, uint16_t *out)
- * \brief Read a word from an IO port
- * \param Object	Port Object
- * \param out	Pointer to put read data
- */
-int32_t Edi_Int_IO_ReadWord(object_pointer Object, uint16_t *out)
-{
-	
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	if( !obj->State )	return 0;
-	if( obj->State & 1 )	return -1;	// Unintialised
-	
-	__asm__ __volatile__ ( "inw %%dx, %%ax" : "=a" (*out) : "d" ( obj->Num ) );
-	
-	return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_ReadDWord(object_pointer Object, uint32_t *out)
- * \brief Read a double word from an IO port
- * \param Object	Port Object
- * \param out	Pointer to put read data
- */
-int32_t Edi_Int_IO_ReadDWord(object_pointer Object, uint32_t *out)
-{
-	
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	if( !obj->State )	return 0;
-	if( obj->State & 1 )	return -1;	// Unintialised
-	
-	__asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*out) : "d" ( obj->Num ) );
-	
-	return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_ReadQWord(object_pointer Object, uint64_t *out)
- * \brief Read a quad word from an IO port
- * \param Object	Port Object
- * \param out	Pointer to put read data
- */
-int32_t Edi_Int_IO_ReadQWord(object_pointer Object, uint64_t *out)
-{
-	uint32_t	*out32 = (uint32_t*)out;
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	if( !obj->State )	return 0;
-	if( obj->State & 1 )	return -1;	// Unintialised
-	
-	__asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*out32) : "d" ( obj->Num ) );
-	__asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*(out32+1)) : "d" ( obj->Num+4 ) );
-	
-	return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_ReadString(object_pointer Object, uint32_t Length, uint8_t *out)
- * \brief Read a byte from an IO port
- * \param Object	Port Object
- * \param Length	Number of bytes to read
- * \param out	Pointer to put read data
- */
-int32_t Edi_Int_IO_ReadString(object_pointer Object, uint32_t Length, uint8_t *out)
-{
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	if( !obj->State )	return 0;
-	if( obj->State & 1 )	return -1;	// Unintialised
-	
-	__asm__ __volatile__ ( "rep insb" : : "c" (Length), "D" (out), "d" ( obj->Num ) );
-	
-	return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_WriteByte(object_pointer Object, uint8_t in)
- * \brief Write a byte from an IO port
- * \param Object	Port Object
- * \param in	Data to write
- */
-int32_t Edi_Int_IO_WriteByte(object_pointer Object, uint8_t in)
-{
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	if( !obj->State )	return 0;
-	if( obj->State & 1 )	return -1;	// Unintialised
-	
-	__asm__ __volatile__ ( "outb %%al, %%dx" : : "a" (in), "d" ( obj->Num ) );
-	
-	return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_WriteWord(object_pointer Object, uint16_t in)
- * \brief Write a word from an IO port
- * \param Object	Port Object
- * \param in	Data to write
- */
-int32_t Edi_Int_IO_WriteWord(object_pointer Object, uint16_t in)
-{
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	if( !obj->State )	return 0;
-	if( obj->State & 1 )	return -1;	// Unintialised
-	
-	__asm__ __volatile__ ( "outw %%ax, %%dx" : : "a" (in), "d" ( obj->Num ) );
-	
-	return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_WriteDWord(object_pointer Object, uint32_t in)
- * \brief Write a double word from an IO port
- * \param Object	Port Object
- * \param in	Data to write
- */
-int32_t Edi_Int_IO_WriteDWord(object_pointer Object, uint32_t in)
-{
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	if( !obj->State )	return 0;
-	if( obj->State & 1 )	return -1;	// Unintialised
-	
-	__asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (in), "d" ( obj->Num ) );
-	
-	return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_WriteQWord(object_pointer Object, uint64_t in)
- * \brief Write a quad word from an IO port
- * \param Object	Port Object
- * \param in	Data to write
- */
-int32_t Edi_Int_IO_WriteQWord(object_pointer Object, uint64_t in)
-{
-	uint32_t	*in32 = (uint32_t*)&in;
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	if( !obj->State )	return 0;
-	if( obj->State & 1 )	return -1;	// Unintialised
-	
-	__asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (*in32), "d" ( obj->Num ) );
-	__asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (*(in32+1)), "d" ( obj->Num+4 ) );
-	
-	return 1;
-}
-
-/**
- * \fn int32_t Edi_Int_IO_WriteString(object_pointer Object, uint32_t Length, uint8_t *in)
- * \brief Read a byte from an IO port
- * \param Object	Port Object
- * \param Length	Number of bytes to write
- * \param in	Pointer to of data to write
- */
-int32_t Edi_Int_IO_WriteString(object_pointer Object, uint32_t Length, uint8_t *in)
-{
-	tEdiPort	*obj;
-	// Get Data Pointer
-	VALIDATE_PTR(Object, 0);
-	obj = GET_DATA(Object);
-	if( !obj->State )	return 0;
-	if( obj->State & 1 )	return -1;	// Unintialised
-	
-	__asm__ __volatile__ ( "rep outsb" : : "c" (Length), "D" (in), "d" ( obj->Num ) );
-	
-	return 1;
-}
-
-// === CLASS DECLARATION ===
-/*static edi_variable_declaration_t	*scEdi_Int_Variables_IO[] = {
-	{
-		{"pointer", "port_object", 0},
-		{"uint16_t", "port", 0}
-	},
-	{
-		{"pointer", "port_object", 0}
-	},
-	{
-		{"pointer", "port_object", 0},
-		{"pointer int8_t", "out", 0}
-	}
-};*/
-static edi_function_declaration_t	scEdi_Int_Functions_IO[] = {
-		{"int32_t", "init_io_port", 1, 2, NULL, //scEdi_Int_Variables_IO[0],
-			(function_pointer)Edi_Int_IO_InitPort
-			},
-		{"uint16_t", "get_port_number", 1, 1, NULL, //scEdi_Int_Variables_IO[1],
-			(function_pointer)Edi_Int_IO_GetPortNum
-			},
-		{"int32_t", "read_byte_io_port", 1, 2, NULL, //scEdi_Int_Variables_IO[2],
-			(function_pointer)Edi_Int_IO_ReadByte
-			},
-		{"int32_t", "read_word_io_port", 1, 2, NULL/*{
-				{"pointer", "port_object", 0},
-				{"pointer int16_t", "out", 0}
-			}*/,
-			(function_pointer)Edi_Int_IO_ReadWord
-			},
-		{"int32_t", "read_long_io_port", 1, 2, NULL/*{
-				{"pointer", "port_object", 0},
-				{"pointer int32_t", "out", 0}
-			}*/,
-			(function_pointer)Edi_Int_IO_ReadDWord
-			},
-		{"int32_t", "read_longlong_io_port", 1, 2, NULL/*{
-				{"pointer", "port_object", 0},
-				{"pointer int64_t", "out", 0}
-			}*/,
-			(function_pointer)Edi_Int_IO_ReadQWord
-			},
-		{"int32_t", "read_string_io_port", 1, 3, NULL/*{
-				{"pointer", "port_object", 0},
-				{"int32_T", "data_length", 0},
-				{"pointer int64_t", "out", 0}
-			}*/,
-			(function_pointer)Edi_Int_IO_ReadString
-			},
-			
-		{"int32_t", "write_byte_io_port", 1, 2, NULL/*{
-				{"pointer", "port_object", 0},
-				{"int8_t", "in", 0}
-			}*/,
-			(function_pointer)Edi_Int_IO_WriteByte},
-		{"int32_t", "write_word_io_port", 1, 2, NULL/*{
-				{"pointer", "port_object", 0},
-				{"int16_t", "in", 0}
-			}*/,
-			(function_pointer)Edi_Int_IO_WriteWord},
-		{"int32_t", "write_long_io_port", 1, 2, NULL/*{
-				{"pointer", "port_object", 0},
-				{"int32_t", "in", 0}
-			}*/,
-			(function_pointer)Edi_Int_IO_WriteDWord},
-		{"int32_t", "write_longlong_io_port", 1, 2, NULL/*{
-				{"pointer", "port_object", 0},
-				{"int64_t", "in", 0}
-			}*/,
-			(function_pointer)Edi_Int_IO_WriteQWord}
-	};
-static edi_class_declaration_t	scEdi_Int_Class_IO = 
-	{
-		IO_PORT_CLASS, 1, 12,
-		scEdi_Int_Functions_IO,
-		Edi_Int_IO_Construct,
-		Edi_Int_IO_Destruct,
-		NULL
-	};
diff --git a/KernelLand/Modules/Interfaces/EDI/main.c b/KernelLand/Modules/Interfaces/EDI/main.c
deleted file mode 100644
index c3d0272af8c61a1dc6feaab88a6b2998fa88e534..0000000000000000000000000000000000000000
--- a/KernelLand/Modules/Interfaces/EDI/main.c
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * Acess2 EDI Layer
- */
-#define DEBUG	0
-#define VERSION	((0<<8)|1)
-#include <acess.h>
-#include <modules.h>
-#include <fs_devfs.h>
-#define	IMPLEMENTING_EDI	1
-#include "edi/edi.h"
-
-#define	VALIDATE_PTR(_ptr,_err)	do{if(!(_ptr))return _err; }while(0)
-#define	GET_DATA(_ptr)	(Object)
-
-#include "edi_io.inc.c"
-#include "edi_int.inc.c"
-
-// === STRUCTURES ===
-typedef struct sAcessEdiDriver {
-	struct sAcessEdiDriver	*Next;
-	tDevFS_Driver	Driver;
-	 int	FileCount;
-	struct {
-		char	*Name;
-		tVFS_Node	Node;
-	}	*Files;
-	edi_object_metadata_t	*Objects;
-	edi_initialization_t	Init;
-	driver_finish_t	Finish;
-} tAcessEdiDriver;
-
-// === PROTOTYPES ===
- int	EDI_Install(char **Arguments);
- int	EDI_DetectDriver(void *Base);
- int	EDI_LoadDriver(void *Base);
-vfs_node *EDI_FS_ReadDir(vfs_node *Node, int Pos);
-vfs_node *EDI_FS_FindDir(vfs_node *Node, char *Name);
- int	EDI_FS_CharRead(vfs_node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
- int	EDI_FS_CharWrite(vfs_node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
- int	EDI_FS_IOCtl(vfs_node *Node, int Id, void *Data);
-data_pointer EDI_GetInternalClass(edi_string_t ClassName);
-
-// === GLOBALS ===
-MODULE_DEFINE(0, VERSION, EDI, EDI_Install, NULL, NULL);
-tModuleLoader	gEDI_Loader = {
-	NULL, "EDI", EDI_DetectDriver, EDI_LoadDriver, NULL
-};
-tSpinlock	glEDI_Drivers;
-tAcessEdiDriver	*gEdi_Drivers = NULL;
-edi_class_declaration_t	*gcEdi_IntClasses[] = {
-	&scEdi_Int_Class_IO, &scEdi_Int_Class_IRQ
-};
-#define	NUM_INT_CLASSES	(sizeof(gcEdi_IntClasses)/sizeof(gcEdi_IntClasses[0]))
-char *csCharNumbers[] = {"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9"};
-char *csBlockNumbers[] = {"blk0", "blk1", "blk2", "blk3", "blk4", "blk5", "blk6", "blk7", "blk8", "blk9"};
-
-// === CODE ===
-/**
- * \fn int EDI_Install(char **Arguments)
- * \brief Stub intialisation routine
- */
-int EDI_Install(char **Arguments)
-{
-	Module_RegisterLoader( &gEDI_Loader );
-	return 1;
-}
-
-/**
- * \brief Detects if a driver should be loaded by the EDI subsystem
- */
-int EDI_DetectDriver(void *Base)
-{
-	if( Binary_FindSymbol(BASE, "driver_init", NULL) == 0 )
-		return 0;
-	
-	return 1;
-}
-
-/**
- * \fn int Edi_LoadDriver(void *Base)
- * \brief Load an EDI Driver from a loaded binary
- * \param Base	Binary Handle
- * \return 0 on success, non zero on error
- */
-int EDI_LoadDriver(void *Base)
-{
-	driver_init_t	init;
-	driver_finish_t	finish;
-	tAcessEdiDriver	*info;
-	 int	i, j;
-	 int	devfsId;
-	edi_class_declaration_t	*classes;
-
-	ENTER("pBase", Base);
-	
-	// Get Functions
-	if( !Binary_FindSymbol(Base, "driver_init", (Uint*)&init)
-	||	!Binary_FindSymbol(Base, "driver_finish", (Uint*)&finish) )
-	{
-		Warning("[EDI  ] Driver %p does not provide both `driver_init` and `driver_finish`\n", Base);
-		Binary_Unload(Base);
-		return 0;
-	}
-	
-	// Allocate Driver Information
-	info = malloc( sizeof(tAcessEdiDriver) );
-	info->Finish = finish;
-	
-	// Initialise Driver
-	info->Init = init( 0, NULL );	// TODO: Implement Resources
-	
-	LOG("info->Init.driver_name = '%s'", info->Init.driver_name);
-	LOG("info->Init.num_driver_classes = %i", info->Init.num_driver_classes);
-	
-	// Count mappable classes
-	classes = info->Init.driver_classes;
-	info->FileCount = 0;
-	info->Objects = NULL;
-	for( i = 0, j = 0; i < info->Init.num_driver_classes; i++ )
-	{
-		if( strncmp(classes[i].name, "EDI-CHARACTER-DEVICE", 32) == 0 )
-		{
-			data_pointer	*obj;
-			// Initialise Object Instances
-			for( ; (obj = classes[i].constructor()); j++ ) {
-				LOG("%i - Constructed '%s'", j, classes[i].name);
-				info->FileCount ++;
-				info->Objects = realloc(info->Objects, sizeof(*info->Objects)*info->FileCount);
-				info->Objects[j].object = obj;
-				info->Objects[j].object_class = &classes[i];
-			}
-		}
-		else
-			LOG("%i - %s", i, classes[i].name);
-	}
-	
-	if(info->FileCount)
-	{
-		 int	iNumChar = 0;
-		// Create VFS Nodes
-		info->Files = malloc( info->FileCount * sizeof(*info->Files) );
-		memset(info->Files, 0, info->FileCount * sizeof(*info->Files));
-		j = 0;
-		for( j = 0; j < info->FileCount; j++ )
-		{
-			classes = info->Objects[j].object_class;
-			if( strncmp(classes->name, "EDI-CHARACTER-DEVICE", 32) == 0 )
-			{
-				LOG("%i - %s", j, csCharNumbers[iNumChar]);
-				info->Files[j].Name = csCharNumbers[iNumChar];
-				info->Files[j].Node.NumACLs = 1;
-				info->Files[j].Node.ACLs = &gVFS_ACL_EveryoneRW;
-				info->Files[j].Node.ImplPtr = &info->Objects[j];
-				info->Files[j].Node.Read = EDI_FS_CharRead;
-				info->Files[j].Node.Write = EDI_FS_CharWrite;
-				info->Files[j].Node.IOCtl = EDI_FS_IOCtl;
-				info->Files[j].Node.CTime =
-					info->Files[j].Node.MTime =
-					info->Files[j].Node.ATime = now();
-				
-				iNumChar ++;
-				continue;
-			}
-		}
-		
-		// Create DevFS Driver
-		info->Driver.ioctl = EDI_FS_IOCtl;
-		memsetda(&info->Driver.rootNode, 0, sizeof(vfs_node) / 4);
-		info->Driver.Name = info->Init.driver_name;
-		info->Driver.RootNode.Flags = VFS_FFLAG_DIRECTORY;
-		info->Driver.RootNode.NumACLs = 1;
-		info->Driver.RootNode.ACLs = &gVFS_ACL_EveryoneRX;
-		info->Driver.RootNode.Length = info->FileCount;
-		info->Driver.RootNode.ImplPtr = info;
-		info->Driver.RootNode.ReadDir = EDI_FS_ReadDir;
-		info->Driver.RootNode.FindDir = EDI_FS_FindDir;
-		info->Driver.RootNode.IOCtl = EDI_FS_IOCtl;
-		
-		// Register
-		devfsId = dev_addDevice( &info->Driver );
-		if(devfsId == -1) {
-			free(info->Files);	// Free Files
-			info->Finish();	// Clean up driver
-			free(info);		// Free info structure
-			Binary_Unload(iDriverBase);	// Unload library
-			return -3;	// Return error
-		}
-	}
-	
-	// Append to loaded list;
-	LOCK(&glEDI_Drivers);
-	info->Next = gEDI_Drivers;
-	gEDI_Drivers = info;
-	RELEASE(&glEDI_Drivers);
-	
-	LogF("[EDI ] Loaded Driver '%s' (%s)\n", info->Init.driver_name, Path);
-	LEAVE('i', 1);
-	return 1;
-}
-
-// --- Filesystem Interaction ---
-/**
- * \brief Read from a drivers class list
- * \param Node	Driver's Root Node
- * \param Pos	Index of file to get
- */
-char *EDI_FS_ReadDir(tVFS_Node *Node, int Pos)
-{
-	tAcessEdiDriver	*info;
-	
-	// Sanity Check
-	if(!Node)	return NULL;
-	
-	// Get Information Structure
-	info = (void *) Node->ImplPtr;
-	if(!info)	return NULL;
-	
-	// Check Position
-	if(Pos < 0)	return NULL;
-	if(Pos >= info->FileCount)	return NULL;
-	
-	return strdup( info->Files[Pos].Name );
-}
-
-/**
- * \fn tVFS_Node *EDI_FS_FindDir(tVFS_Node *Node, char *Name)
- * \brief Find a named file in a driver
- * \param Node	Driver's Root Node
- * \param Name	Name of file to find
- */
-tVFS_Node *EDI_FS_FindDir(tVFS_Node *Node, char *Name)
-{
-	tAcessEdiDriver	*info;
-	 int	i;
-	
-	// Sanity Check
-	if(!Node)	return NULL;
-	if(!Name)	return NULL;
-	
-	// Get Information Structure
-	info = (void *) Node->ImplPtr;
-	if(!info)	return NULL;
-	
-	for( i = 0; i < info->FileCount; i++ )
-	{
-		if(strcmp(info->Files[i].name, Name) == 0)
-			return &info->Files[i].Node;
-	}
-	
-	return NULL;
-}
-
-/**
- * \fn Uint64 EDI_FS_CharRead(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Read from an EDI Character Device
- * \param Node	File Node
- * \param Offset	Offset into file (ignored)
- * \param Length	Number of characters to read
- * \param Buffer	Destination for data
- * \return	Number of characters read
- */
-Uint64 EDI_FS_CharRead(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
-	edi_object_metadata_t	*meta;
-	edi_class_declaration_t	*class;
-	
-	// Sanity Check
-	if(!Node || !Buffer)	return 0;
-	if(Length <= 0)	return 0;
-	// Get Object Metadata
-	meta = (void *) Node->ImplPtr;
-	if(!meta)	return 0;
-	
-	// Get Class
-	class = meta->object_class;
-	if(!class)	return 0;
-	
-	// Read from object
-	if( ((tAnyFunction) class->methods[0].code)( meta->object, Buffer, Length ))
-		return Length;
-
-	return 0;
-}
-
-/**
- * \fn Uint64 EDI_FS_CharWrite(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
- * \brief Write to an EDI Character Device
- * \param Node	File Node
- * \param Offset	Offset into file (ignored)
- * \param Length	Number of characters to write
- * \param Buffer	Source for data
- * \return	Number of characters written
- */
-Uint64 EDI_FS_CharWrite(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
-{
-	edi_object_metadata_t	*meta;
-	edi_class_declaration_t	*class;
-	
-	// Sanity Check
-	if(!Node || !Buffer)	return 0;
-	if(Length <= 0)	return 0;
-	// Get Object Metadata
-	meta = (void *) Node->ImplPtr;
-	if(!meta)	return 0;
-	
-	// Get Class
-	class = meta->object_class;
-	if(!class)	return 0;
-	
-	// Write to object
-	if( ((tAnyFunction) class->methods[1].code)( meta->object, Buffer, Length ))
-		return Length;
-
-	return 0;
-}
-
-/**
- * \fn int EDI_FS_IOCtl(tVFS_Node *Node, int Id, void *Data)
- * \brief Perfom an IOCtl call on the object
- */
-int EDI_FS_IOCtl(tVFS_Node *Node, int Id, void *Data)
-{
-	return 0;
-}
-
-// --- EDI Functions ---
-/**
- * \fn data_pointer EDI_GetDefinedClass(edi_string_t ClassName)
- * \brief Gets the structure of a driver defined class
- * \param ClassName	Name of class to find
- * \return Class definition or NULL
- */
-data_pointer EDI_GetDefinedClass(edi_string_t ClassName)
-{
-	 int	i;
-	tAcessEdiDriver	*drv;
-	edi_class_declaration_t	*classes;
-	
-	for(drv = gEdi_Drivers;
-		drv;
-		drv = drv->Next	)
-	{
-		classes = drv->Init.driver_classes;
-		for( i = 0; i < drv->Init.num_driver_classes; i++ )
-		{
-			if( strncmp(classes[i].name, ClassName, 32) == 0 )
-				return &classes[i];
-		}
-	}
-	return NULL;
-}
-
-/**
- * \fn int32_t EDI_CheckClassExistence(edi_string_t ClassName)
- * \brief Checks if a class exists
- * \param ClassName	Name of class
- * \return 1 if the class exists, -1 otherwise
- */
-int32_t EDI_CheckClassExistence(edi_string_t ClassName)
-{
-	//LogF("check_class_existence: (ClassName='%s')\n", ClassName);
-	if(EDI_GetInternalClass(ClassName))
-		return 1;
-		
-	if(EDI_GetDefinedClass(ClassName))	// Driver Defined
-		return 1;
-	
-	return -1;
-}
-
-/**
- * \fn edi_object_metadata_t EDI_ConstructObject(edi_string_t ClassName)
- * \brief Construct an instance of an class (an object)
- * \param ClassName	Name of the class to construct
- */
-edi_object_metadata_t EDI_ConstructObject(edi_string_t ClassName)
-{
-	edi_object_metadata_t	ret = {0, 0};
-	edi_class_declaration_t	*class;
-	
-	//LogF("EDI_ConstructObject: (ClassName='%s')\n", ClassName);
-	
-	// Get class definition
-	if( !(class = EDI_GetInternalClass(ClassName)) )	// Internal
-		if( !(class = EDI_GetDefinedClass(ClassName)) )	// Driver Defined
-			return ret;		// Return ERROR
-	
-	// Initialise
-	ret.object = class->constructor();
-	if( !ret.object )
-		return ret;	// Return ERROR
-	
-	// Set declaration pointer
-	ret.object_class = class;
-	
-	//LogF("EDI_ConstructObject: RETURN {0x%x,0x%x}\n", ret.object, ret.object_class);
-	return ret;
-}
-
-/**
- * \fn void EDI_DestroyObject(edi_object_metadata_t Object)
- * \brief Destroy an instance of a class
- * \param Object	Object to destroy
- */
-void EDI_DestroyObject(edi_object_metadata_t Object)
-{
-	if( !Object.object )	return;
-	if( !Object.object_class )	return;
-	
-	((edi_class_declaration_t*)(Object.object_class))->destructor( &Object );
-}
-
-/**
- * \fn function_pointer EDI_GetMethodByName(data_pointer ObjectClass, edi_string_t MethodName)
- * \brief Get a method of a class by it's name
- * \param ObjectClass	Pointer to a ::edi_object_metadata_t of the object
- * \param MethodName	Name of the desired method
- * \return Function address or NULL
- */
-function_pointer EDI_GetMethodByName(data_pointer ObjectClass, edi_string_t MethodName)
-{
-	edi_class_declaration_t	*dec = ObjectClass;
-	 int	i;
-	
-	//LogF("get_method_by_name: (ObjectClass=0x%x, MethodName='%s')\n", ObjectClass, MethodName);
-	
-	if(!ObjectClass)	return NULL;
-	
-	for(i = 0; i < dec->num_methods; i++)
-	{
-		if(strncmp(MethodName, dec->methods[i].name, 32) == 0)
-			return dec->methods[i].code;
-	}
-	return NULL;
-}
-
-#if 0
-function_pointer get_method_by_declaration(data_pointer Class, edi_function_declaration_t Declaration);
-#endif
-
-/**
- * \fn edi_string_ptr_t EDI_GetClassParent(edi_string_t ClassName)
- * \brief Get the parent of the named class
- * \todo Implement
- */
-edi_string_ptr_t EDI_GetClassParent(edi_string_t ClassName)
-{
-	WarningEx("EDI", "`get_class_parent` is unimplemented");
-	return NULL;
-}
-
-/**
- * \fn data_pointer EDI_GetInternalClass(edi_string_t ClassName)
- * \brief Get a builtin class
- * \param ClassName	Name of class to find
- * \return Pointer to the ::edi_class_declaration_t of the class
- */
-data_pointer EDI_GetInternalClass(edi_string_t ClassName)
-{
-	 int	i;
-	//LogF("get_internal_class: (ClassName='%s')\n", ClassName);
-	for( i = 0; i < NUM_INT_CLASSES; i++ )
-	{
-		if( strncmp( gcEdi_IntClasses[i]->name, ClassName, 32 ) == 0 ) {
-			return gcEdi_IntClasses[i];
-		}
-	}
-	//LogF("get_internal_class: RETURN NULL\n");
-	return NULL;
-}
-
-/**
- * \fn edi_string_ptr_t EDI_GetObjectClass(data_pointer Object)
- * \brief Get the name of the object of \a Object
- * \param Object	Object to get name of
- * \return Pointer to the class name
- */
-edi_string_ptr_t EDI_GetObjectClass(data_pointer ObjectClass)
-{
-	edi_object_metadata_t	*Metadata = ObjectClass;
-	// Sanity Check
-	if(!ObjectClass)	return NULL;
-	if(!(edi_class_declaration_t*) Metadata->object_class)	return NULL;
-	
-	// Return Class Name
-	return ((edi_class_declaration_t*) Metadata->object_class)->name;
-}
-
-// === EXPORTS ===
-EXPORTAS(EDI_CheckClassExistence, check_class_existence);
-EXPORTAS(EDI_ConstructObject, construct_object);
-EXPORTAS(EDI_DestroyObject, destroy_object);
-EXPORTAS(EDI_GetMethodByName, get_method_by_name);
-EXPORTAS(EDI_GetClassParent, get_class_parent);
-EXPORTAS(EDI_GetInternalClass, get_internal_class);
-EXPORTAS(EDI_GetObjectClass, get_object_class);
diff --git a/KernelLand/Modules/Interfaces/UDI/Makefile b/KernelLand/Modules/Interfaces/UDI/Makefile
index 3b1387bebf6f20d3432c0faba0ace399967d15e5..9b397401a42104ed15025c57cb228839f9ba0991 100644
--- a/KernelLand/Modules/Interfaces/UDI/Makefile
+++ b/KernelLand/Modules/Interfaces/UDI/Makefile
@@ -2,6 +2,7 @@
 #
 
 CPPFLAGS = -I../../../../UDI/include -Iinclude
+CPPFLAGS += -D UDI_VERSION=0x101 -D UDI_PHYSIO_VERSION=0x101 -D UDI_NIC_VERSION=0x101
 
 # - UDI Library Files
 LIB_OBJS := core/logging.o core/strmem.o core/imc.o core/mem.o core/buf.o
diff --git a/KernelLand/Modules/Interfaces/UDI/main.c b/KernelLand/Modules/Interfaces/UDI/main.c
index 1c9fc29cc332a5e04101c14454ea3d771b82c02e..4a655f0c34ce64b0f05c448039ce405e47239723 100644
--- a/KernelLand/Modules/Interfaces/UDI/main.c
+++ b/KernelLand/Modules/Interfaces/UDI/main.c
@@ -37,6 +37,10 @@ tUDI_DriverModule	*gpUDI_LoadedModules;
  */
 int UDI_Install(char **Arguments)
 {
+	if( Arguments && Arguments[0] && strcmp(Arguments[0], "disable") == 0 ) {
+		// Module disabled by user
+		return MODULE_ERR_NOTNEEDED;
+	}
 	Module_RegisterLoader( &gUDI_Loader );
 
 	Proc_SpawnWorker(UDI_int_DeferredThread, NULL);
@@ -121,10 +125,10 @@ static udi_boolean_t _get_token_bool(const char *str, const char **outstr)
 static udi_index_t _get_token_idx(const char *str, const char **outstr)
 {
 	char	*end;
-	int ret = strtol(str, &end, 10);
-	if( ret < 0 || ret > 255 ) {
-		Log_Notice("UDI", "Value '%.*s' out of range for udi_index_t",
-			end-str, str);
+	unsigned long ret = strtoul(str, &end, 10);
+	if( ret > 255 ) {
+		Log_Notice("UDI", "Value '%.*s' (0x%lx) out of range for udi_index_t",
+			end-str, str, ret);
 		*outstr = NULL;
 		return 0;
 	}
@@ -145,8 +149,8 @@ static udi_ubit16_t _get_token_uint16(const char *str, const char **outstr)
 	char	*end;
 	unsigned long ret = strtoul(str, &end, 10);
 	if( ret > 0xFFFF ) {
-		Log_Notice("UDI", "Value '%.*s' out of range for udi_ubit16_t",
-			end-str, str);
+		Log_Notice("UDI", "Value '%.*s' (0x%lx) out of range for udi_ubit16_t",
+			end-str, str, ret);
 		*outstr = NULL;
 		return 0;
 	}
@@ -419,12 +423,14 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 	 int	child_bind_index = 0;
 	 int	device_index = 0;
 	 int	next_unpop_region = 1;
+	#define IF_ERROR(op)	if(!str){error_hit=1;op;}
 	for( int i = 0; i < nLines; i ++ )
 	{
 		const char *str = udipropsptrs[i];
 		if( !*str )
 			continue ;
 		 int	sym = _get_token_sym_v(str, &str, true, caUDI_UdipropsNames);
+		//LOG("Processing option '%s'", (sym >= 0 ? caUDI_UdipropsNames[sym] : "ERR"));
 		switch(sym)
 		{
 		case UDIPROPS__properties_version:
@@ -439,7 +445,7 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 		case UDIPROPS__meta: {
 			tUDI_MetaLangRef *ml = &driver_module->MetaLangs[ml_index++];
 			ml->meta_idx = _get_token_idx(str, &str);
-			if( !str )	continue;
+			IF_ERROR(continue);
 			ml->interface_name = str;
 			// TODO: May need to trim trailing spaces
 			ml->metalang = UDI_int_GetMetaLangByName(ml->interface_name);
@@ -455,7 +461,7 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 			tUDI_PropMessage *msg = &driver_module->Messages[msg_index++];
 			msg->locale = cur_locale;
 			msg->index = _get_token_uint16(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			msg->string = str;
 			//Log_Debug("UDI", "Message %i/%i: '%s'", msg->locale, msg->index, msg->string);
 			break;
@@ -467,7 +473,7 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 		case UDIPROPS__region:
 			{
 			udi_index_t rgn_idx = _get_token_idx(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			// Search for region index (just in case internal_bind_ops appears earlier)
 			tUDI_PropRegion	*rgn = &driver_module->RegionTypes[0];
 			if( rgn_idx > 0 )
@@ -516,7 +522,7 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 					rgn->OverrunTime = _get_token_uint32(str, &str);
 					break;
 				}
-				if( !str )	break ;
+				IF_ERROR(break);
 			}
 			break;
 			}
@@ -524,12 +530,13 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 			{
 			tUDI_BindOps	*bind = &driver_module->Parents[parent_index++];
 			bind->meta_idx = _get_token_idx(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			bind->region_idx = _get_token_idx(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			bind->ops_idx = _get_token_idx(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			bind->bind_cb_idx = _get_token_idx(str, &str);
+			IF_ERROR(continue);
 			if( *str ) {
 				// Expected EOL, didn't get it :(
 			}
@@ -541,9 +548,9 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 			{
 			// Get region using index
 			udi_index_t meta = _get_token_idx(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			udi_index_t rgn_idx = _get_token_idx(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			
 			// Search for region index (just in case the relevant 'region' comes after)
 			tUDI_PropRegion	*rgn = &driver_module->RegionTypes[0];
@@ -569,11 +576,11 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 			rgn->BindMeta = meta;
 			
 			rgn->PriBindOps = _get_token_idx(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			rgn->SecBindOps = _get_token_idx(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			rgn->BindCb = _get_token_idx(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			if( *str ) {
 				// TODO: Please sir, I want an EOL
 			}
@@ -583,10 +590,11 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 			{
 			tUDI_BindOps	*bind = &driver_module->ChildBindOps[child_bind_index++];
 			bind->meta_idx = _get_token_idx(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			bind->region_idx = _get_token_idx(str, &str);
-			if( !str )	continue ;
+			IF_ERROR(continue);
 			bind->ops_idx = _get_token_idx(str, &str);
+			IF_ERROR(continue);
 			if( *str ) {
 				// Expected EOL, didn't get it :(
 			}
@@ -607,26 +615,27 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 			{
 			 int	n_attr = 0;
 			// Count properties (and validate)
-			_get_token_idx(str, &str);	// message
-			if( !str )	continue;
+			_get_token_uint16(str, &str);	// message
+			IF_ERROR(continue);
 			_get_token_idx(str, &str);	// meta
-			if( !str )	continue;
+			IF_ERROR(continue);
 			while( *str )
 			{
 				_get_token_str(str, &str, NULL);
-				if( !str )	break;
+				IF_ERROR(break);
 				_get_token_sym(str, &str, true, "string", "ubit32", "boolean", "array", NULL);
-				if( !str )	break;
+				IF_ERROR(break);
 				_get_token_str(str, &str, NULL);
-				if( !str )	break;
+				IF_ERROR(break);
 				n_attr ++;
 			}
 			// Rewind and actually parse
+			// - Eat the 'device' token and hence reset 'str'
 			_get_token_str(udipropsptrs[i], &str, NULL);
 			
 			tUDI_PropDevSpec *dev = NEW_wA(tUDI_PropDevSpec, Attribs, n_attr);
 			driver_module->Devices[device_index++] = dev;;
-			dev->MessageNum = _get_token_idx(str, &str);
+			dev->MessageNum = _get_token_uint16(str, &str);
 			dev->MetaIdx = _get_token_idx(str, &str);
 			dev->nAttribs = n_attr;
 			n_attr = 0;
@@ -634,10 +643,10 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 			{
 				udi_instance_attr_list_t *at = &dev->Attribs[n_attr];
 				_get_token_str(str, &str, at->attr_name);
-				if( !str )	break;
+				IF_ERROR(break);
 				at->attr_type = _get_token_sym(str, &str, true,
 					" ", "string", "array", "ubit32", "boolean", NULL);
-				if( !str )	break;
+				IF_ERROR(break);
 				udi_ubit32_t	val;
 				switch( dev->Attribs[n_attr].attr_type )
 				{
@@ -662,7 +671,7 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 					UDI_ATTR32_SET(at->attr_value, _get_token_bool(str, &str));
 					break;
 				}
-				if( !str )	break;
+				IF_ERROR(break);
 				n_attr ++;
 			}
 			
@@ -675,7 +684,7 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 	}
 	free(udipropsptrs);
 	if( error_hit ) {
-		Log_Error("UDI", "Error encountered while parsing udiprops for '%s' (%p), bailing",
+		Log_Error("UDI", "Error encountered while parsing udiprops for '%s' (LoadBase=%p), bailing",
 			driver_module->ModuleName, LoadBase);
 		for( int i = 0; i < device_index; i ++ )
 			free(driver_module->Devices[i]);
@@ -688,10 +697,14 @@ tUDI_DriverModule *UDI_int_LoadDriver(void *LoadBase, const udi_init_t *info, co
 		free(driver_module);
 		return NULL;
 	}
+	ASSERTC(device_index, ==, driver_module->nDevices);
 
-	for( int i = 0; i < driver_module->nDevices; i ++ )
+	for( int i = 0; i < driver_module->nDevices; i ++ ) {
+		ASSERT(driver_module);
+		ASSERT(driver_module->Devices[i]);
 		driver_module->Devices[i]->Metalang = UDI_int_GetMetaLang(driver_module,
 			driver_module->Devices[i]->MetaIdx);
+	}
 	
 	// Sort message list
 	// TODO: Sort message list
diff --git a/KernelLand/Modules/Interfaces/UDI/trans/bus_pci.c b/KernelLand/Modules/Interfaces/UDI/trans/bus_pci.c
index deb78259d79705dd8173b12a9a0596e66a2b0f74..990965554acac3ceec30b5f6ebaea3270fa05f1d 100644
--- a/KernelLand/Modules/Interfaces/UDI/trans/bus_pci.c
+++ b/KernelLand/Modules/Interfaces/UDI/trans/bus_pci.c
@@ -6,6 +6,9 @@
  * - PCI Bus Driver
  */
 #define DEBUG	0
+#define UDI_VERSION	0x101
+#define UDI_PHYSIO_VERSION	0x101
+#define UDI_PCI_VERSION	0x101
 #include <udi.h>
 #include <udi_physio.h>
 #include <udi_pci.h>
diff --git a/KernelLand/Modules/Interfaces/UDI/trans/gfx.c b/KernelLand/Modules/Interfaces/UDI/trans/gfx.c
new file mode 100644
index 0000000000000000000000000000000000000000..73efc103227458b9302e90f2f90a3a6cbaaf700e
--- /dev/null
+++ b/KernelLand/Modules/Interfaces/UDI/trans/gfx.c
@@ -0,0 +1,91 @@
+/*
+ * Acess2 UDI Layer
+ * - By John Hodge (thePowersGang)
+ *
+ * trans/gfx.c
+ * - Graphics interface
+ */
+#define DEBUG	1
+#define UDI_VERSION 0x101
+#define UDI_GFX_VERSION 0x101
+#include <udi.h>
+#include <udi_gfx.h>
+#include <acess.h>
+#include <api_drv_video.h>
+
+// === TYPES ===
+typedef struct rdata_s
+{
+	
+} rdata_t;
+
+// === PROTOTYPES ===
+// --- Management metalang
+void acessgfx_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level);
+void acessgfx_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID);
+void acessgfx_final_cleanup_req(udi_mgmt_cb_t *cb);
+// --- GFX Binding
+void acessgfx_channel_event_ind(udi_channel_event_cb_t *cb);
+void acessgfx_bind_ack(udi_gfx_bind_cb_t *cb, udi_index_t sockets, udi_index_t engines, udi_status_t status);
+void acessgfx_unbind_ack(udi_gfx_bind_cb_t *cb);
+void acessgfx_set_connector_ack(udi_gfx_state_cb_t *cb);
+void acessgfx_get_connector_ack(udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+void acessgfx_range_connector_ack(udi_gfx_range_cb_t *cb);
+void acessgfx_set_engine_ack(udi_gfx_state_cb_t *cb);
+void acessgfx_get_engine_ack(udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+void acessgfx_range_engine_ack(udi_gfx_range_cb_t *cb);
+void acessgfx_command_ack(udi_gfx_command_cb_t *cb);
+
+// === CODE ===
+void acessgfx_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level)
+{
+	rdata_t *rdata = UDI_GCB(cb)->context;
+	
+	// Set up structures that don't need interegating the card to do
+	
+	udi_usage_res(cb);
+}
+
+void acessgfx_channel_event_ind(udi_channel_event_cb_t *cb)
+{
+	rdata_t *rdata = UDI_GCB(cb)->context;
+	
+	switch(cb->event)
+	{
+	case UDI_CHANNEL_CLOSED:
+		udi_channel_event_complete(cb);
+		return;
+	case UDI_CHANNEL_BOUND:
+		rdata->active_cb = UDI_GCB(cb);
+		acessgfx_channel_event_ind$bound(cb->params.parent_bound.bind_cb);
+		return;
+	default:
+		// TODO: emit an error of some form?
+		return;
+	}
+}
+
+void acessgfx_channel_event_ind$bound(udi_gfx_bind_cb_t *cb)
+{
+	rdata_t *rdata = UDI_GCB(bind_cb)->context;
+	
+	// request metalanguage-level bind
+	udi_gfx_bind_req(bind_cb);
+	// Continued in acessgfx_bind_ack
+}
+
+void acessgfx_bind_ack(udi_gfx_bind_cb_t *cb, udi_index_t sockets, udi_index_t engines, udi_status_t status)
+{
+	rdata_t *rdata = UDI_GCB(bind_cb)->context;
+	
+	if( status != UDI_OK ) {
+		// binding failed
+		
+		return ;
+	}
+	
+	// 
+}
+
+// === UDI Bindings ===
+
diff --git a/KernelLand/Modules/Interfaces/UDI/trans/gfx.udic b/KernelLand/Modules/Interfaces/UDI/trans/gfx.udic
new file mode 100644
index 0000000000000000000000000000000000000000..d61579d484c292703180e24bbdaa63d557f9a0ea
--- /dev/null
+++ b/KernelLand/Modules/Interfaces/UDI/trans/gfx.udic
@@ -0,0 +1,78 @@
+/*
+ * Acess2 UDI Layer
+ * - By John Hodge (thePowersGang)
+ *
+ * trans/gfx.c
+ * - Graphics interface
+ */
+#define DEBUG	1
+#define UDI_VERSION 0x101
+#define UDI_GFX_VERSION 0x101
+#include <udi.h>
+#include <udi_gfx.h>
+#include <acess.h>
+#include <api_drv_video.h>
+
+// === TYPES ===
+typedef struct rdata_s
+{
+	
+} rdata_t;
+
+// === CODE ===
+void usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level)
+{
+	// Set up structures that don't need interegating the card to do
+	
+}
+
+@1=GFX_CLIENT channel_event_ind(udi_channel_event_cb_t *cb)
+{
+	udi_gfx_bind_cb_t	*bind_cb = cb->params.parent_bound.bind_cb;
+	switch(cb->event)
+	{
+	case UDI_CHANNEL_CLOSED:
+		return;
+	case UDI_CHANNEL_BOUND:
+		[sockets, engines, status] = udi_gfx_bind_req(bind_cb);
+		if( status != UDI_OK ) {
+			return;
+		}
+		// allocate a CB
+		[new_cb] = udi_cb_alloc(bind_cb, ACESSGFX_CB_STATE);
+		udi_gfx_state_cb_t *state_cb = new_cb;
+		for( int i = 0; i < engines; i ++ )
+		{
+			state_cb->subsystem = i;
+			state_cb->attribute = UDI_GFX_PROP_PIXELFORMAT;
+			[pixfmt] = @TRY_NAK[status] udi_gfx_get_engine_req(state_cb)
+			{
+				// Shit to do if NAK happens
+				return ;
+				// break PIXFMT_NONE;
+			}
+			
+			// TODO: Annotate udi_gfx_state_cb_t to note values are kept?
+			state_cb->subsystem = i;
+			state_cb->attribute = UDI_GFX_PROP_OPERATOR_INDEX;
+			[count] = udi_gfx_get_engine_req(state_cb);
+			for( int j = 0; j < count; j ++ )
+			{
+				state_cb->attribute = j;
+				[operator, arg_1, arg_2, arg_3] = udi_gfx_engine_getop_req(state_cb);
+				Log_Debug("UDIGFX", "%i/%i: %i 0x%x 0x%x 0x%x",
+					i, j,
+					operator, arg_1, arg_2, arg_3);
+			}
+		}
+		return;
+	default:
+		// TODO: emit an error of some form?
+		return;
+	}
+}
+
+// === UDI Bindings ===
+
+
+// vim: ft=c
diff --git a/KernelLand/Modules/Interfaces/UDI/trans/gfx_plan.txt b/KernelLand/Modules/Interfaces/UDI/trans/gfx_plan.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b8e4bb9157ab41dc451c6279725a1c6613f21324
--- /dev/null
+++ b/KernelLand/Modules/Interfaces/UDI/trans/gfx_plan.txt
@@ -0,0 +1,12 @@
+
+Upon binding:
+
+1. Enumerate connectors (types and possible inputs)
+2. For all engines that can have their input be -1, get pixel format
+  a. Parse operator list into a tree
+3. Create MIN(n_conns,n_edge) video devices
+  a. Each device represents an edge engine (engine selected on modeset)
+
+Notes:
+- Need to figure out what engine(s) to use as framebuffer, and what ones to use as cursor
+ > Allow user/admin intervention to set these relations (based on an identifier from the driver)
diff --git a/KernelLand/Modules/Libraries/VirtIO/virtio.c b/KernelLand/Modules/Libraries/VirtIO/virtio.c
index 614bd357c5759efeb349413bfe96bc94074ecddb..4631b72c14eb78d05a8072ae2dc178ea6c1d9f15 100644
--- a/KernelLand/Modules/Libraries/VirtIO/virtio.c
+++ b/KernelLand/Modules/Libraries/VirtIO/virtio.c
@@ -390,7 +390,8 @@ void VirtIO_ReleaseBuffer(tVirtIO_Buf *Buffer)
 	do {
 		has_next = !!(queue->Entries[idx].Flags & VRING_DESC_F_NEXT);
 		int next_idx = queue->Entries[idx].Next;
-		ASSERTC(!has_next || next_idx,!=,idx);
+		if( has_next )
+			ASSERTC(next_idx,!=,idx);
 		
 		VirtIO_int_ReleaseQDesc(queue, idx);
 	
diff --git a/KernelLand/Modules/Makefile.tpl b/KernelLand/Modules/Makefile.tpl
index a5ea222abe066d0291ea2004a65a1653d7a5f5f2..c96d8b058e001648a2713ae42b40b7cb4e535533 100644
--- a/KernelLand/Modules/Makefile.tpl
+++ b/KernelLand/Modules/Makefile.tpl
@@ -62,17 +62,17 @@ endif
 
 ifneq ($(BUILDTYPE),static)
 $(BIN): %.kmd.$(ARCH): $(OBJ)
-	@echo --- $(LD) -o $@
+	@echo --- [LD] -o $@
 	@$(LD) --allow-shlib-undefined -shared -nostdlib -o $@ $(OBJ) -defsym=DriverInfo=_DriverInfo_$(FULLNAME) $(LDFLAGS)
 	@$(DISASM) $(BIN) > $(BIN).dsm
 else
 $(BIN): %.xo.$(ARCH): $(OBJ)
-	@echo --- $(LD) -o $@
+	@echo --- [LD] -o $@
 	@$(LD) -r -o $@ $(OBJ) $(LDFLAGS)
 endif
 
 obj-$(_SUFFIX)/%.o: %.c Makefile $(CFGFILES)
-	@echo --- $(CC) -o $@
+	@echo --- [CC] -o $@
 	@mkdir -p $(dir $@)
 	@$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
 	@$(CC) -M $(CPPFLAGS) -MT $@ -MP -o obj-$(_SUFFIX)/$*.d $<
diff --git a/KernelLand/Modules/Network/E1000/e1000.c b/KernelLand/Modules/Network/E1000/e1000.c
index 0ad49b01ae6bc1f7049f591ffca41b0c6c9b410a..600f3348ef560703e98657a65f61e137632d8e3f 100644
--- a/KernelLand/Modules/Network/E1000/e1000.c
+++ b/KernelLand/Modules/Network/E1000/e1000.c
@@ -64,6 +64,7 @@ int E1000_Install(char **Arguments)
 	// Allocate card array
 	gaE1000_Cards = calloc(sizeof(tCard), card_count);
 	if( !gaE1000_Cards ) {
+		Log_Warning("E1000", "Allocation of %i card structures failed", card_count);
 		return MODULE_ERR_MALLOC;
 	}	
 
@@ -88,6 +89,7 @@ int E1000_Install(char **Arguments)
 			Log_Debug("E1000", "Card %i: %P IRQ %i", card_idx, card->MMIOBasePhys, card->IRQ);
 
 			if( E1000_int_InitialiseCard(card) ) {
+				Log_Warning("E1000", "Initialisation of card #%i failed", card_idx);
 				return MODULE_ERR_MALLOC;
 			}
 			
diff --git a/Makefile b/Makefile
index 0edc9835bedf0574c7e004b85db5fe142bada23a..a2e46d10a5f38fb18773ff396442dd6d6e6adda8 100644
--- a/Makefile
+++ b/Makefile
@@ -10,9 +10,10 @@
 
 SUBMAKE = $(MAKE) --no-print-directory
 
-USRLIBS := crt0.o acess.ld ld-acess.so libc.so libc++.so libposix.so
+USRLIBS := crt0.o ld-acess.so libc.so libposix.so libc++.so
 USRLIBS += libreadline.so libnet.so liburi.so libpsocket.so
 USRLIBS += libimage_sif.so libunicode.so libm.so
+USRLIBS += libaxwin4.so
 
 EXTLIBS := 
 #libspiderscript
@@ -23,6 +24,7 @@ USRAPPS += insmod
 USRAPPS += bomb lspci
 USRAPPS += ip dhcpclient ping telnet irc wget telnetd
 USRAPPS += axwin3 gui_ate gui_terminal
+USRAPPS += axwin4
 
 define targetclasses
  AI_$1      := $$(addprefix allinstall-,$$($1))
@@ -66,15 +68,31 @@ all-install:	install-Filesystem SyscallList ai-user ai-kmode
 clean:	clean-kmode clean-user
 install:	install-Filesystem SyscallList install-user install-kmode
 
-utest: $(USRLIBS:%=utest-%)
+utest-build: $(USRLIBS:%=utest-build-%)
+utest-run: $(USRLIBS:%=utest-run-%)
+utest: utest-build utest-run
 
-$(USRLIBS:%=utest-%): utest-%:
+utest-build-%:
 	@CC=$(NCC) $(SUBMAKE) -C Usermode/Libraries/$*_src generate_exp
-	@CC=$(NCC) $(SUBMAKE) -C Usermode/Libraries/$*_src utest -k
+	@CC=$(NCC) $(SUBMAKE) -C Usermode/Libraries/$*_src utest-build
+utest-run-%:
+	@CC=$(NCC) $(SUBMAKE) -C Usermode/Libraries/$*_src utest-run -k
 
 # TODO: Module tests using DiskTool and NetTest
-mtest:
+mtest: mtest-build mtest-run
 	@echo > /dev/null
+mtest-build:
+	# Network
+	@echo "== Build Module Tests"
+	@echo "-- nativelib"
+	@CC=$(NCC) $(SUBMAKE) -C Tools/nativelib
+	@echo "-- NetTest"
+	@CC=$(NCC) $(SUBMAKE) -C Tools/NetTest
+	@echo "-- NetTest Runner"
+	@CC=$(NCC) $(SUBMAKE) -C Tools/NetTest_Runner
+mtest-run:
+	@echo "=== Network Module Test ==="
+	@cd Tools && ./nettest_runner
 
 SyscallList: include/syscalls.h
 include/syscalls.h: KernelLand/Kernel/Makefile KernelLand/Kernel/syscalls.lst
@@ -85,13 +103,13 @@ _build_stmod  := BUILDTYPE=static $(SUBMAKE) -C KernelLand/Modules/
 _build_kernel := $(SUBMAKE) -C KernelLand/Kernel
 
 define rules
-$$(ALL_$1): all-%:
+$$(ALL_$1): all-%: $(CC)
 	+@echo === $2 && $3 all
-$$(AI_$1): allinstall-%:
+$$(AI_$1): allinstall-%: $(CC)
 	+@echo === $2 && $3 all install
-$$(CLEAN_$1): clean-%:
+$$(CLEAN_$1): clean-%: $(CC)
 	+@echo === $2 && $3 clean
-$$(INSTALL_$1): install-%:
+$$(INSTALL_$1): install-%: $(CC)
 	+@$3 install
 endef
 
@@ -100,13 +118,23 @@ $(eval $(call rules,MODULES,Module: $$*,$(_build_stmod)$$*))
 $(eval $(call rules,USRLIBS,User Library: $$*,$(SUBMAKE) -C Usermode/Libraries/$$*_src))
 $(eval $(call rules,EXTLIBS,External Library: $$*,$(SUBMAKE) -C Externals/$$*))
 $(eval $(call rules,USRAPPS,User Application: $$*,$(SUBMAKE) -C Usermode/Applications/$$*_src))
-all-Kernel:
+all-Kernel: $(CC)
 	+@echo === Kernel && $(_build_kernel) all
-allinstall-Kernel:
+allinstall-Kernel: $(CC)
 	+@echo === Kernel && $(_build_kernel) all install
-clean-Kernel:
+clean-Kernel: $(CC)
 	+@$(_build_kernel) clean
-install-Kernel:
+install-Kernel: $(CC)
 	@$(_build_kernel) install
-install-Filesystem:
+install-Filesystem: $(CC)
 	@$(SUBMAKE) install -C Usermode/Filesystem
+
+ifeq ($(ARCHDIR),native)
+.PHONY: $(CC)
+else
+$(CC):
+	@echo ---
+	@echo $(CC) does not exist, recompiling
+	@echo ---
+	make -C Externals/cross-compiler/ -f Makefile.cross
+endif
diff --git a/Makefile.cfg b/Makefile.cfg
index 022a02ef573eb705a4711817ffb39f28f401e64a..d12710cf90852936131825e5ad44055a786b681c 100644
--- a/Makefile.cfg
+++ b/Makefile.cfg
@@ -2,12 +2,15 @@
 # Acess2 Build Configuration
 #
 
+V ?= @
+
 ACESSDIR := $(dir $(lastword $(MAKEFILE_LIST)))
 ACESSDIR := $(shell cd $(ACESSDIR) && pwd)
 
 # Install destination configuration
 DISTROOT := -i $(ACESSDIR)/Acess2.img ::/Acess2
 NCC := $(CC)
+NCXX := $(CXX)
 xCP := mcopy -D o
 xMKDIR := mmd -D s
 
@@ -16,7 +19,6 @@ xMKDIR := mmd -D s
 # Default build programs
 #CC := gcc
 #LD := ld
-AS := nasm
 DISASM := objdump -d -S
 RM := @rm -f
 STRIP := strip
@@ -35,12 +37,26 @@ ifeq ($(ARCHDIR),)
 	ARCHDIR := x86
 endif
 
+# Default compilers
+ifneq ($(ARCHDIR),native)
+CC  = $(COMPILERDIR)bin/$(TRIPLET)-gcc
+CXX = $(COMPILERDIR)bin/$(TRIPLET)-g++
+LD  = $(COMPILERDIR)bin/$(TRIPLET)-ld
+OBJDUMP = $(COMPILERDIR)bin/$(TRIPLET)-objdump
+ ifeq ($(AS),as)
+  AS = $(COMPILERDIR)bin/$(TRIPLET)-gcc -c
+ endif
+endif
+
 ifneq ($(ARCH),host)
  ifneq ($(ARCHDIR),$(ARCH))
   include $(ACESSDIR)/BuildConf/$(ARCHDIR)/Makefile.cfg
  endif
 endif
 
+COMPILERDIR := $(ACESSDIR)/Externals/Output/$(ARCHDIR)-BUILD/
+PATH := $(COMPILERDIR)bin/ $(PATH)
+
 ifeq ($(PLATFORM),)
 	PLATFORM := default
 endif
diff --git a/Notes/TODO b/Notes/TODO
index 601e78adcadf496785f696dbfe6e64431c58eaef..dbc46fada578b5071019302a1e5a021017baead3 100644
--- a/Notes/TODO
+++ b/Notes/TODO
@@ -1,8 +1,5 @@
-Implement Ctrl-C in vterm
 Fix TCP code
 Get IPv6 working
-Implement a DHCP client
-Get UDI working (udiref)
 Finalise AxWin API structure
 Implement input in AxWin
 Write TFTP filesystem driver
diff --git a/RunQemu b/RunQemu
index e69a55ee9b796956ff2da5d806a6a7d0bdcd1b25..1202613f7ed1ec99ca592dfd7ae2b842b78dae22 100755
--- a/RunQemu
+++ b/RunQemu
@@ -80,6 +80,11 @@ while [ $# -ne 0 ]; do
 		QEMU_PARAMS=$QEMU_PARAMS" -device pci-serial,chardev=serial2"
 		QEMU_PARAMS=$QEMU_PARAMS" -chardev socket,id=serial2,host=localhost,port=10023,server,telnet"
 		;;
+	-sata)
+		QEMU_PARAMS=$QEMU_PARAMS" -device ich9-ahci,id=ahci"
+		QEMU_PARAMS=$QEMU_PARAMS" -drive if=none,id=sata_disk,file=HDD_sata.img"
+		QEMU_PARAMS=$QEMU_PARAMS" -device ide-drive,drive=sata_disk,bus=ahci.0"
+		;;
 	esac
 	shift
 done
diff --git a/Tools/NetTest/Makefile b/Tools/NetTest/Makefile
index f76c8b47f8b538378f9114486e5ee918cf4563bc..913a9beabcfc38c74764146888f1a6edfd0c872c 100644
--- a/Tools/NetTest/Makefile
+++ b/Tools/NetTest/Makefile
@@ -23,7 +23,7 @@ L_OBJ = vfs_shim.o nic.o tcpclient.o tcpserver.o helpers.o cmdline_backend.o
 N_OBJ = main.o tap.o mode_cmdline.o
 
 # Compilation Options
-CFLAGS := -Wall -std=gnu99 -g -Werror -O0 -pthread
+CFLAGS := -Wall -std=gnu99 -g -O0 -pthread
 CPPFLAGS := -I include/ -I ../nativelib/include
 K_CPPFLAGS := -I $(KERNEL_SRC)include -I $(MODULE_SRC) -I ../../Usermode/Libraries/ld-acess.so_src/include_exp/
 LDFLAGS += -Wl,--defsym,__buildnum=$(BUILD_NUM) -g -L .. -lpthread -lnativelib
diff --git a/Tools/NetTest/vfs_shim.c b/Tools/NetTest/vfs_shim.c
index 6d420646fa7e3371829cd259cbd4492f4ec83b4d..d2d1d5fe5c45bb3cf3d0ff71816062edb25c1280 100644
--- a/Tools/NetTest/vfs_shim.c
+++ b/Tools/NetTest/vfs_shim.c
@@ -12,7 +12,7 @@
 // === CODE ===
 int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode)
 {
-	const int maxfd = *Threads_GetMaxFD();
+	const int maxfd = *Threads_GetMaxFD(NULL);
 	tVFS_Handle	*handles = *Threads_GetHandlesPtr();
 	if( !handles ) {
 		handles = calloc( maxfd, sizeof(tVFS_Handle) );
@@ -34,7 +34,7 @@ int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode)
 
 tVFS_Handle *VFS_GetHandle(int FD)
 {
-	const int maxfd = *Threads_GetMaxFD();
+	const int maxfd = *Threads_GetMaxFD(NULL);
 	tVFS_Handle	*handles = *Threads_GetHandlesPtr();
 	if( !handles )
 		return NULL;
@@ -47,7 +47,7 @@ tVFS_Handle *VFS_GetHandle(int FD)
 
 int VFS_SetHandle(int FD, tVFS_Node *Node, int Mode)
 {
-	const int maxfd = *Threads_GetMaxFD();
+	const int maxfd = *Threads_GetMaxFD(NULL);
 	tVFS_Handle	*handles = *Threads_GetHandlesPtr();
 	if( !handles )
 		return -1;
diff --git a/Tools/NetTest_Runner/Makefile b/Tools/NetTest_Runner/Makefile
index e9575793bb1b8b6202740ad133b078e3d1730100..59704c70404fec4ba9c18b01adb817144229e9c6 100644
--- a/Tools/NetTest_Runner/Makefile
+++ b/Tools/NetTest_Runner/Makefile
@@ -6,7 +6,7 @@ OBJ += link.o
 OBJ += test_arp.o test_tcp.o
 BIN := ../nettest_runner
 
-CFLAGS := -Wall -std=c99
+CFLAGS := -Wall -std=c99 -g
 CPPFLAGS := -Iinclude
 LIBS := 
 
@@ -23,7 +23,7 @@ $(BIN): $(OBJ)
 	@echo [CC] -o $@
 	@$(CC) $(LINKFLAGS) -o $@ $(OBJ) $(LIBS)
 
-obj/%.o: %.c
+obj/%.o: %.c Makefile
 	@mkdir -p $(dir $@)
 	@echo [CC] -c -o $@
 	@$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< -MMD -MP
diff --git a/Tools/NetTest_Runner/include/stack.h b/Tools/NetTest_Runner/include/stack.h
index 7ac2083b3e69ddd69849c1f06f13cbfd00772351..0110f04b4622996f2455c15bfbec253219dadff0 100644
--- a/Tools/NetTest_Runner/include/stack.h
+++ b/Tools/NetTest_Runner/include/stack.h
@@ -12,7 +12,7 @@ extern void	Stack_AddInterface(const char *Name, int AddrType, const void *Addr,
 extern void	Stack_AddRoute(int Type, const void *Network, int MaskBits, const void *NextHop);
 
 extern void	Stack_AddArg(const char *Fmt, ...);
-extern int	Stack_Start(const char *Subcommand);
+extern int	Stack_Start(const char *TestName, const char *Subcommand);
 extern void	Stack_Kill(void);
 
 extern int	Stack_SendCommand(const char *CommandString, ...);
diff --git a/Tools/NetTest_Runner/include/test.h b/Tools/NetTest_Runner/include/test.h
index bf2da1516c83030e28956d8934655edb434a8852..9ad23c35cce0e3699962d978e69a9935b3d9e904 100644
--- a/Tools/NetTest_Runner/include/test.h
+++ b/Tools/NetTest_Runner/include/test.h
@@ -6,6 +6,7 @@
 #include <stddef.h>
 
 #define TEST_SETNAME(name)	test_setname(name)
+#define TEST_STEP(name) 	do{}while(0)	//test_setstep(name)
 #define TEST_ASSERT(cnd)	do{if(!(cnd)) {test_assertion_fail(__FILE__,__LINE__,"%s",#cnd);return false;}}while(0)
 #define TEST_ASSERT_REL(a,r,b)	do{long long a_val=(a),b_val=(b);if(!(a_val r b_val)) {test_assertion_fail(__FILE__,__LINE__,"%s(0x%llx)%s%s(0x%llx)",#a,a_val,#r,#b,b_val);return false;}}while(0)
 #define TEST_WARN(msg...)	test_message(__FILE__,__LINE__,msg)
@@ -16,5 +17,18 @@ extern void	test_assertion_fail(const char *filename, int line, const char *test
 extern void	test_trace(const char *msg, ...);
 extern void	test_trace_hexdump(const char *hdr, const void *data, size_t len);
 
+// Some helpful macros
+// - They require some names to be present
+#define RX_HEADER \
+	size_t	rxlen, ofs, len; \
+	do { ofs = 0; ofs = ofs; len = 0; len = len; } while(0);\
+	char rxbuf[MTU]
+#define TEST_HEADER \
+	TEST_SETNAME(__func__);\
+	RX_HEADER
+
+#define TEST_ASSERT_rx()	TEST_ASSERT( rxlen = Net_Receive(0, sizeof(rxbuf), rxbuf, ERX_TIMEOUT) )
+#define TEST_ASSERT_no_rx()	TEST_ASSERT( Net_Receive(0, sizeof(rxbuf), rxbuf, NRX_TIMEOUT) == 0 )
+
 #endif
 
diff --git a/Tools/NetTest_Runner/include/tests.h b/Tools/NetTest_Runner/include/tests.h
index a96f3881f9cd1d190916f653bf789a5035d8f465..f6cc1356c85fe83ee316efeebebb90fc923a6a1a 100644
--- a/Tools/NetTest_Runner/include/tests.h
+++ b/Tools/NetTest_Runner/include/tests.h
@@ -10,6 +10,7 @@
 
 extern bool	Test_ARP_Basic(void);
 extern bool	Test_TCP_Basic(void);
+extern bool	Test_TCP_Reset(void);
 extern bool	Test_TCP_WindowSizes(void);
 
 #endif
diff --git a/Tools/NetTest_Runner/main.c b/Tools/NetTest_Runner/main.c
index b1d95c13f847b93b897171e8fba0f8e4c69f6a97..5c175d71e8d60f633478c560fc7168b5c96c71e3 100644
--- a/Tools/NetTest_Runner/main.c
+++ b/Tools/NetTest_Runner/main.c
@@ -23,43 +23,54 @@ int main(int argc, char *argv[])
 		return 1;
 
 	typedef bool t_test(void);
-	t_test	*tests[] = {
-		Test_ARP_Basic,
-		Test_TCP_Basic,
-		Test_TCP_WindowSizes,
-		NULL
+	struct {
+		t_test	*fcn;
+		const char *name;
+	} tests[] = {
+		#define _(fcn)	{fcn, #fcn}
+		_(Test_ARP_Basic),
+		_(Test_TCP_Basic),
+		//_(Test_TCP_WindowSizes),
+		_(Test_TCP_Reset),
+		{NULL,NULL}
 		};
 
+	// Truncate the two output files
 	// TODO: Move to stack.c
-	FILE	*fp;
-	fp = fopen("stdout.txt", "w");	fclose(fp);
-	fp = fopen("stderr.txt", "w");	fclose(fp);
+	fclose( fopen("stdout.txt", "w") );
+	fclose( fopen("stderr.txt", "w") );
 	
 	Net_Open(0, "/tmp/acess2net");
 
-	for(int i = 0; tests[i]; i ++ )
+	 int	n_pass = 0;
+	 int	n_fail = 0;
+	for(int i = 0; tests[i].fcn; i ++ )
 	{
 		Stack_AddDevice("/tmp/acess2net", (char[]){TEST_MAC});
 		Stack_AddInterface("eth0", 4, (const char[]){TEST_IP}, 24);
 		Stack_AddRoute(4, "\0\0\0\0", 0, (const char[]){HOST_IP});
-		if( Stack_Start("cmdline") )
+		if( Stack_Start(tests[i].name, "cmdline") )
 			goto teardown;
 		
 		if( Net_Receive(0, 1, &argc, 1000) == 0 )
 			goto teardown;
 		
-		if( tests[i]() )
-			printf("%s: PASS\n", gsTestName);
+		bool	result = tests[i].fcn();
+		
+		printf("%s: %s\n", gsTestName, (result ? "PASS" : "FAIL"));
+		if(result)
+			n_pass ++;
 		else
-			printf("%s: FAIL\n", gsTestName);
+			n_fail ++;
 	
 	teardown:
 		Stack_Kill();
 	}
 	Net_Close(0);
 	unlink("/tmp/acess2net");
+	printf("--- All tests done %i pass, %i fail\n", n_pass, n_fail);
 
-	return 0;
+	return n_fail;
 }
 
 void PrintUsage(const char *ProgName)
diff --git a/Tools/NetTest_Runner/net.c b/Tools/NetTest_Runner/net.c
index 231df5024ff1c4284e67ce608da85276661e7efa..4cbf3c8de88ffc8682f8d615f4cf875eb86ac988 100644
--- a/Tools/NetTest_Runner/net.c
+++ b/Tools/NetTest_Runner/net.c
@@ -9,6 +9,8 @@
 #include <sys/select.h>
 #include "net.h"
 #include <stdint.h>
+#include <unistd.h>	// unlink/...
+#include <sys/time.h>	// gettimeofday
 
 #define CONNECT_TIMEOUT	10*1000
 #define MAX_IFS	4
@@ -145,7 +147,7 @@ size_t Net_Receive(int IfNum, size_t MaxLen, void *DestBuf, unsigned int Timeout
 	
 	if( Net_int_EnsureConnected(IfNum) && WaitOnFD(If->FD, false, Timeout) )
 	{
-		size_t rv = recvfrom(If->FD, DestBuf, MaxLen, 0, &If->addr, &If->addrlen);
+		size_t rv = recvfrom(If->FD, DestBuf, MaxLen, 0, (struct sockaddr*)&If->addr, &If->addrlen);
 		Net_int_SavePacket(If, rv, DestBuf);
 		return rv;
 	}
@@ -160,7 +162,7 @@ void Net_Send(int IfNum, size_t Length, const void *Buf)
 	if( !WaitOnFD(If->FD, true, CONNECT_TIMEOUT) )
 		return ;
 	Net_int_SavePacket(If, Length, Buf);
-	int rv = sendto(If->FD, Buf, Length, 0, &If->addr, If->addrlen);
+	int rv = sendto(If->FD, Buf, Length, 0, (struct sockaddr*)&If->addr, If->addrlen);
 	if( rv < 0 )
 		perror("Net_Send - send");
 }
diff --git a/Tools/NetTest_Runner/stack.c b/Tools/NetTest_Runner/stack.c
index 7d5e2613fd7c0a3a36755951e4dc36ceb54b1a26..07f8be7a58aea02e76126416d6aa9a2983dcca52 100644
--- a/Tools/NetTest_Runner/stack.c
+++ b/Tools/NetTest_Runner/stack.c
@@ -12,6 +12,8 @@
 
 #include <fcntl.h>
 #include <spawn.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 
 #define MAX_ARGS	16
 
@@ -53,6 +55,7 @@ void Stack_AddInterface(const char *Name, int AddrType, const void *Addr, int Ma
 		break;
 	default:
 		assert(AddrType == 4);
+		fprintf(stderr, "Stack_AddInterface: Bad address type %i\n", AddrType);
 		exit(1);
 	}
 }
@@ -63,14 +66,24 @@ void Stack_AddRoute(int Type, const void *Network, int MaskBits, const void *Nex
 
 void Stack_AddArg(const char *Fmt, ...)
 {
+	if( giNumStackArgs == MAX_ARGS ) {
+		fprintf(stderr, "ERROR: Too many arguments to stack, %i max\n", MAX_ARGS);
+		return ;
+	}
 	va_list	args;
+	
 	va_start(args, Fmt);
 	size_t len = vsnprintf(NULL, 0, Fmt, args);
 	va_end(args);
+
 	char *arg = malloc(len+1);
+	assert(arg);
+
 	va_start(args, Fmt);
 	vsnprintf(arg, len+1, Fmt, args);
 	va_end(args);
+	
+	assert( arg[len] == '\0' );
 	gasStackArgs[giNumStackArgs++] = arg;
 }
 
@@ -81,7 +94,7 @@ void sigchld_handler(int signum)
 	fprintf(stderr, "FAILURE: Child exited (%i)\n", status);
 }
 
-int Stack_Start(const char *Subcommand)
+int Stack_Start(const char *RunName, const char *Subcommand)
 {
 	Stack_AddArg(Subcommand);
 	
@@ -96,8 +109,8 @@ int Stack_Start(const char *Subcommand)
 	fcntl(giStack_InFD, F_SETFD, FD_CLOEXEC);
 
 	FILE	*fp;
-	fp = fopen("stdout.txt", "a"); fprintf(fp, "--- Startup\n"); fclose(fp);
-	fp = fopen("stderr.txt", "a"); fprintf(fp, "--- Startup\n"); fclose(fp);
+	fp = fopen("stdout.txt", "a"); fprintf(fp, "--- TEST: %s\n", RunName); fclose(fp);
+	fp = fopen("stderr.txt", "a"); fprintf(fp, "--- TEST: %s\n", RunName); fclose(fp);
 
 	posix_spawn_file_actions_t	fa;
 	posix_spawn_file_actions_init(&fa);
@@ -123,6 +136,10 @@ void Stack_Kill(void)
 		kill(giStack_PID, SIGTERM);
 		giStack_PID = 0;
 	}
+	
+	for( int i = 1; i < giNumStackArgs; i ++ )
+		free(gasStackArgs[i]);
+	giNumStackArgs = 1;
 }
 
 int Stack_SendCommand(const char *Fmt, ...)
diff --git a/Tools/NetTest_Runner/test_arp.c b/Tools/NetTest_Runner/test_arp.c
index 92acc718e4f66b77ee156e185cec294c90bbc9b7..9c6f04447eb6c32db5f268e035d68a29ef14e720 100644
--- a/Tools/NetTest_Runner/test_arp.c
+++ b/Tools/NetTest_Runner/test_arp.c
@@ -6,25 +6,26 @@
 #include "stack.h"
 #include "arp.h"
 
+static const int	ERX_TIMEOUT = 1000;	// Expect RX timeout (timeout=failure)
+static const int	NRX_TIMEOUT = 250;	// Not expect RX timeout (timeout=success)
+
 bool Test_ARP_Basic(void)
 {
-	TEST_SETNAME(__func__);
-	size_t	rxlen;
-	char rxbuf[MTU];
+	TEST_HEADER;
 	
 	// Request test machine's IP
 	ARP_SendRequest(0, BLOB(TEST_IP));
-	TEST_ASSERT( rxlen = Net_Receive(0, sizeof(rxbuf), rxbuf, 1000) );
+	TEST_ASSERT_rx();
 	TEST_ASSERT( ARP_Pkt_IsResponse(rxlen, rxbuf, BLOB(TEST_IP), BLOB(TEST_MAC)) );
 
 	// Request host machine's IP
 	ARP_SendRequest(0, BLOB(HOST_IP));
-	TEST_ASSERT( Net_Receive(0, sizeof(rxbuf), rxbuf, 1000) == 0 );
+	TEST_ASSERT_no_rx();
 
 	#if 0	
 	// Ask test machine to request our IP
 	Stack_SendCommand("arprequest "HOST_IP_STR);
-	TEST_ASSERT( rxlen = Net_Receive(0, sizeof(rxbuf), rxbuf, 1000) );
+	TEST_ASSERT_rx();
 	TEST_ASSERT( ARP_Pkt_IsRequest(rxlen, rxbuf, HOST_IP) );
 
 	// Respond
@@ -32,7 +33,7 @@ bool Test_ARP_Basic(void)
 	
 	// Ask test machine to request our IP again (expecting nothing)
 	Stack_SendCommand("arprequest "HOST_IP_STR);
-	TEST_ASSERT( !Net_Receive(0, sizeof(rxbuf), rxbuf, 1000) );
+	TEST_ASSERT_no_rx();
 	#endif
 	
 	return true;
diff --git a/Tools/NetTest_Runner/test_tcp.c b/Tools/NetTest_Runner/test_tcp.c
index 36942a5a1f0890fa6149699965d0c45d2d51d283..42aa2d5032c77c6bf480d14e55812452d74c6959 100644
--- a/Tools/NetTest_Runner/test_tcp.c
+++ b/Tools/NetTest_Runner/test_tcp.c
@@ -4,6 +4,7 @@
  *
  * test_tcp.c
  * - Tests for the behavior of the "Transmission Control Protocol"
+ * - These tests are written off RFC793
  */
 #include "test.h"
 #include "tests.h"
@@ -13,20 +14,18 @@
 #include "tcp.h"
 #include <string.h>
 
-#define TEST_ASSERT_rx()	TEST_ASSERT( rxlen = Net_Receive(0, sizeof(rxbuf), rxbuf, ERX_TIMEOUT) )
-#define TEST_ASSERT_no_rx()	TEST_ASSERT( Net_Receive(0, sizeof(rxbuf), rxbuf, NRX_TIMEOUT) == 0 )
-const int	ERX_TIMEOUT = 1000;	// Expect RX timeout (timeout=failure)
-const int	NRX_TIMEOUT = 250;	// Not expect RX timeout (timeout=success)
-const int	RETX_TIMEOUT = 1000;	// OS PARAM - Retransmit timeout
-const int	LOST_TIMEOUT = 1000;	// OS PARAM - Time before sending an ACK 
-const int	DACK_TIMEOUT = 500;	// OS PARAM - Timeout for delayed ACKs
-const size_t	DACK_BYTES = 4096;	// OS PARAM - Threshold for delayed ACKs
+#define TEST_TIMERS	0
+
+static const int	ERX_TIMEOUT = 1000;	// Expect RX timeout (timeout=failure)
+static const int	NRX_TIMEOUT = 250;	// Not expect RX timeout (timeout=success)
+static const int	RETX_TIMEOUT = 1000;	// OS PARAM - Retransmit timeout
+static const int	LOST_TIMEOUT = 1000;	// OS PARAM - Time before sending an ACK 
+static const int	DACK_TIMEOUT = 500;	// OS PARAM - Timeout for delayed ACKs
+static const size_t	DACK_BYTES = 4096;	// OS PARAM - Threshold for delayed ACKs
 
 bool Test_TCP_Basic(void)
 {
-	TEST_SETNAME(__func__);
-	size_t	rxlen, ofs, len;
-	char rxbuf[MTU];
+	TEST_HEADER;
 
 	tTCPConn	testconn = {
 		.IFNum = 0, .AF = 4,
@@ -48,6 +47,7 @@ bool Test_TCP_Basic(void)
 	// > RFC793 Pg.65
 	
 	// 1.1. Send SYN packet
+	TEST_STEP("1.1. Send SYN packet to CLOSED");
 	TCP_SendC(&testconn, TCP_SYN, testblob_len, testblob);
 	testconn.RSeq = 0;
 	testconn.LSeq += testblob_len;
@@ -57,6 +57,7 @@ bool Test_TCP_Basic(void)
 	TEST_ASSERT_REL(ofs, ==, rxlen);
 	
 	// 1.2. Send a SYN,ACK packet
+	TEST_STEP("1.2. Send SYN,ACK packet to CLOSED");
 	testconn.RSeq = 12345;
 	TCP_SendC(&testconn, TCP_SYN|TCP_ACK, 0, NULL);
 	// Expect a TCP_RST with SEQ=ACK
@@ -66,6 +67,7 @@ bool Test_TCP_Basic(void)
 	testconn.LSeq ++;
 	
 	// 1.3. Send a RST packet
+	TEST_STEP("1.2. Send RST packet to CLOSED");
 	TCP_SendC(&testconn, TCP_RST, 0, NULL);
 	// Expect nothing
 	TEST_ASSERT_no_rx();
@@ -183,7 +185,7 @@ bool Test_TCP_Basic(void)
 	// 2.6. Close connection (TCP FIN)
 	TCP_SendC(&testconn, TCP_ACK|TCP_FIN, 0, NULL);
 	testconn.LSeq ++;	// Empty = 1 byte
-	// Expect ACK? (Does acess do delayed ACKs here?)
+	// Expect ACK? (Does Acess do delayed ACKs here?)
 	TEST_ASSERT_rx();
 	TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_ACK) );
 	TEST_ASSERT_REL( len, ==, 0 );
@@ -206,20 +208,41 @@ bool Test_TCP_Basic(void)
 	return true;
 }
 
+bool Test_TCP_int_OpenConnection(tTCPConn *Conn)
+{
+	RX_HEADER;
+	// >> SYN
+	TCP_SendC(Conn, TCP_SYN, 0, NULL);
+	Conn->LSeq ++;
+	TEST_ASSERT_rx();
+	// << SYN|ACK (save remote sequence number)
+	TCP_SkipCheck_Seq(true);
+	TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, Conn, TCP_SYN|TCP_ACK) );
+	TEST_ASSERT_REL(len, ==, 0);
+	Conn->RSeq = TCP_Pkt_GetSeq(rxlen, rxbuf, Conn->AF) + 1;
+	// >> ACK
+	TCP_SendC(Conn, TCP_ACK, 0, NULL);
+	TEST_ASSERT_no_rx();
+	return true;
+}
+
+#if 0
 bool Test_TCP_SYN_RECEIVED(void)
 {
-	TEST_SETNAME(__func__);
+	TEST_HEADER;
+	
 	// 1. Get into SYN-RECEIVED
+	TCP_SendC(&testconn, TCP_SYN, 0, NULL);
 	
 	// 2. Send various non-ACK packets
 	return false;
 }
+#endif
 
-bool Test_TCP_WindowSizes(void)
+bool Test_TCP_Reset(void)
 {
-	TEST_SETNAME(__func__);
-	size_t	rxlen, ofs, len;
-	char rxbuf[MTU];
+	TEST_HEADER;
+	
 	tTCPConn	testconn = {
 		.IFNum = 0,
 		.AF = 4,
@@ -231,20 +254,71 @@ bool Test_TCP_WindowSizes(void)
 		.RSeq = 0x600,
 		.Window = 128
 	};
-	char	testdata[152];
-	memset(testdata, 0xAB, sizeof(testdata));
-	
+
 	Stack_SendCommand("tcp_echo_server %i", testconn.RPort);
-	// > Open Connection
+
+	// 1. Response in listen-based SYN-RECEIVED
+	// >> SYN
 	TCP_SendC(&testconn, TCP_SYN, 0, NULL);
 	testconn.LSeq ++;
+	// << SYN|ACK :: Now in SYN-RECEIVED
 	TEST_ASSERT_rx();
 	TCP_SkipCheck_Seq(true);
 	TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_SYN|TCP_ACK) );
 	TEST_ASSERT_REL(len, ==, 0);
 	testconn.RSeq = TCP_Pkt_GetSeq(rxlen, rxbuf, testconn.AF) + 1;
+	// >> RST (not ACK)
+	TCP_SendC(&testconn, TCP_RST, 0, NULL);
+	// << nothing (connection should now be dead)
+	TEST_ASSERT_no_rx();
+	// >> ACK (this should be to a closed conneciton, see LISTEN[ACK] above)
 	TCP_SendC(&testconn, TCP_ACK, 0, NULL);
+	// << RST
+	TEST_ASSERT_rx();
+	TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_RST) );
+	TEST_ASSERT_REL(len, ==, 0);
+	
+	// 2. Response in open-based SYN-RECEIVED? (What is that?)
+	TEST_WARN("TODO: RFC793 pg70 mentions OPEN-based SYN-RECEIVED");
+	
+	testconn.LPort += 1234;
+	// ESTABLISHED[RST] - RFC793:Pg70
+	// 2. Response in ESTABLISHED 
+	TEST_ASSERT( Test_TCP_int_OpenConnection(&testconn) );
+	// >> RST
+	TCP_SendC(&testconn, TCP_RST, 0, NULL);
+	// << no response, connection closed
 	TEST_ASSERT_no_rx();
+	// >> ACK (LISTEN[ACK])
+	TCP_SendC(&testconn, TCP_ACK, 0, NULL);
+	// << RST
+	TEST_ASSERT_rx();
+	TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_RST) );
+	TEST_ASSERT_REL(len, ==, 0);
+	
+	return true;
+}
+
+bool Test_TCP_WindowSizes(void)
+{
+	TEST_HEADER;
+	tTCPConn	testconn = {
+		.IFNum = 0,
+		.AF = 4,
+		.LAddr = BLOB(HOST_IP),
+		.RAddr = BLOB(TEST_IP),
+		.LPort = 44359,
+		.RPort = 9,
+		.LSeq = 0x600,
+		.RSeq = 0x600,
+		.Window = 128
+	};
+	char	testdata[152];
+	memset(testdata, 0xAB, sizeof(testdata));
+	
+	Stack_SendCommand("tcp_echo_server %i", testconn.RPort);
+	// > Open Connection
+	TEST_ASSERT( Test_TCP_int_OpenConnection(&testconn) );
 	
 	// 1. Test remote respecting our transmission window (>=1 byte)
 	// > Send more data than our RX window
@@ -265,7 +339,47 @@ bool Test_TCP_WindowSizes(void)
 	TEST_ASSERT_REL(len, ==, 1);
 	testconn.RSeq += len;
 	// 2. Test remote handling our window being 0 (should only send ACKs)
+	// TODO: 
 	// 3. Test that remote drops data outside of its RX window
 	// 3.1. Ensure that data that wraps the end of the RX window is handled correctly
+	// TODO: 
+	return false;
+}
+
+// RFC793 pg41
+bool Test_TCP_Retransmit(void)
+{
+	TEST_HEADER;
+	tTCPConn	testconn = {
+		.IFNum = 0,
+		.AF = 4,
+		.LAddr = BLOB(HOST_IP),
+		.RAddr = BLOB(TEST_IP),
+		.LPort = 44359,
+		.RPort = 9,
+		.LSeq = 0x600,
+		.RSeq = 0x600,
+		.Window = 128
+	};
+	char	testdata[128];
+	memset(testdata, 0xAB, sizeof(testdata));
+
+	TEST_STEP("1. Open and connect to TCP server");
+	Stack_SendCommand("tcp_echo_server %i", testconn.RPort);
+	TEST_ASSERT( Test_TCP_int_OpenConnection(&testconn) );
+	
+	
+	TEST_STEP("2. Send data and expect it to be echoed");
+	TCP_SendC(&testconn, TCP_PSH, sizeof(testdata), testdata);
+	testconn.LSeq += sizeof(testdata);
+	TEST_ASSERT_rx();
+	TEST_ASSERT( TCP_Pkt_CheckC(rxlen, rxbuf, &ofs, &len, &testconn, TCP_ACK|TCP_PSH) );
+	
+	TEST_STEP("3. Expect nothing for TCP_RTX_TIMEOUT_1");
+	TEST_ASSERT( Net_Receive(0, sizeof(rxbuf), rxbuf, RETX_TIMEOUT-100) == 0 );
+	
+	TEST_STEP("4. Expect a retransmit");
+	TEST_ASSERT_rx();
+	
 	return false;
 }
diff --git a/Tools/nativelib/Makefile b/Tools/nativelib/Makefile
index 1a50d88128722ea0a4eb2863e0d66048a8f1b5fd..8a8fc3300df42bc687a288a33f0fb6d672e0d0f9 100644
--- a/Tools/nativelib/Makefile
+++ b/Tools/nativelib/Makefile
@@ -19,7 +19,7 @@ KOBJ := $(KOBJ:%.o=obj/_Kernel/%.o)
 OBJ := $(NOBJ) $(LOBJ) $(KOBJ)
 BIN := ../libnativelib.a
 
-CFLAGS := -Wall -std=c99 -Werror
+CFLAGS := -Wall -std=c99
 CPPFLAGS := -I include
 
 
diff --git a/Tools/nativelib/include/threads_int.h b/Tools/nativelib/include/threads_int.h
index 7bd91c17ec92431c3d9e4e666096b1176df9eca8..08bb4f789fa2ebc56fc316295272c1b11dadd65d 100644
--- a/Tools/nativelib/include/threads_int.h
+++ b/Tools/nativelib/include/threads_int.h
@@ -103,12 +103,12 @@ extern int	Threads_int_CreateThread(struct sThread *Thread);
 extern int	Threads_int_ThreadingEnabled(void);
 
 
-extern tThread	*Proc_GetCurThread(void);
-extern tThread	*Threads_RemActive(void);
-extern void	Threads_AddActive(tThread *Thread);
+extern struct sThread	*Proc_GetCurThread(void);
+extern struct sThread	*Threads_RemActive(void);
+extern void	Threads_AddActive(struct sThread *Thread);
 extern void	Threads_int_WaitForStatusEnd(enum eThreadStatus Status);
-extern int	Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, tThread **ListHead, tThread **ListTail, tShortSpinlock *Lock);
-extern void	Semaphore_ForceWake(tThread *Thread);
+extern int	Threads_int_Sleep(enum eThreadStatus Status, void *Ptr, int Num, struct sThread **ListHead, struct sThread **ListTail, tShortSpinlock *Lock);
+extern void	Semaphore_ForceWake(struct sThread *Thread);
 
 extern tThreadIntMutex	*Threads_int_MutexCreate(void);
 extern void	Threads_int_MutexDestroy(tThreadIntMutex *Mutex);
diff --git a/Tools/nativelib/threads.c b/Tools/nativelib/threads.c
index dafdc404ce6d87cac00a625a683266ebf6cb55c6..f80447334fcc89e47d5d7332c2d1e3c526600c40 100644
--- a/Tools/nativelib/threads.c
+++ b/Tools/nativelib/threads.c
@@ -40,9 +40,12 @@ tGID Threads_GetGID(void) { return 0; }
 
 tTID Threads_GetTID(void) { return lpThreads_This ? lpThreads_This->TID : 0; }
 
-int *Threads_GetMaxFD(void)        { return &lpThreads_This->Process->MaxFDs;  }
-char **Threads_GetCWD(void)        { return &lpThreads_This->Process->CWD;     }
-char **Threads_GetChroot(void)     { return &lpThreads_This->Process->Chroot;  }
+static inline tProcess* getproc(tProcess *Process) {
+	return (Process ? Process : lpThreads_This->Process);
+}
+int *Threads_GetMaxFD(tProcess *Process)	{ return &getproc(Process)->MaxFDs;  }
+char **Threads_GetCWD(tProcess *Process)        { return &getproc(Process)->CWD;     }
+char **Threads_GetChroot(tProcess *Process)     { return &getproc(Process)->Chroot;  }
 void **Threads_GetHandlesPtr(void) { return &lpThreads_This->Process->Handles; }
 
 void Threads_Yield(void)
diff --git a/UDI/Tools/udibuild.ini b/UDI/Tools/udibuild.ini
index 227adc02f09129b1b9fabe7c29652d659f91e3c1..c5187fac11f755bfbac8677f987cc0580c79abb9 100644
--- a/UDI/Tools/udibuild.ini
+++ b/UDI/Tools/udibuild.ini
@@ -1,6 +1,8 @@
 [COMMON]
 
 [ia32]
-CFLAGS=-ffreestanding -I/home/tpg/Projects/GitClones/acess2/UDI/include/
+CFLAGS=-ffreestanding -I/home/tpg/Projects/GitClones/acess2/UDI/include/ -Wall -Wextra -Wno-unused-parameter
+# -std=c89
+# -Wc++-compat
 CC=i586-elf-gcc
 LD=i586-elf-ld
diff --git a/UDI/Tools/udibuild_src/build.c b/UDI/Tools/udibuild_src/build.c
index 4d0b294781cc59d55c67a65ba378538caee23ba9..88f047385cd623f1bf9627c111acfe1cb36fa129 100644
--- a/UDI/Tools/udibuild_src/build.c
+++ b/UDI/Tools/udibuild_src/build.c
@@ -40,6 +40,9 @@ int Build_CompileFile(tIniFile *opts, const char *abi, tUdiprops *udiprops, tUdi
 		srcfile->CompileOpts ? srcfile->CompileOpts : "",
 		srcfile->Filename, objfile);
 	printf("--- Compiling: %s\n", srcfile->Filename);
+	if( gbTraceEnabled ) {
+		printf(">> %s\n", cmd);
+	}
 	 int rv = system(cmd);
 	free(cmd);
 	free(objfile);
@@ -110,7 +113,9 @@ int Build_LinkObjects(tIniFile *opts, const char *abi, tUdiprops *udiprops)
 		abi, udiprops->ModuleName, objfiles_str, udiprops_c
 		);
 	printf("--- Linking: bin/%s/%s\n", abi, udiprops->ModuleName);
-	printf("%s\n", cmd);
+	if( gbTraceEnabled ) {
+		printf(">> %s\n", cmd);
+	}
 	int rv = system(cmd);
 	free(cmd);
 	free(udiprops_c);
diff --git a/UDI/Tools/udibuild_src/include/common.h b/UDI/Tools/udibuild_src/include/common.h
index 0cb419b66b7f364824fe93baa36fd55e6d63f923..2f92913e502ce31daedcede2c79169dcd8d20d81 100644
--- a/UDI/Tools/udibuild_src/include/common.h
+++ b/UDI/Tools/udibuild_src/include/common.h
@@ -9,6 +9,7 @@
 #define _COMMON_H_
 
 #include <stdarg.h>
+#include <stdbool.h>
 
 #ifndef __GNUC__
 # define __attribute__(...)
@@ -16,5 +17,7 @@
 
 extern char	*mkstr(const char *fmt, ...) __attribute__((format(printf,1,2)));
 
+extern bool	gbTraceEnabled;
+
 #endif
 
diff --git a/UDI/Tools/udibuild_src/main.c b/UDI/Tools/udibuild_src/main.c
index b5b0042e770b1afc9365c8d6a2f118f317170caf..f933cc1bc5f21eec2b17bbe26bdad15daf3befdf 100644
--- a/UDI/Tools/udibuild_src/main.c
+++ b/UDI/Tools/udibuild_src/main.c
@@ -29,6 +29,7 @@
 void	Usage(const char *progname);
 
 // === GLOBALS ===
+bool	gbTraceEnabled = false;
 const char *gsRuntimeDir = RUNTIME_DIR;
 const char *gsOpt_ConfigFile;
 const char *gsOpt_WorkingDir;
diff --git a/UDI/drivers/gfx_bochs/bochsga_common.h b/UDI/drivers/gfx_bochs/bochsga_common.h
new file mode 100644
index 0000000000000000000000000000000000000000..5d40cfac964b52798089766239d77f1cd0acdfde
--- /dev/null
+++ b/UDI/drivers/gfx_bochs/bochsga_common.h
@@ -0,0 +1,74 @@
+/*
+ * UDI Bochs Graphics Driver
+ * By John Hodge (thePowersGang)
+ *
+ * bochsga_common.c
+ * - Common definitions
+ */
+#ifndef _BOCHSGA_COMMON_H_
+#define _BOCHSGA_COMMON_H_
+
+/**
+ * Definitions to match udiprops.txt
+ * \{
+ */
+#define BOCHSGA_META_BUS	1
+#define BOCHSGA_META_GFX	2
+
+#define BOCHSGA_OPS_DEV	1
+#define BOCHSGA_OPS_GFX	2
+
+#define BOCHSGA_CB_BUS_BIND	1
+#define BOCHSGA_CB_GFX_BIND	2
+#define BOCHSGA_CB_GFX_STATE	3
+#define BOCHSGA_CB_GFX_RANGE	4
+#define BOCHSGA_CB_GFX_COMMAND	5
+
+#define BOCHSGA_MSGNUM_PROPUNK	1001
+#define BOCHSGA_MSGNUM_BUFUNK	1002
+/**
+ * \}
+ */
+
+#include "bochsga_pio.h"
+
+typedef struct {
+	udi_ubit32_t	width;
+	udi_ubit32_t	height;
+	udi_index_t	bitdepth;
+} engine_t;
+
+#define N_ENGINES	1
+
+/**
+ * Region data
+ */
+typedef struct
+{
+	udi_cb_t	*active_cb;
+	struct {
+		udi_index_t	pio_index;
+	} init;
+
+	udi_pio_handle_t	pio_handles[N_PIO];
+	
+	udi_boolean_t	output_enable;
+	struct {
+		udi_ubit32_t	width;
+		udi_ubit32_t	height;
+		udi_ubit8_t	bitdepth;
+		udi_index_t	engine;
+	} outputstate;
+	struct {
+		udi_ubit32_t	max_width;	// 1024 or 1280
+		udi_ubit32_t	max_height;	// 768 or 1024
+	} limits;
+	
+	engine_t	engines[N_ENGINES];
+} rdata_t;
+
+#define BOCHSGA_MIN_WIDTH	360
+#define BOCHSGA_MIN_HEIGHT	240
+
+#endif
+
diff --git a/UDI/drivers/gfx_bochs/bochsga_core.c b/UDI/drivers/gfx_bochs/bochsga_core.c
new file mode 100644
index 0000000000000000000000000000000000000000..458f89339c33a6cd3756484aeeba7e18856190ba
--- /dev/null
+++ b/UDI/drivers/gfx_bochs/bochsga_core.c
@@ -0,0 +1,485 @@
+/*
+ * UDI Bochs Graphics Driver
+ * By John Hodge (thePowersGang)
+ *
+ * bochsga_core.c
+ * - Core Code
+ */
+#define UDI_VERSION	0x101
+#define UDI_PHYSIO_VERSION	0x101
+#define UDI_GFX_VERSION	0x101
+#define UDI_PCI_VERSION	0x101
+#include <udi.h>
+#include <udi_physio.h>
+#include <udi_gfx.h>
+#include <udi_pci.h>
+#define DEBUG_ENABLED	1
+#include "../helpers.h"
+#include "../helpers_gfx.h"
+#include "bochsga_common.h"
+
+/* --- Management Metalang -- */
+void bochsga_usage_ind(udi_usage_cb_t *cb, udi_ubit8_t resource_level)
+{
+	//rdata_t	*rdata = UDI_GCB(cb)->context;
+	/*udi_trace_write(rdata->init_context, UDI_TREVENT_LOCAL_PROC_ENTRY, 0, );*/
+
+	/* TODO: Set up region data */
+
+	udi_usage_res(cb);
+}
+
+void bochsga_enumerate_req(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level)
+{
+	//rdata_t	*rdata = UDI_GCB(cb)->context;
+	udi_instance_attr_list_t *attr_list = cb->attr_list;
+	
+	switch(enumeration_level)
+	{
+	case UDI_ENUMERATE_START:
+	case UDI_ENUMERATE_START_RESCAN:
+		cb->attr_valid_length = attr_list - cb->attr_list;
+		udi_enumerate_ack(cb, UDI_ENUMERATE_OK, BOCHSGA_OPS_GFX);
+		break;
+	case UDI_ENUMERATE_NEXT:
+		udi_enumerate_ack(cb, UDI_ENUMERATE_DONE, 0);
+		break;
+	}
+}
+void bochsga_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID)
+{
+}
+void bochsga_final_cleanup_req(udi_mgmt_cb_t *cb)
+{
+}
+/* --- */
+void bochsga_bus_dev_channel_event_ind(udi_channel_event_cb_t *cb);
+void bochsga_bus_dev_bus_bind_ack(udi_bus_bind_cb_t *cb, udi_dma_constraints_t dma_constraints, udi_ubit8_t perferred_endianness, udi_status_t status);
+void bochsga_bus_dev_bind__pio_map(udi_cb_t *cb, udi_pio_handle_t new_pio_handle);
+void bochsga_bus_dev_bind__intr_chanel(udi_cb_t *gcb, udi_channel_t new_channel);
+void bochsga_bus_dev_bus_unbind_ack(udi_bus_bind_cb_t *cb);
+
+void bochsga_bus_dev_channel_event_ind(udi_channel_event_cb_t *cb)
+{
+	udi_cb_t	*gcb = UDI_GCB(cb);
+	rdata_t	*rdata = gcb->context;
+	
+	switch(cb->event)
+	{
+	case UDI_CHANNEL_CLOSED:
+		break;
+	case UDI_CHANNEL_BOUND: {
+		rdata->active_cb = gcb;
+		udi_bus_bind_cb_t *bus_bind_cb = UDI_MCB(cb->params.parent_bound.bind_cb, udi_bus_bind_cb_t);
+		udi_bus_bind_req( bus_bind_cb );
+		// continue at bochsga_bus_dev_bus_bind_ack
+		return; }
+	}
+}
+void bochsga_bus_dev_bus_bind_ack(udi_bus_bind_cb_t *cb,
+	udi_dma_constraints_t dma_constraints, udi_ubit8_t perferred_endianness, udi_status_t status)
+{
+	udi_cb_t	*gcb = UDI_GCB(cb);
+	rdata_t	*rdata = gcb->context;
+	
+	/* Set up PIO handles */
+	rdata->init.pio_index = -1;
+	bochsga_bus_dev_bind__pio_map(gcb, UDI_NULL_PIO_HANDLE);
+	/* V V V V */
+}
+void bochsga_bus_dev_bind__pio_map(udi_cb_t *gcb, udi_pio_handle_t new_pio_handle)
+{
+	rdata_t	*rdata = gcb->context;
+	if( rdata->init.pio_index != (udi_index_t)-1 )
+	{
+		rdata->pio_handles[rdata->init.pio_index] = new_pio_handle;
+	}
+	
+	rdata->init.pio_index ++;
+	if( rdata->init.pio_index < N_PIO )
+	{
+		const struct s_pio_ops	*ops = &bochsga_pio_ops[rdata->init.pio_index];
+		udi_pio_map(bochsga_bus_dev_bind__pio_map, gcb,
+			ops->regset_idx, ops->base_offset, ops->length,
+			ops->trans_list, ops->list_length,
+			UDI_PIO_LITTLE_ENDIAN, 0, 0
+			);
+		return ;
+	}
+	
+	udi_channel_event_complete( UDI_MCB(rdata->active_cb, udi_channel_event_cb_t), UDI_OK);
+	// = = = = =
+}
+void bochsga_bus_dev_bus_unbind_ack(udi_bus_bind_cb_t *cb)
+{
+}
+void bochsga_bus_dev_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status)
+{
+}
+void bochsga_bus_dev_intr_detach_ack(udi_intr_detach_cb_t *intr_detach_cb)
+{
+}
+
+/* ---  GFX Provider ops -- */
+void bochsga_gfx_channel_event_ind(udi_channel_event_cb_t *cb)
+{
+	/* No operation */
+}
+void bochsga_gfx_bind_req(udi_gfx_bind_cb_t *cb)
+{
+	/* TODO: ACK bind if nothing already bound */
+}
+void bochsga_gfx_unbind_req(udi_gfx_bind_cb_t *cb)
+{
+	/* TODO: Release internal state? */
+}
+void bochsga_gfx_set_connector_req$pio(udi_cb_t *gcb, udi_buf_t *new_buf, udi_status_t status, udi_ubit16_t result)
+{
+	udi_gfx_state_cb_t *cb = UDI_MCB(gcb, udi_gfx_state_cb_t);
+	udi_gfx_set_connector_ack(cb);
+}
+void bochsga_gfx_set_connector_req(udi_gfx_state_cb_t *cb, udi_ubit32_t value)
+{
+	udi_cb_t	*gcb = UDI_GCB(cb);
+	rdata_t	*rdata = gcb->context;
+	
+	switch(cb->attribute)
+	{
+	case UDI_GFX_PROP_ENABLE:
+		if( rdata->output_enable != !!value )
+		{
+			rdata->output_enable = !!value;
+			udi_pio_trans(bochsga_gfx_set_connector_req$pio, gcb,
+				rdata->pio_handles[BOCHSGA_PIO_ENABLE], rdata->output_enable, NULL, &rdata->outputstate);
+			return ;
+		}
+		udi_gfx_set_connector_ack(cb);
+		return;
+	/* Change input engine */
+	case UDI_GFX_PROP_INPUT:
+		udi_gfx_set_connector_ack(cb);
+		return;
+	/* Alter output dimensions */
+	case UDI_GFX_PROP_WIDTH:
+		if( value % 8 != 0 ) {
+			/* Qemu doesn't like resolutions not a multiple of 8 */
+			return ;
+		}
+		if( !(320 <= value && value <= rdata->limits.max_width) ) {
+			return ;
+		} 
+		rdata->outputstate.width = value;
+		udi_pio_trans(bochsga_gfx_set_connector_req$pio, gcb,
+			rdata->pio_handles[BOCHSGA_PIO_ENABLE], rdata->output_enable, NULL, &rdata->outputstate);
+		return;
+	case UDI_GFX_PROP_HEIGHT:
+		if( !(240 <= value && value <= rdata->limits.max_height) ) {
+			return ;
+		} 
+		rdata->outputstate.height = value;
+		udi_pio_trans(bochsga_gfx_set_connector_req$pio, gcb,
+			rdata->pio_handles[BOCHSGA_PIO_ENABLE], rdata->output_enable, NULL, &rdata->outputstate);
+		return;
+	}
+	CONTIN(bochsga_gfx_set_connector_req, udi_log_write,
+		(UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+		(udi_status_t status)
+		);
+	udi_gfx_state_cb_t	*cb = UDI_MCB(cb, udi_gfx_state_cb_t);
+	udi_gfx_set_connector_ack(cb /*, UDI_STAT_NOT_SUPPORTED*/);
+}
+void bochsga_gfx_get_connector_req(udi_gfx_state_cb_t *cb)
+{
+	udi_cb_t	*gcb = UDI_GCB(cb);
+	rdata_t	*rdata = gcb->context;
+	
+	switch(cb->attribute)
+	{
+	case UDI_GFX_PROP_ENABLE:
+		udi_gfx_get_connector_ack(cb, !!rdata->output_enable);
+		return;
+	case UDI_GFX_PROP_INPUT:
+		udi_gfx_get_connector_ack(cb, 0);
+		return;
+	case UDI_GFX_PROP_WIDTH:
+		udi_gfx_get_connector_ack(cb, rdata->outputstate.width);
+		return;
+	case UDI_GFX_PROP_HEIGHT:
+		udi_gfx_get_connector_ack(cb, rdata->outputstate.height);
+		return;
+	case UDI_GFX_PROP_CONNECTOR_TYPE:
+		udi_gfx_get_connector_ack(cb, UDI_GFX_CONNECTOR_HIDDEN);
+		return;
+	case UDI_GFX_PROP_SIGNAL:
+		udi_gfx_get_connector_ack(cb, UDI_GFX_SIGNAL_INTEGRATED);
+		return;
+	}
+	CONTIN(bochsga_gfx_get_connector_req, udi_log_write,
+		(UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+		(udi_status_t status)
+		);
+	udi_gfx_state_cb_t	*cb = UDI_MCB(cb, udi_gfx_state_cb_t);
+	udi_gfx_get_connector_ack(cb, 0);
+}
+void bochsga_gfx_range_connector_req(udi_gfx_range_cb_t *cb)
+{
+	udi_cb_t	*gcb = UDI_GCB(cb);
+	rdata_t	*rdata = gcb->context;
+	
+	switch(cb->attribute)
+	{
+	case UDI_GFX_PROP_ENABLE:
+		/* 2 values: 0 and 1 */
+		gfxhelpers_return_range_set(udi_gfx_range_connector_ack, cb, 2, 0, 1);
+		return;
+	case UDI_GFX_PROP_INPUT:
+		/* Fix 0 */
+		gfxhelpers_return_range_fixed(udi_gfx_range_connector_ack, cb, 0);
+		return;
+	case UDI_GFX_PROP_WIDTH:
+		/* qemu restricts to 8 step */
+		gfxhelpers_return_range_simple(udi_gfx_range_connector_ack, cb,
+			BOCHSGA_MIN_WIDTH, rdata->limits.max_width, 8);
+		return;
+	case UDI_GFX_PROP_HEIGHT:
+		/* step of 8 for neatness */
+		gfxhelpers_return_range_simple(udi_gfx_range_connector_ack, cb,
+			BOCHSGA_MIN_HEIGHT, rdata->limits.max_height, 8);
+		return;
+	case UDI_GFX_PROP_CONNECTOR_TYPE:
+		gfxhelpers_return_range_fixed(udi_gfx_range_connector_ack, cb, UDI_GFX_CONNECTOR_HIDDEN);
+		return;
+	case UDI_GFX_PROP_SIGNAL:
+		gfxhelpers_return_range_fixed(udi_gfx_range_connector_ack, cb, UDI_GFX_SIGNAL_INTEGRATED);
+		return;
+	}
+	CONTIN(bochsga_gfx_range_connector_req, udi_log_write,
+		(UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+		(udi_status_t status)
+		);
+	udi_gfx_range_cb_t	*cb = UDI_MCB(cb, udi_gfx_range_cb_t);
+	udi_gfx_range_connector_ack(cb);
+}
+
+/* --- Engine Manipulation --- */
+void bochsga_gfx_set_engine_req(udi_gfx_state_cb_t *cb, udi_ubit32_t value)
+{
+	udi_cb_t	*gcb = UDI_GCB(cb);
+	rdata_t	*rdata = gcb->context;
+	
+	if( cb->subsystem >= N_ENGINES ) {
+		udi_gfx_get_engine_ack(cb, 0);
+		return;
+	}
+	
+	engine_t *engine = &rdata->engines[cb->subsystem];
+	
+	switch(cb->attribute)
+	{
+	case UDI_GFX_PROP_WIDTH:
+		engine->width = value;
+		udi_gfx_set_engine_ack(cb);
+		return;
+	case UDI_GFX_PROP_HEIGHT:
+		engine->height = value;
+		udi_gfx_set_engine_ack(cb);
+		return;
+	}
+	CONTIN(bochsga_gfx_set_engine_req, udi_log_write,
+		(UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+		(udi_status_t status)
+		);
+	udi_gfx_state_cb_t	*cb = UDI_MCB(cb, udi_gfx_state_cb_t);
+	udi_gfx_set_engine_ack(cb);
+}
+void bochsga_gfx_get_engine_req(udi_gfx_state_cb_t *cb)
+{
+	udi_cb_t	*gcb = UDI_GCB(cb);
+	rdata_t	*rdata = gcb->context;
+	
+	if( cb->subsystem >= N_ENGINES ) {
+		udi_gfx_get_engine_ack(cb, 0);
+		return;
+	}
+	
+	const engine_t *engine = &rdata->engines[cb->subsystem];
+	
+	switch(cb->attribute)
+	{
+	case UDI_GFX_PROP_ENABLE:
+		udi_gfx_get_engine_ack(cb, 1);
+		return;
+	
+	case UDI_GFX_PROP_INPUT:
+		udi_gfx_get_engine_ack(cb, -1);
+		return;
+	
+	case UDI_GFX_PROP_WIDTH:
+		udi_gfx_get_engine_ack(cb, engine->width);
+		return;
+	case UDI_GFX_PROP_HEIGHT:
+		udi_gfx_get_engine_ack(cb, engine->height);
+		return;
+	
+	case UDI_GFX_PROP_STOCK_FORMAT:
+		udi_gfx_get_engine_ack(cb, UDI_GFX_STOCK_FORMAT_R8G8B8);
+		return;
+	}
+	CONTIN(bochsga_gfx_get_engine_req, udi_log_write,
+		(UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+		(udi_status_t status)
+		);
+	udi_gfx_state_cb_t	*cb = UDI_MCB(cb, udi_gfx_state_cb_t);
+	udi_gfx_get_engine_ack(cb, 0);
+}
+void bochsga_gfx_range_engine_req(udi_gfx_range_cb_t *cb)
+{
+	udi_cb_t	*gcb = UDI_GCB(cb);
+	rdata_t	*rdata = gcb->context;
+	(void)rdata;
+	
+	if( cb->subsystem >= N_ENGINES ) {
+		udi_gfx_range_engine_ack(cb);
+		return;
+	}
+	
+	//engine_t *engine = &rdata->engines[cb->subsystem];
+	
+	switch(cb->attribute)
+	{
+	case UDI_GFX_PROP_ENABLE:
+		gfxhelpers_return_range_fixed(udi_gfx_range_engine_ack, cb, 1);
+		return;
+	case UDI_GFX_PROP_INPUT:
+		gfxhelpers_return_range_fixed(udi_gfx_range_engine_ack, cb, -1);
+		return;
+	
+	case UDI_GFX_PROP_STOCK_FORMAT:
+		gfxhelpers_return_range_fixed(udi_gfx_range_engine_ack, cb, UDI_GFX_STOCK_FORMAT_B8G8R8);
+		return;
+	}
+	CONTIN(bochsga_gfx_range_engine_req, udi_log_write,
+		(UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_PROPUNK, __func__, cb->attribute),
+		(udi_status_t status)
+		);
+	udi_gfx_range_cb_t *cb = UDI_MCB(cb, udi_gfx_range_cb_t);
+	udi_gfx_range_engine_ack( cb );
+}
+void bochsga_gfx_get_engine_operator_req(udi_gfx_range_cb_t *cb)
+{
+	/* TODO: Get Engine operator */
+	udi_gfx_get_engine_operator_ack(cb, 0, 0,0,0);
+}
+void bochsga_gfx_connector_command_req(udi_gfx_command_cb_t *cb)
+{
+	/* Need to parse the GLX stream */
+	udi_gfx_connector_command_ack(cb);
+}
+void bochsga_gfx_engine_command_req(udi_gfx_command_cb_t *cb)
+{
+	/* Need to parse the GLX stream */
+	udi_gfx_engine_command_ack(cb);
+}
+void bochsga_gfx_buffer_info_req(udi_gfx_buffer_info_cb_t *cb)
+{
+	udi_cb_t *gcb = UDI_GCB(cb);
+	switch(cb->buffer_index)
+	{
+	case 0:
+		udi_gfx_buffer_info_ack(cb, 1024, 768, 24, 0);
+		return;
+	default:
+		break;
+	}
+	CONTIN(bochsga_gfx_buffer_info_req, udi_log_write,
+		(UDI_TREVENT_LOG, UDI_LOG_INFORMATION, BOCHSGA_OPS_GFX, 0, BOCHSGA_MSGNUM_BUFUNK, __func__, cb->buffer_index),
+		(udi_status_t status)
+		);
+	udi_gfx_buffer_info_cb_t *cb = UDI_MCB(gcb, udi_gfx_buffer_info_cb_t);
+	udi_gfx_buffer_info_ack(cb, 0,0,0,0);
+}
+void bochsga_gfx_buffer_write_req(udi_gfx_buffer_cb_t *cb)
+{
+}
+void bochsga_gfx_buffer_read_req(udi_gfx_buffer_cb_t *cb)
+{
+}
+
+/*
+====================================================================
+Management ops
+====================================================================
+*/
+udi_mgmt_ops_t	bochsga_mgmt_ops = {
+	bochsga_usage_ind,
+	bochsga_enumerate_req,
+	bochsga_devmgmt_req,
+	bochsga_final_cleanup_req
+};
+udi_ubit8_t	bochsga_mgmt_op_flags[4] = {0,0,0,0};
+/* - Bus Ops */
+udi_bus_device_ops_t	bochsga_bus_dev_ops = {
+	bochsga_bus_dev_channel_event_ind,
+	bochsga_bus_dev_bus_bind_ack,
+	bochsga_bus_dev_bus_unbind_ack,
+	bochsga_bus_dev_intr_attach_ack,
+	bochsga_bus_dev_intr_detach_ack
+};
+udi_ubit8_t	bochsga_bus_dev_ops_flags[5] = {0};
+/* - GFX provider ops */
+udi_gfx_provider_ops_t	bochsga_gfx_ops = {
+	bochsga_gfx_channel_event_ind,
+	bochsga_gfx_bind_req,
+	bochsga_gfx_unbind_req,
+	bochsga_gfx_set_connector_req,
+	bochsga_gfx_set_engine_req,
+	bochsga_gfx_get_connector_req,
+	bochsga_gfx_get_engine_req,
+	bochsga_gfx_range_connector_req,
+	bochsga_gfx_range_engine_req,
+	bochsga_gfx_get_engine_operator_req,
+	bochsga_gfx_connector_command_req,
+	bochsga_gfx_engine_command_req,
+	bochsga_gfx_buffer_info_req,
+	bochsga_gfx_buffer_read_req,
+	bochsga_gfx_buffer_write_req,
+};
+udi_ubit8_t	bochsga_gfx_ops_flags[10] = {0};
+/* -- */
+udi_primary_init_t	bochsga_pri_init = {
+	.mgmt_ops = &bochsga_mgmt_ops,
+	.mgmt_op_flags = bochsga_mgmt_op_flags,
+	.mgmt_scratch_requirement = 0,
+	.enumeration_attr_list_length = 0,
+	.rdata_size = sizeof(rdata_t),
+	.child_data_size = 0,
+	.per_parent_paths = 0
+};
+udi_ops_init_t	bochsga_ops_list[] = {
+	{
+		BOCHSGA_OPS_DEV, BOCHSGA_META_BUS, UDI_BUS_DEVICE_OPS_NUM,
+		0,
+		(udi_ops_vector_t*)&bochsga_bus_dev_ops,
+		bochsga_bus_dev_ops_flags
+	},
+	{
+		BOCHSGA_OPS_GFX, BOCHSGA_META_GFX, UDI_GFX_PROVIDER_OPS_NUM,
+		0,
+		(udi_ops_vector_t*)&bochsga_gfx_ops,
+		bochsga_gfx_ops_flags
+	},
+	{0}
+};
+udi_cb_init_t bochsga_cb_init_list[] = {
+	{BOCHSGA_CB_BUS_BIND,    BOCHSGA_META_BUS, UDI_BUS_BIND_CB_NUM, 0, 0,NULL},
+	{BOCHSGA_CB_GFX_BIND,    BOCHSGA_META_GFX, UDI_GFX_BIND_CB_NUM, 0, 0,NULL},
+	{BOCHSGA_CB_GFX_STATE,   BOCHSGA_META_GFX, UDI_GFX_STATE_CB_NUM, 0, 0,NULL},
+	{BOCHSGA_CB_GFX_RANGE,   BOCHSGA_META_GFX, UDI_GFX_RANGE_CB_NUM, 0, 0,NULL},
+	{BOCHSGA_CB_GFX_COMMAND, BOCHSGA_META_GFX, UDI_GFX_COMMAND_CB_NUM, 0, 0,NULL},
+	{0}
+};
+const udi_init_t	udi_init_info = {
+	.primary_init_info = &bochsga_pri_init,
+	.ops_init_list = bochsga_ops_list,
+	.cb_init_list = bochsga_cb_init_list,
+};
diff --git a/UDI/drivers/gfx_bochs/bochsga_engines.h b/UDI/drivers/gfx_bochs/bochsga_engines.h
new file mode 100644
index 0000000000000000000000000000000000000000..272f0be76c67d464dcac855dd9634d21c862a3ad
--- /dev/null
+++ b/UDI/drivers/gfx_bochs/bochsga_engines.h
@@ -0,0 +1,34 @@
+/*
+ * 
+ */
+
+#define BOCHSGA_ENGINE_PROP_BUFFER	(UDI_GFX_PROP_CUSTOM+0)
+
+/* === CONSTANTS === */
+const gfxhelpers_op_t	bochsga_engine_ops_8bpp[] = {
+};
+const gfxhelpers_op_t	bochsga_engine_ops_32bpp[] = {
+	{UDI_GFX_OPERATOR_RGB,    1,  2,  3},	/* #0 Output RGB from ops #1,#2,#3 */
+	{UDI_GFX_OPERATOR_SEG,    4, 16,  8},	/* #1 Extract 8 bits from bit 16 of #4 */
+	{UDI_GFX_OPERATOR_SEG,    4,  8,  8},	/* #2 8 bits from ofs 8 of #4 */
+	{UDI_GFX_OPERATOR_SEG,    4,  0,  8},	/* #3 8 bits from ofs 0 of #4 */
+	{UDI_GFX_OPERATOR_BUFFER, 5,  6, 32},	/* #4 32 bits from buffer #5 ofs #6 */
+	{UDI_GFX_OPERATOR_ATTR,   0, BOCHSGA_ENGINE_PROP_BUFFER, 0},	/* #5 Buffer index */
+	{UDI_GFX_OPERATOR_MAD,    7,  8,  9},	/* #6 Get offset (#8 * #7 + #9) */
+	{UDI_GFX_OPERATOR_ATTR,   0, UDI_GFX_PROP_SOURCE_WIDTH, 0},	/* #7 Read buffer width */
+	{UDI_GFX_OPERATOR_Y,      0,  0,  0},	/* #8 Y coordinate */
+	{UDI_GFX_OPERATOR_X,      0,  0,  0}	/* #9 X coordinate */
+};
+
+typedef struct {
+	udi_ubit8_t	bitdepth;
+	gfxhelpers_op_map_t	op_map;
+} engine_static_t;
+
+const engine_static_t	bochsga_engine_defs[] = {
+	{.bitdepth = 8, .op_map = {ARRAY_COUNT(bochsga_engine_ops_8bpp), bochsga_engine_ops_8bpp}},
+	{.bitdepth = 16},
+	{.bitdepth = 24},
+	{.bitdepth = 32, .op_map = {ARRAY_COUNT(bochsga_engine_ops_8bpp), bochsga_engine_ops_32bpp}},
+};
+#define N_ENGINES	ARRAY_COUNT(bochsga_engine_defs)
diff --git a/UDI/drivers/gfx_bochs/bochsga_pio.h b/UDI/drivers/gfx_bochs/bochsga_pio.h
new file mode 100644
index 0000000000000000000000000000000000000000..f5ddf2656d4cb4bc2aedd14a0c88e077a5c4eed5
--- /dev/null
+++ b/UDI/drivers/gfx_bochs/bochsga_pio.h
@@ -0,0 +1,22 @@
+/*
+ * TODO
+ */
+#ifndef _BOCHSGA_PIO_H_
+#define _BOCHSGA_PIO_H_
+
+udi_pio_trans_t	bochsga_pio_enable[] = {
+	{UDI_PIO_END_IMM, UDI_PIO_2BYTE, 0},
+	};
+
+enum {
+	BOCHSGA_PIO_ENABLE,
+};
+
+const struct s_pio_ops	bochsga_pio_ops[] = {
+	[BOCHSGA_PIO_ENABLE] = UDIH_PIO_OPS_ENTRY(bochsga_pio_enable, 0, UDI_PCI_BAR_2, 0x400, 0xB*2),
+//	UDIH_PIO_OPS_ENTRY(bochsga_pio_enable, 0, UDI_PCI_BAR_2, 0x400, 0xB*2),
+	};
+#define N_PIO	(sizeof(bochsga_pio_ops)/sizeof(struct s_pio_ops))
+
+#endif
+
diff --git a/UDI/drivers/gfx_bochs/udiprops.txt b/UDI/drivers/gfx_bochs/udiprops.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8b43d5c1852bddd138a940e3d1aab8b0bbb54c33
--- /dev/null
+++ b/UDI/drivers/gfx_bochs/udiprops.txt
@@ -0,0 +1,43 @@
+properties_version 0x101
+supplier 1
+contact 2
+name 3
+shortname bochsga
+release 5 1.0
+
+requires udi 0x101
+requires udi_physio 0x101
+requires udi_bridge 0x101
+requires udi_gfx 0x101
+
+meta 1 udi_bridge
+meta 2 udi_gfx
+#meta 3 udi_gio
+
+parent_bind_ops 1 0 1 1	# bridge, rgn 0, ops 1, cb 1
+child_bind_ops 2 0 2	# Provider
+#parent_bind_ops 3 0 2 3	# GIO bound to 3D provider
+
+#enumerates 102 1 1 3  gio_type string software3d
+
+# - Classic non-PCI version
+device 100 1  bus string system  sysbus_io_addr_lo ubit32 0x01CE  sysbus_io_size ubit32 2  sysbys_mem_addr_lo ubit32 0xE0000000 sysbus_mem_size 0x400000
+# - PCI Version (Non-indexed registers at offset 0x500 in BAR2 MMIO)
+device 101 1  bus string pci  pci_vendor_id ubit32 0x1234  pci_device_id ubit32 0x1111  pci_base_class ubit32 0x03  pci_sub_clais ubit32 0x00  pci_prog_if ubit32 0x00
+
+# Messages
+message 1	John Hodge (thePowersGang)
+message 2	udi@mutabah.net
+message 3	Bochs Graphics Adapter
+message	5	BochsGA
+message 100	BochsGA ISA Device
+message 101	BochsGA PCI Device
+
+message 1001	"Unknown property passed to %s: %i"
+
+module bochsga
+region 0
+
+# Source-only udiprops
+compile_options -Wall
+source_files bochsga_core.c
diff --git a/UDI/drivers/helpers.h b/UDI/drivers/helpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..dde98164c6cff4952c611d11e28ce44affbe8155
--- /dev/null
+++ b/UDI/drivers/helpers.h
@@ -0,0 +1,87 @@
+/*
+ * UDI Driver helper macros
+ */
+#ifndef _UDI_HELPERS_H_
+#define _UDI_HELPERS_H_
+
+#if DEBUG_ENABLED
+# define DEBUG_OUT(fmt, v...)	udi_debug_printf("%s: "fmt"\n", __func__ ,## v) 
+#else
+# define DEBUG_OUT(...)	do{}while(0)
+#endif
+
+#define ARRAY_COUNT(arr)	(sizeof(arr)/sizeof(arr[0]))
+
+#define __EXPJOIN(a,b)	a##b
+#define _EXPJOIN(a,b)	__EXPJOIN(a,b)
+#define _EXPLODE(params...)	params
+#define _ADDGCB(params...)	(udi_cb_t *gcb, params)
+#define CONTIN(suffix, call, args, params)	extern void _EXPJOIN(suffix##$,__LINE__) _ADDGCB params;\
+	call( _EXPJOIN(suffix##$,__LINE__), gcb, _EXPLODE args); } \
+	void _EXPJOIN(suffix##$,__LINE__) _ADDGCB params { \
+	rdata_t	*rdata = gcb->context; \
+	(void)rdata;
+
+/* Copied from http://projectudi.cvs.sourceforge.net/viewvc/projectudi/udiref/driver/udi_dpt/udi_dpt.h */
+#define UDIH_SET_ATTR_BOOLEAN(attr, name, val)	\
+		udi_strcpy((attr)->attr_name, (name)); \
+		(attr)->attr_type = UDI_ATTR_BOOLEAN; \
+		(attr)->attr_length = sizeof(udi_boolean_t); \
+		UDI_ATTR32_SET((attr)->attr_value, (val))
+
+#define UDIH_SET_ATTR32(attr, name, val)	\
+		udi_strcpy((attr)->attr_name, (name)); \
+		(attr)->attr_type = UDI_ATTR_UBIT32; \
+		(attr)->attr_length = sizeof(udi_ubit32_t); \
+		UDI_ATTR32_SET((attr)->attr_value, (val))
+
+#define UDIH_SET_ATTR_ARRAY8(attr, name, val, len) \
+		udi_strcpy((attr)->attr_name, (name)); \
+		(attr)->attr_type = UDI_ATTR_ARRAY8; \
+		(attr)->attr_length = (len); \
+		udi_memcpy((attr)->attr_value, (val), (len))
+
+#define UDIH_SET_ATTR_STRING(attr, name, val, len) \
+		udi_strcpy((attr)->attr_name, (name)); \
+		(attr)->attr_type = UDI_ATTR_STRING; \
+		(attr)->attr_length = (len); \
+		udi_strncpy_rtrim((char *)(attr)->attr_value, (val), (len))
+#define UDIH_SET_ATTR_STRFMT(attr, name, maxlen, fmt, v...) \
+		udi_strcpy((attr)->attr_name, (name)); \
+		(attr)->attr_type = UDI_ATTR_STRING; \
+		(attr)->attr_length = udi_snprintf((char *)(attr)->attr_value, (maxlen), (fmt) ,## v )
+
+/**
+ * \brief UDI PIO Helpers
+ */
+struct s_pio_ops {
+	udi_pio_trans_t	*trans_list;
+	udi_ubit16_t	list_length;
+	udi_ubit16_t	pio_attributes;
+	udi_ubit32_t	regset_idx;
+	udi_ubit32_t	base_offset;
+	udi_ubit32_t	length;
+};
+#define UDIH_PIO_OPS_ENTRY(list, attr, regset, base, len)	{list, ARRAY_COUNT(list), attr, regset, base, len}
+
+
+#define UDIH_INIT_PIO(fcnname, _array, index_field, final_code) \
+	void fcnname(udi_cb_t *gcb, udi_pio_handle_t new_pio_handle) {\
+		rdata_t	*rdata = gcb->context; \
+		if( rdata->index_field != (udi_index_t)-1 ) { \
+			rdata->pio_handles[rdata->index_field] = new_pio_handle; \
+		}\
+		rdata->index_field ++; \
+		if( rdata->index_field < sizeof(_array)/sizeof(_array[0]) ) { \
+			const struct s_pio_ops	*ops = &pio_ops[rdata->index_field]; \
+			udi_pio_map(bus_dev_bind__pio_map, gcb, \
+				ops->regset_idx, ops->base_offset, ops->length, \
+				ops->trans_list, ops->list_length, \
+				UDI_PIO_LITTLE_ENDIAN, 0, 0 \
+				); \
+			return ; \
+		} \
+		final_code \
+	}
+
+#endif
diff --git a/UDI/drivers/helpers_gfx.h b/UDI/drivers/helpers_gfx.h
new file mode 100644
index 0000000000000000000000000000000000000000..999a4082ba674e0e5e3ea056ac48f7ed8d29cabb
--- /dev/null
+++ b/UDI/drivers/helpers_gfx.h
@@ -0,0 +1,50 @@
+/*
+ * UDI Driver Helper Macros
+ *
+ * GFX-specific helpers
+ */
+#ifndef _HELPERS_GFX_H_
+#define _HELPERS_GFX_H_
+
+#if __STDC_VERSION__ < 199901L
+# define	inline
+#endif
+
+typedef struct {
+	udi_index_t	op;
+	udi_ubit32_t	arg_1;
+	udi_ubit32_t	arg_2;
+	udi_ubit32_t	arg_3;
+} gfxhelpers_op_t;
+
+typedef struct {
+	udi_index_t	op_count;
+	const gfxhelpers_op_t	*ops;
+} gfxhelpers_op_map_t;
+
+static inline void gfxhelpers_return_range_simple(
+	udi_gfx_range_connector_ack_op_t *callback, udi_gfx_range_cb_t *cb,
+	udi_ubit32_t min, udi_ubit32_t max, udi_ubit32_t step
+	)
+{
+	
+}
+
+static inline void gfxhelpers_return_range_set(
+	udi_gfx_range_connector_ack_op_t *callback, udi_gfx_range_cb_t *cb,
+	udi_ubit32_t count, ...
+	)
+{
+	
+}
+
+static inline void gfxhelpers_return_range_fixed(
+	udi_gfx_range_connector_ack_op_t *callback, udi_gfx_range_cb_t *cb,
+	udi_ubit32_t value
+	)
+{
+	gfxhelpers_return_range_simple(callback, cb, value, value, 1);
+}
+
+#endif
+
diff --git a/UDI/drivers/net_ne2000/ne2000_common.h b/UDI/drivers/net_ne2000/ne2000_common.h
index b6a1689664711179db8219dc23211a1ddccf1a1d..d4e58aa34fb5326d7d04cf0c3d72f67c6a54a73f 100644
--- a/UDI/drivers/net_ne2000/ne2000_common.h
+++ b/UDI/drivers/net_ne2000/ne2000_common.h
@@ -8,8 +8,14 @@
 #ifndef _NE2000_COMMON_H_
 #define _NE2000_COMMON_H_
 
+#define UDI_VERSION	0x101
+#define UDI_PHYSIO_VERSION	0x101
+#define UDI_PCI_VERSION 0x101
+#define UDI_NIC_VERSION	0x101
+
 #include <udi.h>
 #include <udi_physio.h>
+#include <udi_pci.h>
 #include <udi_nic.h>
 
 #include "ne2000_hw.h"
@@ -77,10 +83,15 @@ typedef struct
 		(attr)->attr_type = UDI_ATTR_STRING; \
 		(attr)->attr_length = (len); \
 		udi_strncpy_rtrim((char *)(attr)->attr_value, (val), (len))
-#define NE2K_SET_ATTR_STRFMT(attr, name, maxlen, fmt, v...) \
+#define NE2K_SET_ATTR_STRFMT(attr, name, maxlen, fmt, ...) \
 		udi_strcpy((attr)->attr_name, (name)); \
 		(attr)->attr_type = UDI_ATTR_STRING; \
-		(attr)->attr_length = udi_snprintf((char *)(attr)->attr_value, (maxlen), (fmt) ,## v )
+		(attr)->attr_length = udi_snprintf((char *)(attr)->attr_value, (maxlen), (fmt) ,## __VA_ARGS__ )
+
+extern udi_usage_ind_op_t	ne2k_usage_ind;
+extern udi_enumerate_req_op_t	ne2k_enumerate_req;
+extern udi_devmgmt_req_op_t	ne2k_devmgmt_req;
+extern udi_final_cleanup_req_op_t	ne2k_final_cleanup_req;
 
 extern udi_channel_event_ind_op_t	ne2k_bus_dev_channel_event_ind;
 extern udi_bus_bind_ack_op_t	ne2k_bus_dev_bus_bind_ack;
diff --git a/UDI/drivers/net_ne2000/ne2000_core.c b/UDI/drivers/net_ne2000/ne2000_core.c
index 2e44db1879c6147214322acb924ebd03e1fcbb4f..d630a8d4abba32239321b2db36416dbbce5daf1b 100644
--- a/UDI/drivers/net_ne2000/ne2000_core.c
+++ b/UDI/drivers/net_ne2000/ne2000_core.c
@@ -5,8 +5,6 @@
  * ne2000_core.c
  * - UDI initialisation 
  */
-#include <udi.h>
-#include <udi_nic.h>
 #include "ne2000_common.h"
 
 enum {
@@ -66,6 +64,9 @@ void ne2k_enumerate_req(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level)
 	case UDI_ENUMERATE_NEXT:
 		udi_enumerate_ack(cb, UDI_ENUMERATE_DONE, 0);
 		break;
+	default:
+		udi_assert(!"invalid enumeration_level");
+		break;
 	}
 }
 void ne2k_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID)
@@ -90,6 +91,9 @@ void ne2k_bus_dev_channel_event_ind(udi_channel_event_cb_t *cb)
 		udi_bus_bind_req( bus_bind_cb );
 		// continue at ne2k_bus_dev_bus_bind_ack
 		return; }
+	default:
+		udi_assert(!"invalid channel event");
+		break;
 	}
 }
 void ne2k_bus_dev_bus_bind_ack(udi_bus_bind_cb_t *cb,
@@ -240,7 +244,7 @@ void ne2k_nd_ctrl_bind__rx_chan_ok(udi_cb_t *gcb, udi_channel_t new_channel)
 	cb->max_perfect_multicast = 0;
 	cb->max_total_multicast = 0;
 	cb->mac_addr_len = 6;
-	memcpy(cb->mac_addr, rdata->macaddr, 6);
+	udi_memcpy(cb->mac_addr, rdata->macaddr, 6);
 	udi_nsr_bind_ack( cb, UDI_OK );
 	// = = = =
 }
diff --git a/UDI/drivers/net_ne2000/ne2000_rx.c b/UDI/drivers/net_ne2000/ne2000_rx.c
index dda35770e2856b639a1eb70f952e61298958eb52..9b4b3c32370195dd8c62dd15049beb268517c408 100644
--- a/UDI/drivers/net_ne2000/ne2000_rx.c
+++ b/UDI/drivers/net_ne2000/ne2000_rx.c
@@ -5,6 +5,8 @@
  * ne2000_rx.c
  * - Receive Code
  */
+#define UDI_VERSION	0x101
+#define UDI_NIC_VERSION	0x101
 #include <udi.h>
 #include <udi_nic.h>
 #include "ne2000_common.h"
@@ -45,7 +47,7 @@ void ne2k_intr__rx_ok(udi_cb_t *gcb)
 		rdata->rx_next_cb = rx_cb->chain;
 		rx_cb->chain = NULL;
 		udi_debug_printf("ne2k_intr__rx_ok: Initialising buffer\n");
-		udi_buf_write(ne2k_rx__buf_allocated, UDI_GCB(rx_cb), NULL, 1520, rx_cb->rx_buf, 0, 0, NULL);
+		udi_buf_write(ne2k_rx__buf_allocated, UDI_GCB(rx_cb), NULL, 1520, rx_cb->rx_buf, 0, 0, UDI_NULL_BUF_PATH);
 	}
 	else
 	{
diff --git a/UDI/drivers/net_ne2000/ne2000_tx.c b/UDI/drivers/net_ne2000/ne2000_tx.c
index b943ae8827d111f7e6e526cc64c9952aaf3ac752..0e54fad2f089fdf66adddb9aa99cdbc518b554e7 100644
--- a/UDI/drivers/net_ne2000/ne2000_tx.c
+++ b/UDI/drivers/net_ne2000/ne2000_tx.c
@@ -5,6 +5,8 @@
  * ne2000_tx.c
  * - Transmit Code
  */
+#define UDI_VERSION	0x101
+#define UDI_NIC_VERSION	0x101
 #include <udi.h>
 #include <udi_nic.h>
 #include "ne2000_common.h"
diff --git a/UDI/drivers/uart_16c550/uart16c550.c b/UDI/drivers/uart_16c550/uart16c550.c
index cfe77707be24bda6c93a43646e41133b2f2e060e..510bc47202e8b3a149a0e9d199d956fe45e407c4 100644
--- a/UDI/drivers/uart_16c550/uart16c550.c
+++ b/UDI/drivers/uart_16c550/uart16c550.c
@@ -5,8 +5,12 @@
  * uart16c550.c
  * - UDI initialisation 
  */
+#define UDI_VERSION	0x101
+#define UDI_PCI_VERSION	0x101
+#define UDI_PHYSIO_VERSION	0x101
 #include <udi.h>
 #include <udi_physio.h>
+#include <udi_pci.h>
 #include "uart16c550_common.h"
 
 #include "uart16c550_pio.h"
@@ -100,7 +104,7 @@ void uart_bus_dev_bus_bind_ack(udi_bus_bind_cb_t *cb,
 void uart_bus_dev_bind__pio_map(udi_cb_t *gcb, udi_pio_handle_t new_pio_handle)
 {
 	rdata_t	*rdata = gcb->context;
-	if( rdata->init.pio_index != -1 )
+	if( rdata->init.pio_index != (udi_index_t)-1 )
 	{
 		rdata->pio_handles[rdata->init.pio_index] = new_pio_handle;
 	}
diff --git a/UDI/gfx_spec_issues.txt b/UDI/gfx_spec_issues.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e97aea4ffd23cab8b991e898739aeead1482b9cf
--- /dev/null
+++ b/UDI/gfx_spec_issues.txt
@@ -0,0 +1,31 @@
+
+
+- Indexed engine operator list (would be better as second set of calls)
+ > Arguments can be passed using custom attributes
+- GL-centric operations (would be better with read_buffer/write_buffer calls)
+- No status returned from operations most
+- Input data format specifcation
+ > Engine operator list specifies it, but in an abstract way (which would require quite smart code to determine)
+ > Doable though, but having a RO property on the engine that notes if it uses a standard pixel format would be nice
+
+
+Engine inspection API:
+> Readonly? Property for number of operations
+ - Readonly because modifcation of the count is not possible
+ - Attributes can be read, so that's your config mechanisim
+> udi_gfx_engine_getop_req(udi_gfx_state_cb_t *cb);
+> udi_gfx_engine_getop_ack(udi_gfx_state_cb_t *cb, udi_ubit8_t operation, udi_ubit32_t arg1, udi_ubit32_t arg2, udi_ubit32_t arg3);
+
+
+Buffer manipulation API:
+
+  struct {
+      udi_cb_t	gcb;
+      udi_index_t buffer_idx;
+      udi_ubit32_t offset;
+      udi_buffer_t *data;
+  } udi_gfx_buffer_cb_t;
+
+> udi_gfx_buffer_write_req(udi_gfx_buffer_cb_t *cb);
+> udi_gfx_buffer_write_ack(udi_gfx_buffer_cb_t *cb, udi_status_t status);
+> (OPTIONAL) udi_gfx_buffer_read_req(udi_gfx_buffer_cb_t *cb);
diff --git a/UDI/include/udi.h b/UDI/include/udi.h
index 50c7077a4957b186af0962dad37aca4c425fee87..76dac7baac84e95cda42f0b735f4985c56b99e0b 100644
--- a/UDI/include/udi.h
+++ b/UDI/include/udi.h
@@ -4,6 +4,16 @@
 #ifndef _UDI_H_
 #define _UDI_H_
 
+#ifndef UDI_VERSION
+# error "Please define UDI_VERSION before including"
+#endif
+#if UDI_VERSION < 0x100
+# error "Requesting an unsupported UDI version (pre 1.0)"
+#endif
+#if UDI_VERSION > 0x101
+# error "Requesting an unsupported UDI version (post 1.01)"
+#endif
+
 #include <stdint.h>
 #include <stdarg.h>
 
diff --git a/UDI/include/udi_gfx.h b/UDI/include/udi_gfx.h
new file mode 100644
index 0000000000000000000000000000000000000000..cd7db759f2002fb130d51860f775571057430bde
--- /dev/null
+++ b/UDI/include/udi_gfx.h
@@ -0,0 +1,850 @@
+/**
+ * Summary: udi_gfx.h
+ * Contains the graphics metalanguage interface details
+ *
+ * Author:
+ *     Marcel Sondaar
+ *
+ * License:
+ *     <Public Domain>
+ *
+ * Origin:
+ *     http:/*www.d-rift.nl/combuster/mos3/?p=viewsource&file=/include/common/udi_gfx.h*/
+ */
+
+/* note that the specification, and thus, the contents of this file is not fixed.*/
+
+#ifndef __UDI_GFX_H__
+#define __UDI_GFX_H__
+
+#include <udi.h>
+
+#ifndef UDI_GFX_VERSION
+#error "UDI_GFX_VERSION not defined."
+#elif UDI_GFX_VERSION != 0x101
+#error "UDI_GFX_VERSION not supported."
+#endif
+
+/**
+ * Enumeration: UDI_GFX_PROP
+ * Lists the various UDI properties
+ */
+
+/* General state properties*/
+/* Constant: UDI_GFX_PROP_ENABLE
+ *
+ * Valid values:
+ *     0 - disabled
+ *     1 - enabled
+ *     2 - reset
+ *
+ * Ranges:
+ *     Must include at least 1
+ *
+ * The primary state of the connector or engine. An enabled state indicates
+ * it is functioning and generating live output. A disabled state is one where
+ * it is not contributing to any output but is otherwise functional. Finally
+ * the reset state is where the driver is free to deallocate all resources 
+ * corresponding to this component and trash any state not referenced by other
+ * components.
+ *
+ * A disabled or reset engine forwards all data from the previous stage 
+ * unmodified. The disabled state indicates that the component might be 
+ * returned to its enabled state within short notice.
+ *
+ * A disabled connector will not send pixel data, but can perform other 
+ * initialisation communication such as DDC. A reset connector will not respond
+ * in any fashion and can not be used for other purposes. Hardware is expected
+ * to be powered down in such state.
+ *
+ * Users should expect significant delays when moving components in and out of
+ * the reset state. Moving engines between the enabled and disabled state should
+ * take effect within one frame, such transition should take effect on a frame 
+ * boundary when supported.
+ */
+#define UDI_GFX_PROP_ENABLE 0
+
+#define UDI_GFX_PROP_ENABLE_DISABLED 0
+#define UDI_GFX_PROP_ENABLE_ENABLED 1
+#define UDI_GFX_PROP_ENABLE_RESET 2
+/* Constant: UDI_GFX_PROP_INPUT
+ *
+ * Valid values:
+ *     Any valid engine ID, provided no dependency cycles are created, or -1
+ *
+ * Ranges:
+ *     Any non-empty set of valid values. Often hardwired.
+ *
+ * Points to the engine that is processed before this unit. In the case of a 
+ * connector, it points to the last engine in a pipeline, and each engine points 
+ * to the next engine in the sequence. A value of -1 indicates a source that 
+ * only yields black pixels. Implementations must not allow cyclic structures. 
+ * Changing this value may reallocate resources, and engines that are no longer 
+ * referenced may lose their data (but not their state) when it is not part of 
+ * any pipeline. If preservation is required, the ENABLE state should be used
+ * instead. Valid ranges includes one or more from the list of engines and -1 
+ * combined. In most cases, this property can not be modified.
+ */
+#define UDI_GFX_PROP_INPUT 1
+/* Constant: UDI_GFX_PROP_WIDTH
+ *
+ * Valid values:
+ *     Any non-zero positive number.
+ *
+ * Ranges:
+ *     Contains at least one valid value. Often only multiples of UNIT_WIDTH
+ *     or a power of two are allowed. May be hardwired.
+ *
+ * Contains the amount of pixels in the horizontal direction. For connectors, 
+ * this is the amount of data pixels rendered horizontally. For engines, this 
+ * is the width in pixels of the image. Pixels requested from an engine outside 
+ * the range (0..width-1) are defined according to the <UDI_GFX_PROP_CLIP> 
+ * property. In some cases, hardware may support only fixed combinations of 
+ * width and height. In such cases, changing the width will also change the 
+ * height to a corresponding valid number. Valid ranges include any values
+ * strictly above zero. For connectors, expect large continuous ranges, large
+ * ranges with a certain modulus, a limited number of fixed values, or a
+ * constant value.
+ */
+#define UDI_GFX_PROP_WIDTH 2
+/* Constant: UDI_GFX_PROP_HEIGHT
+ *
+ * Valid values:
+ *     Any non-zero positive number.
+ *
+ * Ranges:
+ *     Contains at least one valid value. Often only multiples of UNIT_HEIGHT
+ *     or a power of two are allowed. May be hardwired.
+ *
+ * Contains the amount of pixels in the vertical direction. Functions similar
+ * to the width property, but changing it will not alter the width property,
+ * and it's range at any time contains the valid range for the currently
+ * selected width.
+ */
+#define UDI_GFX_PROP_HEIGHT 3
+
+/* Constant: UDI_GFX_PROP_CUSTOM
+ * The first property index of the driver's custom range. These are not assigned
+ * directly assigned by the UDI specification, but may be specified in the
+ * operator tree.
+ */
+#define UDI_GFX_PROP_CUSTOM 1024
+
+/* engine properties*/
+
+/* Constant: UDI_GFX_PROP_CLIP
+ *
+ * Valid values:
+ *     0 - points outside width x height are passed unmodified from input
+ *     1 - the engine's contents is tiled with size width x height
+ *     2 - points outside the width overflow into the y coordinate
+ *     3 - points outside the height overflow into the x coordinate
+ *
+ * Ranges:
+ *     Hardwired zero for connectors. Any non-empty subset for engines, usually
+ *     hardwired.
+ *
+ * For engines, contains the behaviour for pixels requested outside the width
+ * and height of the engine. Can be either 0 (pass from next stage), 1 (the
+ * coordinates are wrapped modulus the height and width), 2 (the coordinates
+ * overflow onto the next scanline horizontally, and wrap vertically), 3 (the
+ * coordinates overflow onto the next column vertically, and wrap horizontally).
+ * Valid ranges contain one or more of these options. For overlays and sprites,
+ * a value 0 is common. For framebuffers, 2 is the most common value. For
+ * connectors, this property is always 0 since they do not store pixel data
+ */
+#define UDI_GFX_PROP_CLIP 4
+
+/* Constant: UDI_GFX_PROP_UNIT_WIDTH
+ *
+ * Valid values:
+ *     Any non-zero positive value
+ *
+ * Ranges:
+ *     Any non-empty set of valid values. May be hardwired to 1 for
+ *     framebuffers, or a range of small values for hardware scaling, or any
+ *     larger hardwired number or set for tiling engines.
+ *
+ * Tiles are used to indicate that the hardware groups sets of pixels and have
+ * each group share certain properties, i.e. color or tile index, or share the
+ * same chroma subsample with only a different intensity. If the engine has no
+ * such grouping, or shares all properties over the entire contents, the value
+ * of this property should be 1. Some tile examples include rescaling, where a
+ * tile width of 2 indicates a pixel doubling in X direction, or in text mode
+ * where a tile width of 8 or 9 corresponds with the width of common bitmap
+ * fonts
+ */
+#define UDI_GFX_PROP_UNIT_WIDTH 5
+
+/* Constant: UDI_GFX_PROP_UNIT_HEIGHT
+ *
+ * Valid values:
+ *     Any non-zero positive value
+ *
+ * Ranges:
+ *     Any non-empty set of valid values. May be hardwired to 1 for
+ *     framebuffers, or a range of small values for hardware scaling, or any
+ *     larger hardwired number or set for tiling engines.
+ *
+ * See <UDI_GFX_PROP_UNIT_WIDTH>, but for the Y direction. Common values are
+ * 1-2 for framebuffers (doublescanning on or off), identical to the tile
+ * width, or mostly independent.
+ */
+#define UDI_GFX_PROP_UNIT_HEIGHT 6
+
+/* Constant: UDI_GFX_PROP_TRANSLATEX
+ * 
+ * Valid values:
+ *     Any, signed value.
+ *
+ * Ranges:
+ *     Any non-empty set. Typical values are hardwired zero, continuous
+ *     between -WIDTH and WIDTH, -WIDTH to zero inclusive, or all possible values
+ *
+ * The horizontal offset where drawing starts. A positive value means the top-left 
+ * corner moves towards the right end of the screen, a negative value moves the
+ * origin off the screen on the left side. Clipped areas moved off the screen do 
+ * not reappear on the opposite side.
+ *
+ * With clipping enabled, this field combined with <UDI_GFX_PROP_WIDTH> 
+ * determines the area where the image should be drawn, which is the horizontal 
+ * range from UDI_GFX_PROP_TRANSLATEX to UDI_GFX_PROP_WIDTH + 
+ * UDI_GFX_PROP_TRANSLATEX - 1
+ */
+#define UDI_GFX_PROP_TRANSLATEX 7
+
+/* Constant: UDI_GFX_PROP_TRANSLATEY
+ *
+ * Valid values:
+ *     Any signed value.
+ *
+ * Ranges:
+ *     Any non-empty set. Typical values are hardwired zero, continuous
+ *     between -WIDTH and WIDTH, or all possible values
+ *
+ * See <UDI_GFX_PROP_TRANSLATEX> but for the Y direction.
+ */
+#define UDI_GFX_PROP_TRANSLATEY 8
+
+#define UDI_GFX_PROP_GL_VERSION 14
+#define UDI_GFX_PROP_GLES_VERSION 15
+#define UDI_GFX_PROP_STATE_BLOCK 16
+#define UDI_GFX_PROP_COLOR_BITS 22
+#define UDI_GFX_PROP_GL_TARGET 23
+
+/* Constant: UDI_GFX_PROP_STOCK_FORMAT
+ *
+ * Value:
+ *     Zero, or any constant from <UDI_GFX_STOCK_FORMAT>
+ *
+ * Ranges:
+ *     Any non-empty set of valid values.
+ *
+ * This field indicates the storage format is one from a limited set of 
+ * typical configurations. If the field is zero, the engine is not knowingly
+ * configured as a common framebuffer. If nonzero, the operator chain and any
+ * dependent settings are defined to be functionally equivalent to that of a
+ * typical framebuffer device.
+ *
+ * The value zero does not imply that the device does not actually follow a
+ * set convention. This saves drivers from writing elaborate checking code
+ * to determine the condition in question.
+ *
+ * Writing this field potentially modifies other property fields within the
+ * same engine to establish the requested configuration. Manually writing such 
+ * properties after changing this setting may in turn revert this property to
+ * the zero state, even if there was no modification or the behaviour is still
+ * as previously advertised by this property.
+ */
+#define UDI_GFX_PROP_STOCK_FORMAT 27
+
+/* Constant: UDI_GFX_PROP_OPERATOR_COUNT
+ * 
+ * Valid values:
+ *     Any non-zero positive number
+ * 
+ * Ranges:
+ *     Most likely constant. Can be any set of valid values.
+ *
+ * The current value is the number of entries in the operator tree that can
+ * be requested for this engine using <udi_gfx_get_engine_operator_req> and
+ * <udi_gfx_get_engine_operator_ack>
+ */
+#define UDI_GFX_PROP_OPERATOR_COUNT 28
+
+#if 0
+/* properties for removal:*/
+#define UDI_GFX_PROP_STORE_COUNT 24       /* not generic*/
+#define UDI_GFX_PROP_STORE_WIDTH 9        /* not generic*/
+#define UDI_GFX_PROP_STORE_HEIGHT 10      /* not generic*/
+#define UDI_GFX_PROP_STORE_BITS 11        /* not generic*/
+#define UDI_GFX_PROP_PALETTE 1024         /* optional, can be derived from the operator tree*/
+#define UDI_GFX_PROP_BUFFER 1025          /* optional, can be derived from the operator tree*/
+#define UDI_GFX_PROP_TILESHEET 1026       /* optional, can be derived from the operator tree*/
+#define UDI_GFX_PROP_OPERATOR_INDEX 17    /* deprecated for dedicated methods*/
+#define UDI_GFX_PROP_OPERATOR_OPCODE 18   /* deprecated for dedicated methods*/
+#define UDI_GFX_PROP_OPERATOR_ARG_1 19    /* deprecated for dedicated methods*/
+#define UDI_GFX_PROP_OPERATOR_ARG_2 20    /* deprecated for dedicated methods*/
+#define UDI_GFX_PROP_OPERATOR_ARG_3 21    /* deprecated for dedicated methods*/
+#define UDI_GFX_PROP_SOURCE_WIDTH 12      /* should have been documented when I still knew what it did.*/
+#define UDI_GFX_PROP_SOURCE_HEIGHT 13     /* should have been documented when I still knew what it did.*/
+#define UDI_GFX_PROP_INPUTX 25            /* should have been documented when I still knew what it did.*/
+#define UDI_GFX_PROP_INPUTY 26            /* should have been documented when I still knew what it did.*/
+#endif
+
+/* connector properties*/
+#define UDI_GFX_PROP_SIGNAL 23
+#define UDI_GFX_PROP_CONNECTOR_TYPE 24
+#define UDI_GFX_PROP_VGA_H_FRONT_PORCH 25
+#define UDI_GFX_PROP_VGA_H_BACK_PORCH 26
+#define UDI_GFX_PROP_VGA_H_SYNC 27
+#define UDI_GFX_PROP_VGA_V_FRONT_PORCH 28
+#define UDI_GFX_PROP_VGA_V_BACK_PORCH 29
+#define UDI_GFX_PROP_VGA_V_SYNC 30
+#define UDI_GFX_PROP_DOT_CLOCK 31
+#define UDI_GFX_PROP_VGA_H_SYNC_POL 32
+#define UDI_GFX_PROP_VGA_V_SYNC_POL 33
+
+/**
+ * Enumeration: UDI_GFX_SIGNAL
+ * Lists the various signal types
+ */
+#define UDI_GFX_SIGNAL_HIDDEN 0
+#define UDI_GFX_SIGNAL_INTEGRATED 0
+#define UDI_GFX_SIGNAL_RGBHV 1
+#define UDI_GFX_SIGNAL_RGBS 2
+#define UDI_GFX_SIGNAL_RGSB 3
+#define UDI_GFX_SIGNAL_YPBPR 4
+#define UDI_GFX_SIGNAL_DVID 5
+#define UDI_GFX_SIGNAL_YUV 6
+#define UDI_GFX_SIGNAL_YIQ 7
+#define UDI_GFX_SIGNAL_Y_UV 8
+#define UDI_GFX_SIGNAL_Y_IQ 9
+#define UDI_GFX_SIGNAL_HDMI 10
+#define UDI_GFX_SIGNAL_TEXT 11
+#define UDI_GFX_SIGNAL_CUSTOM 12
+
+/**
+ * Enumeration: UDI_GFX_CONNECTOR
+ * Lists the various external connectors
+ */
+#define UDI_GFX_CONNECTOR_HIDDEN 0
+#define UDI_GFX_CONNECTOR_VGA 1
+#define UDI_GFX_CONNECTOR_DVI 2
+#define UDI_GFX_CONNECTOR_SVIDEO 3
+#define UDI_GFX_CONNECTOR_COMPONENT 4
+#define UDI_GFX_CONNECTOR_HDMI 5
+#define UDI_GFX_CONNECTOR_RF 6
+#define UDI_GFX_CONNECTOR_SCART 7
+#define UDI_GFX_CONNECTOR_COMPOSITE 8
+#define UDI_GFX_CONNECTOR_MEMBUFFER 9
+
+/**
+ * Enumeration: UDI_GFX_OPERATOR
+ * Lists the display output operator
+ */
+#define UDI_GFX_OPERATOR_RGB     0 /* output = (color) red(a1) + green(a2) + blue(a3) (each component is UDI_GFX_PROP_COLOR_BITS*/
+#define UDI_GFX_OPERATOR_YUV     1 /* output = (color) Y(a1) + U(a2) + V(a3)*/
+#define UDI_GFX_OPERATOR_YIQ     2 /* output = (color) Y(a1) + I(a2) + Q(a3)*/
+#define UDI_GFX_OPERATOR_I       3 /* output = (color) intensity(a1)*/
+#define UDI_GFX_OPERATOR_ALPHA   4 /* output = (color) a1 + alpha(a2)*/
+#define UDI_GFX_OPERATOR_ADD     5 /* output = a1 + a2 + v3*/
+#define UDI_GFX_OPERATOR_SUB     6 /* output = a1 - a2 - v3*/
+#define UDI_GFX_OPERATOR_MUL     7 /* output = a1 * a2*/
+#define UDI_GFX_OPERATOR_DIV     8 /* output = a1 / a2*/
+#define UDI_GFX_OPERATOR_MAD     9 /* output = a1 * a2 + a3*/
+#define UDI_GFX_OPERATOR_FRC    10 /* output = (a1 * a2) / a3*/
+#define UDI_GFX_OPERATOR_SHR    11 /* output = a1 >> (a2 + v3)*/
+#define UDI_GFX_OPERATOR_SHL    12 /* output = a1 << (a2 + v3)*/
+#define UDI_GFX_OPERATOR_ROR    13 /* output = a1 >> a2 (over a3 bits)*/
+#define UDI_GFX_OPERATOR_ROL    14 /* output = a1 << a2 (over a3 bits)*/
+#define UDI_GFX_OPERATOR_SAR    15 /* output = a1 >> a2 (width is a3 bits, i.e. empties are filled with bit a3-1)*/
+#define UDI_GFX_OPERATOR_SAL    16 /* output = a1 <<< (a2 + v3) (empties filled with bit 0)*/
+#define UDI_GFX_OPERATOR_AND    17 /* output = a1 & a2*/
+#define UDI_GFX_OPERATOR_OR     18 /* output = a1 | a2 | v3*/
+#define UDI_GFX_OPERATOR_NOT    19 /* output = ~a1*/
+#define UDI_GFX_OPERATOR_XOR    20 /* output = a1 ^ a2 ^ v3*/
+#define UDI_GFX_OPERATOR_NEG    21 /* output = -a1*/
+#define UDI_GFX_OPERATOR_SEG    22 /* output = (a1 >> v2) & (2**v3-1) (select v3 bits starting from bit v2)*/
+#define UDI_GFX_OPERATOR_RANGE  23 /* output = (a1 > a2) ? a2 : ((a1 < a3) ? a3 : a1)*/
+#define UDI_GFX_OPERATOR_CONST  24 /* output = v1*/
+#define UDI_GFX_OPERATOR_ATTR   25 /* output = property[a1 + v2]*/
+#define UDI_GFX_OPERATOR_SWITCH 26 /* output = output[(a1 % v3) + v2]*/
+#define UDI_GFX_OPERATOR_BUFFER 27 /* output = buffer[a1][a2] (buffer is v3 bits per entry)*/
+#define UDI_GFX_OPERATOR_X      28 /* output = output x pixel*/
+#define UDI_GFX_OPERATOR_Y      29 /* output = output y pixel*/
+#define UDI_GFX_OPERATOR_TX     30 /* output = horizontal tile index belonging to output pixel*/
+#define UDI_GFX_OPERATOR_TY     31 /* output = vertical tile index belonging to output pixel*/
+#define UDI_GFX_OPERATOR_TXOFF  32 /* output = horizontal offset from start of tile*/
+#define UDI_GFX_OPERATOR_TYOFF  33 /* output = vertical offset from start of tile*/
+#define UDI_GFX_OPERATOR_INPUT  34 /* output = input engine[x][y]   component v1*/
+#define UDI_GFX_OPERATOR_DINPUT 35 /* output = input engine[a1][a2] component v3*/
+
+/* Enumeration: UDI_GFX_STOCK_FORMAT
+ * Lists stock configurations
+ *
+ * When a stock configuration is used, the device is set to behave as a 
+ * simple framebuffer device. The <UDI_GFX_PROP_WIDTH> and <UDI_GFX_PROP_HEIGHT>
+ * determine the virtual size of the framebuffer, and <UDI_GFX_PROP_TRANSLATEX>
+ * and <UDI_GFX_PROP_TRANSLATEY> indicate the offset into that framebuffer 
+ * that is visible (which are typically restricted to negative values)
+ */
+#define UDI_GFX_STOCK_FORMAT_UNKNOWN  0
+#define UDI_GFX_STOCK_FORMAT_R8G8B8X8 1
+#define UDI_GFX_STOCK_FORMAT_B8G8R8X8 2
+#define UDI_GFX_STOCK_FORMAT_R8G8B8   3
+#define UDI_GFX_STOCK_FORMAT_B8G8R8   4
+#define UDI_GFX_STOCK_FORMAT_R5G6B5   5
+#define UDI_GFX_STOCK_FORMAT_B5G6R5   6
+#define UDI_GFX_STOCK_FORMAT_R5G5B5X1 7
+#define UDI_GFX_STOCK_FORMAT_B5G5R5X1 8
+#define UDI_GFX_STOCK_FORMAT_N8 9
+
+/* Enumeration: UDI_GFX_BUFFER_INFO_FLAG*/
+/* Lists behavioural patterns for direct buffer accesses.*/
+/**/
+#define UDI_GFX_BUFFER_INFO_FLAG_R              0x0001  /* buffer can be read*/
+#define UDI_GFX_BUFFER_INFO_FLAG_W              0x0002  /* buffer can be written*/
+#define UDI_GFX_BUFFER_INFO_FLAG_BITALIGN_ENTRY 0x0004  /* for non-multiple-of-eight buffer slot sizes, align on byte boundary every unit*/
+#define UDI_GFX_BUFFER_INFO_FLAG_BITALIGN_ROW   0x0008  /* for non-multiple-of-eight buffer slot sizes, align only the start of the row*/
+
+
+/* Constant: UDI_GFX_PROVIDER_OPS_NUM*/
+/* the ops number used for the graphics driver*/
+#define UDI_GFX_PROVIDER_OPS_NUM 1
+
+/* Constant: UDI_GFX_CLIENT_OPS_NUM*/
+/* the ops number used for the graphics application*/
+#define UDI_GFX_CLIENT_OPS_NUM 2
+
+/* Structure: udi_gfx_bind_cb_t*/
+/* Contains the operations of a driver binding request*/
+typedef struct {
+    /* Variable: gcb*/
+    /* The main control block*/
+    udi_cb_t gcb;    
+} udi_gfx_bind_cb_t;
+#define UDI_GFX_BIND_CB_NUM 1
+
+/* Function: udi_block_bind_req*/
+/* function pointer prototype for connecting to a block device*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_block_bind_cb_t>*/
+/**/
+typedef void udi_gfx_bind_req_op_t (udi_gfx_bind_cb_t *cb );
+udi_gfx_bind_req_op_t udi_gfx_bind_req;
+
+/* Function: udi_gfx_bind_ack*/
+/* function pointer prototype for acknowledging a connection request*/
+/* */
+/* in:*/
+/*     cb      - A pointer to a <udi_gfx_bind_cb_t>*/
+/*     sockets - The number of addressable socket components*/
+/*     engines - The number of addressable engine components*/
+/*     status  - The result of the bind operation*/
+/**/
+typedef void udi_gfx_bind_ack_op_t (udi_gfx_bind_cb_t *cb, udi_index_t sockets, udi_index_t engines, udi_status_t status );
+udi_gfx_bind_ack_op_t udi_gfx_bind_ack;
+
+/* Function: udi_gfx_unbind_req*/
+/* function pointer prototype for disconnecting a block device*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_block_bind_cb_t>*/
+/**/
+typedef void udi_gfx_unbind_req_op_t (udi_gfx_bind_cb_t *cb );
+udi_gfx_unbind_req_op_t udi_gfx_unbind_req;
+
+/* Function: udi_gfx_unbind_ack*/
+/* function pointer prototype for connecting to a block device*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_bind_cb_t>*/
+/**/
+typedef void udi_gfx_unbind_ack_op_t (udi_gfx_bind_cb_t *cb );
+udi_gfx_unbind_ack_op_t udi_gfx_unbind_ack;
+
+/* Structure: udi_gfx_state_cb_t*/
+/* Contains the operations of a read/write transaction*/
+typedef struct {
+    /* Variable: gcb*/
+    /* The main control block*/
+    udi_cb_t gcb;    
+    udi_ubit32_t subsystem;
+    udi_ubit32_t attribute;
+} udi_gfx_state_cb_t;
+#define UDI_GFX_STATE_CB_NUM 2
+
+/* Function: udi_gfx_set_engine_req*/
+/* function pointer prototype for setting an engine state*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_state_cb_t>*/
+/**/
+typedef void udi_gfx_set_engine_req_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_set_engine_req_op_t udi_gfx_set_engine_req;
+
+/* Function: udi_gfx_set_connector_req*/
+/* function pointer prototype for setting an connector state*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_state_cb_t>*/
+/**/
+typedef void udi_gfx_set_connector_req_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_set_connector_req_op_t udi_gfx_set_connector_req;
+
+/* Function: udi_gfx_set_engine_ack*/
+/* function pointer prototype for setting an engine state*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_state_cb_t>*/
+/**/
+typedef void udi_gfx_set_engine_ack_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_set_engine_ack_op_t udi_gfx_set_engine_ack;
+
+/* Function: udi_gfx_set_connector_ack*/
+/* function pointer prototype for setting an engine state*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_state_cb_t>*/
+/**/
+typedef void udi_gfx_set_connector_ack_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_set_connector_ack_op_t udi_gfx_set_connector_ack;
+
+/* Function: udi_gfx_get_engine_req*/
+/* function pointer prototype for setting an engine state*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_state_cb_t>*/
+/**/
+typedef void udi_gfx_get_engine_req_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_get_engine_req_op_t udi_gfx_get_engine_req;
+
+/* Function: udi_gfx_get_connector_req*/
+/* function pointer prototype for setting an connector state*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_state_cb_t>*/
+/**/
+typedef void udi_gfx_get_connector_req_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_get_connector_req_op_t udi_gfx_get_connector_req;
+
+/* Function: udi_gfx_get_engine_ack*/
+/* function pointer prototype for setting an engine state*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_state_cb_t>*/
+/**/
+typedef void udi_gfx_get_engine_ack_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_get_engine_ack_op_t udi_gfx_get_engine_ack;
+
+/* Function: udi_gfx_get_connector_ack*/
+/* function pointer prototype for setting an engine state*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_state_cb_t>*/
+/**/
+typedef void udi_gfx_get_connector_ack_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_get_connector_ack_op_t udi_gfx_get_connector_ack;
+
+/* Function: udi_gfx_set_engine_nak*/
+/* function pointer prototype for setting an engine state*/
+/* */
+/* in:*/
+/*     cb     - A pointer to a <udi_gfx_state_cb_t>*/
+/*     status - An UDI status value indicative of the error*/
+/**/
+typedef void udi_gfx_set_engine_nak_op_t (udi_gfx_state_cb_t *cb, udi_status_t status);
+udi_gfx_set_engine_nak_op_t udi_gfx_set_engine_nak;
+
+/* Function: udi_gfx_set_connector_nak*/
+/* function pointer prototype for setting an engine state*/
+/* */
+/* in:*/
+/*     cb     - A pointer to a <udi_gfx_state_cb_t>*/
+/*     status - An UDI status value indicative of the error*/
+/**/
+typedef void udi_gfx_set_connector_nak_op_t (udi_gfx_state_cb_t *cb, udi_status_t status);
+udi_gfx_set_connector_nak_op_t udi_gfx_get_connector_nak;
+
+/* Structure: udi_gfx_range_cb_t*/
+/* Contains the operations of a range request transaction*/
+typedef struct {
+    /* Variable: gcb*/
+    /* The main control block*/
+    udi_cb_t gcb;    
+    udi_ubit32_t subsystem;
+    udi_ubit32_t attribute;
+    udi_buf_t * rangedata;  
+} udi_gfx_range_cb_t;
+#define UDI_GFX_RANGE_CB_NUM 3
+
+/* Function: udi_gfx_range_engine_req*/
+/* function pointer prototype for getting an engine property range*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_range_cb_t>*/
+/**/
+typedef void udi_gfx_range_engine_req_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_engine_req_op_t udi_gfx_range_engine_req;
+
+/* Function: udi_gfx_range_connector_req*/
+/* function pointer prototype for getting a connector property range*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_range_cb_t>*/
+/**/
+typedef void udi_gfx_range_connector_req_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_connector_req_op_t udi_gfx_range_connector_req;
+
+/* Function: udi_gfx_range_engine_ack*/
+/* function pointer prototype for replying an engine property range*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_range_cb_t>*/
+/**/
+typedef void udi_gfx_range_engine_ack_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_engine_ack_op_t udi_gfx_range_engine_ack;
+
+/* Function: udi_gfx_range_connector_ack*/
+/* function pointer prototype for replying a connector property range*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_range_cb_t>*/
+/**/
+typedef void udi_gfx_range_connector_ack_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_connector_ack_op_t udi_gfx_range_connector_ack;
+
+/* Function: udi_gfx_get_engine_operator_req*/
+/* function pointer prototype for requesting the engine operator layout*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_state_cb_t>*/
+/**/
+typedef void udi_gfx_get_engine_operator_req_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_get_engine_operator_req_op_t udi_gfx_get_engine_operator_req;
+
+/* Function: udi_gfx_get_engine_operator_ack*/
+/* function pointer prototype for replying the engine operator layout*/
+/* */
+/* in:*/
+/*     cb   - A pointer to a <udi_gfx_state_cb_t>*/
+/*     op   - The operator performed at this index*/
+/*     arg1 - the first argument to this operator*/
+/*     arg2 - the second argument to this operator*/
+/*     arg3 - the third argument to this operator*/
+/**/
+typedef void udi_gfx_get_engine_operator_ack_op_t (udi_gfx_range_cb_t *cb, udi_ubit32_t op, udi_ubit32_t arg1, udi_ubit32_t arg2, udi_ubit32_t arg3 );
+udi_gfx_get_engine_operator_ack_op_t udi_gfx_get_engine_operator_ack;
+
+
+
+/* Structure: udi_gfx_command_cb_t*/
+/* Contains the operations of a command sequence*/
+typedef struct {
+    /* Variable: gcb*/
+    /* The main control block*/
+    udi_cb_t gcb;    
+    udi_buf_t * commanddata;
+} udi_gfx_command_cb_t;
+#define UDI_GFX_COMMAND_CB_NUM 4
+
+/* Function: udi_gfx_connector_command_req*/
+/* function pointer prototype for sending command data to the output connector*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_command_cb_t>*/
+/**/
+typedef void udi_gfx_connector_command_req_op_t (udi_gfx_command_cb_t *cb );
+udi_gfx_connector_command_req_op_t udi_gfx_connector_command_req;
+
+/* Function: udi_gfx_engine_command_req*/
+/* function pointer prototype for sending command data to the engine*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_command_cb_t>*/
+/**/
+typedef void udi_gfx_engine_command_req_op_t (udi_gfx_command_cb_t *cb );
+udi_gfx_engine_command_req_op_t udi_gfx_engine_command_req;
+
+/* Function: udi_gfx_connector_command_ack*/
+/* function pointer prototype for sending command data replies*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_command_cb_t>*/
+/**/
+typedef void udi_gfx_connector_command_ack_op_t (udi_gfx_command_cb_t *cb);
+udi_gfx_connector_command_ack_op_t udi_gfx_connector_command_ack;
+
+/* Function: udi_gfx_engine_command_ack*/
+/* function pointer prototype for sending engine data replies*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_command_cb_t>*/
+/**/
+typedef void udi_gfx_engine_command_ack_op_t (udi_gfx_command_cb_t *cb);
+udi_gfx_engine_command_ack_op_t udi_gfx_engine_command_ack;
+
+/* Structure: udi_gfx_buffer_cb_t*/
+/* Contains a description of a buffer, or area thereof*/
+typedef struct {
+    /* Variable: gcb*/
+    /* The main control block*/
+    udi_cb_t gcb;    
+    udi_ubit32_t buffer_index;
+} udi_gfx_buffer_info_cb_t;
+
+/* Function: udi_gfx_buffer_info_req*/
+/* function pointer prototype for getting buffer configuration information*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_command_cb_t>*/
+/**/
+typedef void udi_gfx_buffer_info_req_op_t (udi_gfx_buffer_info_cb_t *cb);
+udi_gfx_buffer_info_req_op_t udi_gfx_buffer_info_req;
+
+/* Function: udi_gfx_buffer_info_ack*/
+/* function pointer prototype for getting buffer configuration information*/
+/* */
+/* in:*/
+/*     cb       - A pointer to a <udi_gfx_command_cb_t>*/
+/*     width    - The width of the buffer*/
+/*     height   - The height of the buffer*/
+/*     bitsper  - The number of bits read from the buffer per pixel unit*/
+/*     flags    - A bitfield of <UDI_GFX_BUFFER_FLAGS> indicating the exposed */
+/*                capabilities of this buffer*/
+/**/
+/* Note that bitsper might not be a multiple of eight.*/
+/**/
+typedef void udi_gfx_buffer_info_ack_op_t (udi_gfx_buffer_info_cb_t *cb, udi_ubit32_t width, udi_ubit32_t height, udi_ubit32_t bitsper, udi_ubit32_t flags);
+udi_gfx_buffer_info_ack_op_t udi_gfx_buffer_info_ack;
+
+/* Structure: udi_gfx_buffer_cb_t*/
+/* Contains a description of a buffer, or area thereof*/
+typedef struct {
+    /* Variable: gcb*/
+    /* The main control block*/
+    udi_cb_t gcb;    
+    udi_ubit32_t buffer_index;
+    udi_ubit32_t x;
+    udi_ubit32_t y;
+    udi_ubit32_t width;
+    udi_ubit32_t height;
+    udi_buf_t * buffer;
+} udi_gfx_buffer_cb_t;
+
+/* Function: udi_gfx_buffer_write_req_op_t*/
+/* function pointer prototype for writing raw hardware buffers*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_buffer_cb_t>*/
+/**/
+typedef void udi_gfx_buffer_write_req_op_t (udi_gfx_buffer_cb_t *cb);
+udi_gfx_buffer_write_req_op_t udi_gfx_buffer_write_req;
+
+/* Function: udi_gfx_buffer_write_req_op_t*/
+/* function pointer prototype for reading raw hardware buffers*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_buffer_cb_t>*/
+/**/
+typedef void udi_gfx_buffer_read_req_op_t (udi_gfx_buffer_cb_t *cb);
+udi_gfx_buffer_read_req_op_t udi_gfx_buffer_read_req;
+
+/* Function: udi_gfx_buffer_write_ack_op_t*/
+/* function pointer prototype for writing raw hardware buffers*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_buffer_cb_t>*/
+/**/
+typedef void udi_gfx_buffer_write_ack_op_t (udi_gfx_buffer_cb_t *cb);
+udi_gfx_buffer_write_ack_op_t udi_gfx_buffer_write_ack;
+
+/* Function: udi_gfx_buffer_write_ack_op_t*/
+/* function pointer prototype for reading raw hardware buffers*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_buffer_cb_t>*/
+/**/
+typedef void udi_gfx_buffer_read_ack_op_t (udi_gfx_buffer_cb_t *cb);
+udi_gfx_buffer_read_ack_op_t udi_gfx_buffer_read_ack;
+
+/* Function: udi_gfx_buffer_write_nak_op_t*/
+/* error handling for buffer writes*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_buffer_cb_t>*/
+/**/
+typedef void udi_gfx_buffer_write_nak_op_t (udi_gfx_buffer_cb_t *cb, udi_status_t status);
+udi_gfx_buffer_write_nak_op_t udi_gfx_buffer_write_nak;
+
+/* Function: udi_gfx_buffer_write_nak_op_t*/
+/* error handling for buffer reads*/
+/* */
+/* in:*/
+/*     cb - A pointer to a <udi_gfx_buffer_cb_t>*/
+/**/
+typedef void udi_gfx_buffer_read_nak_op_t (udi_gfx_buffer_cb_t *cb, udi_status_t status);
+udi_gfx_buffer_read_nak_op_t udi_gfx_buffer_read_nak;
+
+/* Structure: udi_gfx_provider_ops_t
+ * 
+ * The graphics metalanguage entry points (provider side)
+ */
+typedef const struct {
+    udi_channel_event_ind_op_t          *channel_event_ind_op;
+    udi_gfx_bind_req_op_t               *gfx_bind_req_op;
+    udi_gfx_unbind_req_op_t             *gfx_unbind_req_op;
+    udi_gfx_set_connector_req_op_t      *gfx_set_connector_req_op;
+    udi_gfx_set_engine_req_op_t         *gfx_set_engine_req_op;
+    udi_gfx_get_connector_req_op_t      *gfx_get_connector_req_op;
+    udi_gfx_get_engine_req_op_t         *gfx_get_engine_req_op;
+    udi_gfx_range_connector_req_op_t    *gfx_range_connector_req_op;
+    udi_gfx_range_engine_req_op_t       *gfx_range_engine_req_op;
+    udi_gfx_get_engine_operator_req_op_t*gfx_get_engine_operator_req_op;
+    udi_gfx_connector_command_req_op_t  *gfx_connector_command_req_op;
+    udi_gfx_engine_command_req_op_t     *gfx_engine_command_req_op;
+    udi_gfx_buffer_info_req_op_t        *gfx_buffer_info_req_op;
+    udi_gfx_buffer_read_req_op_t        *gfx_buffer_read_req_op;
+    udi_gfx_buffer_write_req_op_t       *gfx_buffer_write_req_op;
+} udi_gfx_provider_ops_t;
+
+/* Structure: udi_gfx_client_ops_t
+ *
+ * The graphics metalanguage entry points (client side)
+ */
+typedef const struct {
+    udi_channel_event_ind_op_t          *channel_event_ind_op;
+    udi_gfx_bind_ack_op_t               *gfx_bind_ack_op;
+    udi_gfx_unbind_ack_op_t             *gfx_unbind_ack_op;
+    udi_gfx_set_connector_ack_op_t      *gfx_set_connector_ack_op;
+    udi_gfx_set_engine_ack_op_t         *gfx_set_engine_ack_op;
+    udi_gfx_set_connector_nak_op_t      *gfx_set_connector_nak_op;
+    udi_gfx_set_engine_nak_op_t         *gfx_set_engine_nak_op;
+    udi_gfx_get_connector_ack_op_t      *gfx_get_connector_ack_op;
+    udi_gfx_get_engine_ack_op_t         *gfx_get_engine_ack_op;
+    udi_gfx_range_connector_ack_op_t    *gfx_range_connector_ack_op;
+    udi_gfx_range_engine_ack_op_t       *gfx_range_engine_ack_op;
+    udi_gfx_get_engine_operator_req_op_t*gfx_get_engine_operator_ack_op;
+    udi_gfx_connector_command_ack_op_t  *gfx_connector_command_ack_op;
+    udi_gfx_engine_command_ack_op_t     *gfx_engine_command_ack_op;
+    udi_gfx_buffer_info_ack_op_t        *gfx_buffer_info_ack_op;
+    udi_gfx_buffer_read_ack_op_t        *gfx_buffer_read_ack_op;
+    udi_gfx_buffer_write_ack_op_t       *gfx_buffer_write_ack_op;
+    udi_gfx_buffer_read_nak_op_t        *gfx_buffer_read_nak_op;
+    udi_gfx_buffer_write_nak_op_t       *gfx_buffer_write_nak_op;
+} udi_gfx_client_ops_t;
+
+
+/* temporary*/
+#ifndef UDI_ANNOY_ME
+void EngineReturnSimpleRange (int source, int index, int prop, int first, int last, int modulus);
+void ConnectorReturnSimpleRange (int source, int index, int prop, int first, int last, int modulus);
+void EngineReturnConstantRange (int source, int index, int prop, int value);
+void ConnectorReturnConstantRange (int source, int index, int prop, int value);
+void EngineReturnBooleanRange (int source, int index, int prop, int value1, int value2);
+void ConnectorReturnBooleanRange (int source, int index, int prop, int value1, int value2);
+#endif
+
+#endif
diff --git a/UDI/include/udi_gfx.h.ORIG b/UDI/include/udi_gfx.h.ORIG
new file mode 100644
index 0000000000000000000000000000000000000000..fcaa5b7423896241c08210765de46ff4bcdc5e45
--- /dev/null
+++ b/UDI/include/udi_gfx.h.ORIG
@@ -0,0 +1,848 @@
+/**
+ * Summary: udi_gfx.h
+ * Contains the graphics metalanguage interface details
+ *
+ * Author:
+ *     Marcel Sondaar
+ *
+ * License:
+ *     <Public Domain>
+ *
+ * Origin:
+ *     http://www.d-rift.nl/combuster/mos3/?p=viewsource&file=/include/common/udi_gfx.h
+ */
+
+// note that the specification, and thus, the contents of this file is not fixed.
+
+#ifndef __UDI_GFX_H__
+#define __UDI_GFX_H__
+
+#include <udi.h>
+
+#ifndef UDI_GFX_VERSION
+#error "UDI_GFX_VERSION not defined."
+#elif UDI_GFX_VERSION != 0x101
+#error "UDI_GFX_VERSION not supported."
+#endif
+
+/**
+ * Enumeration: UDI_GFX_PROP
+ * Lists the various UDI properties
+ */
+
+// General state properties
+/* Constant: UDI_GFX_PROP_ENABLE
+ *
+ * Valid values:
+ *     0 - disabled
+ *     1 - enabled
+ *     2 - reset
+ *
+ * Ranges:
+ *     Must include at least 1
+ *
+ * The primary state of the connector or engine. An enabled state indicates
+ * it is functioning and generating live output. A disabled state is one where
+ * it is not contributing to any output but is otherwise functional. Finally
+ * the reset state is where the driver is free to deallocate all resources 
+ * corresponding to this component and trash any state not referenced by other
+ * components.
+ *
+ * A disabled or reset engine forwards all data from the previous stage 
+ * unmodified. The disabled state indicates that the component might be 
+ * returned to its enabled state within short notice.
+ *
+ * A disabled connector will not send pixel data, but can perform other 
+ * initialisation communication such as DDC. A reset connector will not respond
+ * in any fashion and can not be used for other purposes. Hardware is expected
+ * to be powered down in such state.
+ *
+ * Users should expect significant delays when moving components in and out of
+ * the reset state. Moving engines between the enabled and disabled state should
+ * take effect within one frame, such transition should take effect on a frame 
+ * boundary when supported.
+ */
+#define UDI_GFX_PROP_ENABLE 0
+
+#define UDI_GFX_PROP_ENABLE_DISABLED 0
+#define UDI_GFX_PROP_ENABLE_ENABLED 1
+#define UDI_GFX_PROP_ENABLE_RESET 2
+/* Constant: UDI_GFX_PROP_INPUT
+ *
+ * Valid values:
+ *     Any valid engine ID, provided no dependency cycles are created, or -1
+ *
+ * Ranges:
+ *     Any non-empty set of valid values. Often hardwired.
+ *
+ * Points to the engine that is processed before this unit. In the case of a 
+ * connector, it points to the last engine in a pipeline, and each engine points 
+ * to the next engine in the sequence. A value of -1 indicates a source that 
+ * only yields black pixels. Implementations must not allow cyclic structures. 
+ * Changing this value may reallocate resources, and engines that are no longer 
+ * referenced may lose their data (but not their state) when it is not part of 
+ * any pipeline. If preservation is required, the ENABLE state should be used
+ * instead. Valid ranges includes one or more from the list of engines and -1 
+ * combined. In most cases, this property can not be modified.
+ */
+#define UDI_GFX_PROP_INPUT 1
+/* Constant: UDI_GFX_PROP_WIDTH
+ *
+ * Valid values:
+ *     Any non-zero positive number.
+ *
+ * Ranges:
+ *     Contains at least one valid value. Often only multiples of UNIT_WIDTH
+ *     or a power of two are allowed. May be hardwired.
+ *
+ * Contains the amount of pixels in the horizontal direction. For connectors, 
+ * this is the amount of data pixels rendered horizontally. For engines, this 
+ * is the width in pixels of the image. Pixels requested from an engine outside 
+ * the range (0..width-1) are defined according to the <UDI_GFX_PROP_CLIP> 
+ * property. In some cases, hardware may support only fixed combinations of 
+ * width and height. In such cases, changing the width will also change the 
+ * height to a corresponding valid number. Valid ranges include any values
+ * strictly above zero. For connectors, expect large continuous ranges, large
+ * ranges with a certain modulus, a limited number of fixed values, or a
+ * constant value.
+ */
+#define UDI_GFX_PROP_WIDTH 2
+/* Constant: UDI_GFX_PROP_HEIGHT
+ *
+ * Valid values:
+ *     Any non-zero positive number.
+ *
+ * Ranges:
+ *     Contains at least one valid value. Often only multiples of UNIT_HEIGHT
+ *     or a power of two are allowed. May be hardwired.
+ *
+ * Contains the amount of pixels in the vertical direction. Functions similar
+ * to the width property, but changing it will not alter the width property,
+ * and it's range at any time contains the valid range for the currently
+ * selected width.
+ */
+#define UDI_GFX_PROP_HEIGHT 3
+
+/* Constant: UDI_GFX_PROP_CUSTOM
+ * The first property index of the driver's custom range. These are not assigned
+ * directly assigned by the UDI specification, but may be specified in the
+ * operator tree.
+ */
+#define UDI_GFX_PROP_CUSTOM 1024
+
+// engine properties
+
+/* Constant: UDI_GFX_PROP_CLIP
+ *
+ * Valid values:
+ *     0 - points outside width x height are passed unmodified from input
+ *     1 - the engine's contents is tiled with size width x height
+ *     2 - points outside the width overflow into the y coordinate
+ *     3 - points outside the height overflow into the x coordinate
+ *
+ * Ranges:
+ *     Hardwired zero for connectors. Any non-empty subset for engines, usually
+ *     hardwired.
+ *
+ * For engines, contains the behaviour for pixels requested outside the width
+ * and height of the engine. Can be either 0 (pass from next stage), 1 (the
+ * coordinates are wrapped modulus the height and width), 2 (the coordinates
+ * overflow onto the next scanline horizontally, and wrap vertically), 3 (the
+ * coordinates overflow onto the next column vertically, and wrap horizontally).
+ * Valid ranges contain one or more of these options. For overlays and sprites,
+ * a value 0 is common. For framebuffers, 2 is the most common value. For
+ * connectors, this property is always 0 since they do not store pixel data
+ */
+#define UDI_GFX_PROP_CLIP 4
+
+/* Constant: UDI_GFX_PROP_UNIT_WIDTH
+ *
+ * Valid values:
+ *     Any non-zero positive value
+ *
+ * Ranges:
+ *     Any non-empty set of valid values. May be hardwired to 1 for
+ *     framebuffers, or a range of small values for hardware scaling, or any
+ *     larger hardwired number or set for tiling engines.
+ *
+ * Tiles are used to indicate that the hardware groups sets of pixels and have
+ * each group share certain properties, i.e. color or tile index, or share the
+ * same chroma subsample with only a different intensity. If the engine has no
+ * such grouping, or shares all properties over the entire contents, the value
+ * of this property should be 1. Some tile examples include rescaling, where a
+ * tile width of 2 indicates a pixel doubling in X direction, or in text mode
+ * where a tile width of 8 or 9 corresponds with the width of common bitmap
+ * fonts
+ */
+#define UDI_GFX_PROP_UNIT_WIDTH 5
+
+/* Constant: UDI_GFX_PROP_UNIT_HEIGHT
+ *
+ * Valid values:
+ *     Any non-zero positive value
+ *
+ * Ranges:
+ *     Any non-empty set of valid values. May be hardwired to 1 for
+ *     framebuffers, or a range of small values for hardware scaling, or any
+ *     larger hardwired number or set for tiling engines.
+ *
+ * See <UDI_GFX_PROP_UNIT_WIDTH>, but for the Y direction. Common values are
+ * 1-2 for framebuffers (doublescanning on or off), identical to the tile
+ * width, or mostly independent.
+ */
+#define UDI_GFX_PROP_UNIT_HEIGHT 6
+
+/* Constant: UDI_GFX_PROP_TRANSLATEX
+ * 
+ * Valid values:
+ *     Any, signed value.
+ *
+ * Ranges:
+ *     Any non-empty set. Typical values are hardwired zero, continuous
+ *     between -WIDTH and WIDTH, -WIDTH to zero inclusive, or all possible values
+ *
+ * The horizontal offset where drawing starts. A positive value means the top-left 
+ * corner moves towards the right end of the screen, a negative value moves the
+ * origin off the screen on the left side. Clipped areas moved off the screen do 
+ * not reappear on the opposite side.
+ *
+ * With clipping enabled, this field combined with <UDI_GFX_PROP_WIDTH> 
+ * determines the area where the image should be drawn, which is the horizontal 
+ * range from UDI_GFX_PROP_TRANSLATEX to UDI_GFX_PROP_WIDTH + 
+ * UDI_GFX_PROP_TRANSLATEX - 1
+ */
+#define UDI_GFX_PROP_TRANSLATEX 7
+
+/* Constant: UDI_GFX_PROP_TRANSLATEY
+ *
+ * Valid values:
+ *     Any signed value.
+ *
+ * Ranges:
+ *     Any non-empty set. Typical values are hardwired zero, continuous
+ *     between -WIDTH and WIDTH, or all possible values
+ *
+ * See <UDI_GFX_PROP_TRANSLATEX> but for the Y direction.
+ */
+#define UDI_GFX_PROP_TRANSLATEY 8
+
+#define UDI_GFX_PROP_GL_VERSION 14
+#define UDI_GFX_PROP_GLES_VERSION 15
+#define UDI_GFX_PROP_STATE_BLOCK 16
+#define UDI_GFX_PROP_COLOR_BITS 22
+#define UDI_GFX_PROP_GL_TARGET 23
+
+/* Constant: UDI_GFX_PROP_STOCK_FORMAT
+ *
+ * Value:
+ *     Zero, or any constant from <UDI_GFX_STOCK_FORMAT>
+ *
+ * Ranges:
+ *     Any non-empty set of valid values.
+ *
+ * This field indicates the storage format is one from a limited set of 
+ * typical configurations. If the field is zero, the engine is not knowingly
+ * configured as a common framebuffer. If nonzero, the operator chain and any
+ * dependent settings are defined to be functionally equivalent to that of a
+ * typical framebuffer device.
+ *
+ * The value zero does not imply that the device does not actually follow a
+ * set convention. This saves drivers from writing elaborate checking code
+ * to determine the condition in question.
+ *
+ * Writing this field potentially modifies other property fields within the
+ * same engine to establish the requested configuration. Manually writing such 
+ * properties after changing this setting may in turn revert this property to
+ * the zero state, even if there was no modification or the behaviour is still
+ * as previously advertised by this property.
+ */
+#define UDI_GFX_PROP_STOCK_FORMAT 27
+
+/* Constant: UDI_GFX_PROP_OPERATOR_COUNT
+ * 
+ * Valid values:
+ *     Any non-zero positive number
+ * 
+ * Ranges:
+ *     Most likely constant. Can be any set of valid values.
+ *
+ * The current value is the number of entries in the operator tree that can
+ * be requested for this engine using <udi_gfx_get_engine_operator_req> and
+ * <udi_gfx_get_engine_operator_ack>
+ */
+#define UDI_GFX_PROP_OPERATOR_COUNT 28
+
+// properties for removal:
+#define UDI_GFX_PROP_STORE_COUNT 24       // not generic
+#define UDI_GFX_PROP_STORE_WIDTH 9        // not generic
+#define UDI_GFX_PROP_STORE_HEIGHT 10      // not generic
+#define UDI_GFX_PROP_STORE_BITS 11        // not generic
+#define UDI_GFX_PROP_PALETTE 1024         // optional, can be derived from the operator tree
+#define UDI_GFX_PROP_BUFFER 1025          // optional, can be derived from the operator tree
+#define UDI_GFX_PROP_TILESHEET 1026       // optional, can be derived from the operator tree
+#define UDI_GFX_PROP_OPERATOR_INDEX 17    // deprecated for dedicated methods
+#define UDI_GFX_PROP_OPERATOR_OPCODE 18   // deprecated for dedicated methods
+#define UDI_GFX_PROP_OPERATOR_ARG_1 19    // deprecated for dedicated methods
+#define UDI_GFX_PROP_OPERATOR_ARG_2 20    // deprecated for dedicated methods
+#define UDI_GFX_PROP_OPERATOR_ARG_3 21    // deprecated for dedicated methods
+#define UDI_GFX_PROP_SOURCE_WIDTH 12      // should have been documented when I still knew what it did.
+#define UDI_GFX_PROP_SOURCE_HEIGHT 13     // should have been documented when I still knew what it did.
+#define UDI_GFX_PROP_INPUTX 25            // should have been documented when I still knew what it did.
+#define UDI_GFX_PROP_INPUTY 26            // should have been documented when I still knew what it did.
+
+// connector properties
+#define UDI_GFX_PROP_SIGNAL 23
+#define UDI_GFX_PROP_CONNECTOR_TYPE 24
+#define UDI_GFX_PROP_VGA_H_FRONT_PORCH 25
+#define UDI_GFX_PROP_VGA_H_BACK_PORCH 26
+#define UDI_GFX_PROP_VGA_H_SYNC 27
+#define UDI_GFX_PROP_VGA_V_FRONT_PORCH 28
+#define UDI_GFX_PROP_VGA_V_BACK_PORCH 29
+#define UDI_GFX_PROP_VGA_V_SYNC 30
+#define UDI_GFX_PROP_DOT_CLOCK 31
+#define UDI_GFX_PROP_VGA_H_SYNC_POL 32
+#define UDI_GFX_PROP_VGA_V_SYNC_POL 33
+
+/**
+ * Enumeration: UDI_GFX_SIGNAL
+ * Lists the various signal types
+ */
+#define UDI_GFX_SIGNAL_HIDDEN 0
+#define UDI_GFX_SIGNAL_INTEGRATED 0
+#define UDI_GFX_SIGNAL_RGBHV 1
+#define UDI_GFX_SIGNAL_RGBS 2
+#define UDI_GFX_SIGNAL_RGSB 3
+#define UDI_GFX_SIGNAL_YPBPR 4
+#define UDI_GFX_SIGNAL_DVID 5
+#define UDI_GFX_SIGNAL_YUV 6
+#define UDI_GFX_SIGNAL_YIQ 7
+#define UDI_GFX_SIGNAL_Y_UV 8
+#define UDI_GFX_SIGNAL_Y_IQ 9
+#define UDI_GFX_SIGNAL_HDMI 10
+#define UDI_GFX_SIGNAL_TEXT 11
+#define UDI_GFX_SIGNAL_CUSTOM 12
+
+/**
+ * Enumeration: UDI_GFX_CONNECTOR
+ * Lists the various external connectors
+ */
+#define UDI_GFX_CONNECTOR_HIDDEN 0
+#define UDI_GFX_CONNECTOR_VGA 1
+#define UDI_GFX_CONNECTOR_DVI 2
+#define UDI_GFX_CONNECTOR_SVIDEO 3
+#define UDI_GFX_CONNECTOR_COMPONENT 4
+#define UDI_GFX_CONNECTOR_HDMI 5
+#define UDI_GFX_CONNECTOR_RF 6
+#define UDI_GFX_CONNECTOR_SCART 7
+#define UDI_GFX_CONNECTOR_COMPOSITE 8
+#define UDI_GFX_CONNECTOR_MEMBUFFER 9
+
+/**
+ * Enumeration: UDI_GFX_OPERATOR
+ * Lists the display output operator
+ */
+#define UDI_GFX_OPERATOR_RGB     0 // output = (color) red(a1) + green(a2) + blue(a3) (each component is UDI_GFX_PROP_COLOR_BITS
+#define UDI_GFX_OPERATOR_YUV     1 // output = (color) Y(a1) + U(a2) + V(a3)
+#define UDI_GFX_OPERATOR_YIQ     2 // output = (color) Y(a1) + I(a2) + Q(a3)
+#define UDI_GFX_OPERATOR_I       3 // output = (color) intensity(a1)
+#define UDI_GFX_OPERATOR_ALPHA   4 // output = (color) a1 + alpha(a2)
+#define UDI_GFX_OPERATOR_ADD     5 // output = a1 + a2 + v3
+#define UDI_GFX_OPERATOR_SUB     6 // output = a1 - a2 - v3
+#define UDI_GFX_OPERATOR_MUL     7 // output = a1 * a2
+#define UDI_GFX_OPERATOR_DIV     8 // output = a1 / a2
+#define UDI_GFX_OPERATOR_MAD     9 // output = a1 * a2 + a3
+#define UDI_GFX_OPERATOR_FRC    10 // output = (a1 * a2) / a3
+#define UDI_GFX_OPERATOR_SHR    11 // output = a1 >> (a2 + v3)
+#define UDI_GFX_OPERATOR_SHL    12 // output = a1 << (a2 + v3)
+#define UDI_GFX_OPERATOR_ROR    13 // output = a1 >> a2 (over a3 bits)
+#define UDI_GFX_OPERATOR_ROL    14 // output = a1 << a2 (over a3 bits)
+#define UDI_GFX_OPERATOR_SAR    15 // output = a1 >> a2 (width is a3 bits, i.e. empties are filled with bit a3-1)
+#define UDI_GFX_OPERATOR_SAL    16 // output = a1 <<< (a2 + v3) (empties filled with bit 0)
+#define UDI_GFX_OPERATOR_AND    17 // output = a1 & a2
+#define UDI_GFX_OPERATOR_OR     18 // output = a1 | a2 | v3
+#define UDI_GFX_OPERATOR_NOT    19 // output = ~a1
+#define UDI_GFX_OPERATOR_XOR    20 // output = a1 ^ a2 ^ v3
+#define UDI_GFX_OPERATOR_NEG    21 // output = -a1
+#define UDI_GFX_OPERATOR_SEG    22 // output = (a1 >> v2) & (2**v3-1) (select v3 bits starting from bit v2)
+#define UDI_GFX_OPERATOR_RANGE  23 // output = (a1 > a2) ? a2 : ((a1 < a3) ? a3 : a1)
+#define UDI_GFX_OPERATOR_CONST  24 // output = v1
+#define UDI_GFX_OPERATOR_ATTR   25 // output = property[a1 + v2]
+#define UDI_GFX_OPERATOR_SWITCH 26 // output = output[(a1 % v3) + v2]
+#define UDI_GFX_OPERATOR_BUFFER 27 // output = buffer[a1][a2] (buffer is v3 bits per entry)
+#define UDI_GFX_OPERATOR_X      28 // output = output x pixel
+#define UDI_GFX_OPERATOR_Y      29 // output = output y pixel
+#define UDI_GFX_OPERATOR_TX     30 // output = horizontal tile index belonging to output pixel
+#define UDI_GFX_OPERATOR_TY     31 // output = vertical tile index belonging to output pixel
+#define UDI_GFX_OPERATOR_TXOFF  32 // output = horizontal offset from start of tile
+#define UDI_GFX_OPERATOR_TYOFF  33 // output = vertical offset from start of tile
+#define UDI_GFX_OPERATOR_INPUT  34 // output = input engine[x][y]   component v1
+#define UDI_GFX_OPERATOR_DINPUT 35 // output = input engine[a1][a2] component v3
+
+/* Enumeration: UDI_GFX_STOCK_FORMAT
+ * Lists stock configurations
+ *
+ * When a stock configuration is used, the device is set to behave as a 
+ * simple framebuffer device. The <UDI_GFX_PROP_WIDTH> and <UDI_GFX_PROP_HEIGHT>
+ * determine the virtual size of the framebuffer, and <UDI_GFX_PROP_TRANSLATEX>
+ * and <UDI_GFX_PROP_TRANSLATEY> indicate the offset into that framebuffer 
+ * that is visible (which are typically restricted to negative values)
+ */
+#define UDI_GFX_STOCK_FORMAT_UNKNOWN  0
+#define UDI_GFX_STOCK_FORMAT_R8G8B8X8 1
+#define UDI_GFX_STOCK_FORMAT_B8G8R8X8 2
+#define UDI_GFX_STOCK_FORMAT_R8G8B8   3
+#define UDI_GFX_STOCK_FORMAT_B8G8R8   4
+#define UDI_GFX_STOCK_FORMAT_R5G6B5   5
+#define UDI_GFX_STOCK_FORMAT_B5G6R5   6
+#define UDI_GFX_STOCK_FORMAT_R5G5B5X1 7
+#define UDI_GFX_STOCK_FORMAT_B5G5R5X1 8
+#define UDI_GFX_STOCK_FORMAT_N8 9
+
+// Enumeration: UDI_GFX_BUFFER_INFO_FLAG
+// Lists behavioural patterns for direct buffer accesses.
+//
+#define UDI_GFX_BUFFER_INFO_FLAG_R              0x0001  // buffer can be read
+#define UDI_GFX_BUFFER_INFO_FLAG_W              0x0002  // buffer can be written
+#define UDI_GFX_BUFFER_INFO_FLAG_BITALIGN_ENTRY 0x0004  // for non-multiple-of-eight buffer slot sizes, align on byte boundary every unit
+#define UDI_GFX_BUFFER_INFO_FLAG_BITALIGN_ROW   0x0008  // for non-multiple-of-eight buffer slot sizes, align only the start of the row
+
+
+// Constant: UDI_GFX_PROVIDER_OPS_NUM
+// the ops number used for the graphics driver
+#define UDI_GFX_PROVIDER_OPS_NUM 1
+
+// Constant: UDI_GFX_CLIENT_OPS_NUM
+// the ops number used for the graphics application
+#define UDI_GFX_CLIENT_OPS_NUM 2
+
+// Structure: udi_gfx_bind_cb_t
+// Contains the operations of a driver binding request
+typedef struct {
+    // Variable: gcb
+    // The main control block
+    udi_cb_t gcb;    
+} udi_gfx_bind_cb_t;
+#define UDI_GFX_BIND_CB_NUM 1
+
+// Function: udi_block_bind_req
+// function pointer prototype for connecting to a block device
+// 
+// in:
+//     cb - A pointer to a <udi_block_bind_cb_t>
+//
+typedef void udi_gfx_bind_req_op_t (udi_gfx_bind_cb_t *cb );
+udi_gfx_bind_req_op_t udi_gfx_bind_req;
+
+// Function: udi_gfx_bind_ack
+// function pointer prototype for acknowledging a connection request
+// 
+// in:
+//     cb      - A pointer to a <udi_gfx_bind_cb_t>
+//     sockets - The number of addressable socket components
+//     engines - The number of addressable engine components
+//     status  - The result of the bind operation
+//
+typedef void udi_gfx_bind_ack_op_t (udi_gfx_bind_cb_t *cb, udi_index_t sockets, udi_index_t engines, udi_status_t status );
+udi_gfx_bind_ack_op_t udi_gfx_bind_ack;
+
+// Function: udi_gfx_unbind_req
+// function pointer prototype for disconnecting a block device
+// 
+// in:
+//     cb - A pointer to a <udi_block_bind_cb_t>
+//
+typedef void udi_gfx_unbind_req_op_t (udi_gfx_bind_cb_t *cb );
+udi_gfx_unbind_req_op_t udi_gfx_unbind_req;
+
+// Function: udi_gfx_unbind_ack
+// function pointer prototype for connecting to a block device
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_bind_cb_t>
+//
+typedef void udi_gfx_unbind_ack_op_t (udi_gfx_bind_cb_t *cb );
+udi_gfx_unbind_ack_op_t udi_gfx_unbind_ack;
+
+// Structure: udi_gfx_state_cb_t
+// Contains the operations of a read/write transaction
+typedef struct {
+    // Variable: gcb
+    // The main control block
+    udi_cb_t gcb;    
+    udi_ubit32_t subsystem;
+    udi_ubit32_t attribute;
+} udi_gfx_state_cb_t;
+#define UDI_GFX_STATE_CB_NUM 2
+
+// Function: udi_gfx_set_engine_req
+// function pointer prototype for setting an engine state
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_set_engine_req_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_set_engine_req_op_t udi_gfx_set_engine_req;
+
+// Function: udi_gfx_set_connector_req
+// function pointer prototype for setting an connector state
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_set_connector_req_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_set_connector_req_op_t udi_gfx_set_connector_req;
+
+// Function: udi_gfx_set_engine_ack
+// function pointer prototype for setting an engine state
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_set_engine_ack_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_set_engine_ack_op_t udi_gfx_set_engine_ack;
+
+// Function: udi_gfx_set_connector_ack
+// function pointer prototype for setting an engine state
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_set_connector_ack_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_set_connector_ack_op_t udi_gfx_set_connector_ack;
+
+// Function: udi_gfx_get_engine_req
+// function pointer prototype for setting an engine state
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_get_engine_req_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_get_engine_req_op_t udi_gfx_get_engine_req;
+
+// Function: udi_gfx_get_connector_req
+// function pointer prototype for setting an connector state
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_get_connector_req_op_t (udi_gfx_state_cb_t *cb );
+udi_gfx_get_connector_req_op_t udi_gfx_get_connector_req;
+
+// Function: udi_gfx_get_engine_ack
+// function pointer prototype for setting an engine state
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_get_engine_ack_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_get_engine_ack_op_t udi_gfx_get_engine_ack;
+
+// Function: udi_gfx_get_connector_ack
+// function pointer prototype for setting an engine state
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_get_connector_ack_op_t (udi_gfx_state_cb_t *cb, udi_ubit32_t value);
+udi_gfx_get_connector_ack_op_t udi_gfx_get_connector_ack;
+
+// Function: udi_gfx_set_engine_nak
+// function pointer prototype for setting an engine state
+// 
+// in:
+//     cb     - A pointer to a <udi_gfx_state_cb_t>
+//     status - An UDI status value indicative of the error
+//
+typedef void udi_gfx_set_engine_nak_op_t (udi_gfx_state_cb_t *cb, udi_status_t status);
+udi_gfx_set_engine_nak_op_t udi_gfx_set_engine_nak;
+
+// Function: udi_gfx_set_connector_nak
+// function pointer prototype for setting an engine state
+// 
+// in:
+//     cb     - A pointer to a <udi_gfx_state_cb_t>
+//     status - An UDI status value indicative of the error
+//
+typedef void udi_gfx_set_connector_nak_op_t (udi_gfx_state_cb_t *cb, udi_status_t status);
+udi_gfx_set_connector_nak_op_t udi_gfx_get_connector_nak;
+
+// Structure: udi_gfx_range_cb_t
+// Contains the operations of a range request transaction
+typedef struct {
+    // Variable: gcb
+    // The main control block
+    udi_cb_t gcb;    
+    udi_ubit32_t subsystem;
+    udi_ubit32_t attribute;
+    udi_buf_t * rangedata;  
+} udi_gfx_range_cb_t;
+#define UDI_GFX_RANGE_CB_NUM 3
+
+// Function: udi_gfx_range_engine_req
+// function pointer prototype for getting an engine property range
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_range_cb_t>
+//
+typedef void udi_gfx_range_engine_req_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_engine_req_op_t udi_gfx_range_engine_req;
+
+// Function: udi_gfx_range_connector_req
+// function pointer prototype for getting a connector property range
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_range_cb_t>
+//
+typedef void udi_gfx_range_connector_req_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_connector_req_op_t udi_gfx_range_connector_req;
+
+// Function: udi_gfx_range_engine_ack
+// function pointer prototype for replying an engine property range
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_range_cb_t>
+//
+typedef void udi_gfx_range_engine_ack_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_engine_ack_op_t udi_gfx_range_engine_ack;
+
+// Function: udi_gfx_range_connector_ack
+// function pointer prototype for replying a connector property range
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_range_cb_t>
+//
+typedef void udi_gfx_range_connector_ack_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_range_connector_ack_op_t udi_gfx_range_connector_ack;
+
+// Function: udi_gfx_get_engine_operator_req
+// function pointer prototype for requesting the engine operator layout
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_state_cb_t>
+//
+typedef void udi_gfx_get_engine_operator_req_op_t (udi_gfx_range_cb_t *cb );
+udi_gfx_get_engine_operator_req_op_t udi_gfx_get_engine_operator_req;
+
+// Function: udi_gfx_get_engine_operator_ack
+// function pointer prototype for replying the engine operator layout
+// 
+// in:
+//     cb   - A pointer to a <udi_gfx_state_cb_t>
+//     op   - The operator performed at this index
+//     arg1 - the first argument to this operator
+//     arg2 - the second argument to this operator
+//     arg3 - the third argument to this operator
+//
+typedef void udi_gfx_get_engine_operator_ack_op_t (udi_gfx_range_cb_t *cb, udi_ubit32_t op, udi_ubit32_t arg1, udi_ubit32_t arg2, udi_ubit32_t arg3 );
+udi_gfx_get_engine_operator_ack_op_t udi_gfx_get_engine_operator_ack;
+
+
+
+// Structure: udi_gfx_command_cb_t
+// Contains the operations of a command sequence
+typedef struct {
+    // Variable: gcb
+    // The main control block
+    udi_cb_t gcb;    
+    udi_buf_t * commanddata;
+} udi_gfx_command_cb_t;
+#define UDI_GFX_COMMAND_CB_NUM 4
+
+// Function: udi_gfx_connector_command_req
+// function pointer prototype for sending command data to the output connector
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_command_cb_t>
+//
+typedef void udi_gfx_connector_command_req_op_t (udi_gfx_command_cb_t *cb );
+udi_gfx_connector_command_req_op_t udi_gfx_connector_command_req;
+
+// Function: udi_gfx_engine_command_req
+// function pointer prototype for sending command data to the engine
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_command_cb_t>
+//
+typedef void udi_gfx_engine_command_req_op_t (udi_gfx_command_cb_t *cb );
+udi_gfx_engine_command_req_op_t udi_gfx_engine_command_req;
+
+// Function: udi_gfx_connector_command_ack
+// function pointer prototype for sending command data replies
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_command_cb_t>
+//
+typedef void udi_gfx_connector_command_ack_op_t (udi_gfx_command_cb_t *cb);
+udi_gfx_connector_command_ack_op_t udi_gfx_connector_command_ack;
+
+// Function: udi_gfx_engine_command_ack
+// function pointer prototype for sending engine data replies
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_command_cb_t>
+//
+typedef void udi_gfx_engine_command_ack_op_t (udi_gfx_command_cb_t *cb);
+udi_gfx_engine_command_ack_op_t udi_gfx_engine_command_ack;
+
+// Structure: udi_gfx_buffer_cb_t
+// Contains a description of a buffer, or area thereof
+typedef struct {
+    // Variable: gcb
+    // The main control block
+    udi_cb_t gcb;    
+    udi_ubit32_t buffer_index;
+} udi_gfx_buffer_info_cb_t;
+
+// Function: udi_gfx_buffer_info_req
+// function pointer prototype for getting buffer configuration information
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_command_cb_t>
+//
+typedef void udi_gfx_buffer_info_req_op_t (udi_gfx_buffer_info_cb_t *cb);
+udi_gfx_buffer_info_req_op_t udi_gfx_buffer_info_req;
+
+// Function: udi_gfx_buffer_info_ack
+// function pointer prototype for getting buffer configuration information
+// 
+// in:
+//     cb       - A pointer to a <udi_gfx_command_cb_t>
+//     width    - The width of the buffer
+//     height   - The height of the buffer
+//     bitsper  - The number of bits read from the buffer per pixel unit
+//     flags    - A bitfield of <UDI_GFX_BUFFER_FLAGS> indicating the exposed 
+//                capabilities of this buffer
+//
+// Note that bitsper might not be a multiple of eight.
+//
+typedef void udi_gfx_buffer_info_ack_op_t (udi_gfx_buffer_info_cb_t *cb, udi_ubit32_t width, udi_ubit32_t height, udi_ubit32_t bitsper, udi_ubit32_t flags);
+udi_gfx_buffer_info_ack_op_t udi_gfx_buffer_info_ack;
+
+// Structure: udi_gfx_buffer_cb_t
+// Contains a description of a buffer, or area thereof
+typedef struct {
+    // Variable: gcb
+    // The main control block
+    udi_cb_t gcb;    
+    udi_ubit32_t buffer_index;
+    udi_ubit32_t x;
+    udi_ubit32_t y;
+    udi_ubit32_t width;
+    udi_ubit32_t height;
+    udi_buf_t * buffer;
+} udi_gfx_buffer_cb_t;
+
+// Function: udi_gfx_buffer_write_req_op_t
+// function pointer prototype for writing raw hardware buffers
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_buffer_cb_t>
+//
+typedef void udi_gfx_buffer_write_req_op_t (udi_gfx_buffer_cb_t *cb);
+udi_gfx_buffer_write_req_op_t udi_gfx_buffer_write_req;
+
+// Function: udi_gfx_buffer_write_req_op_t
+// function pointer prototype for reading raw hardware buffers
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_buffer_cb_t>
+//
+typedef void udi_gfx_buffer_read_req_op_t (udi_gfx_buffer_cb_t *cb);
+udi_gfx_buffer_read_req_op_t udi_gfx_buffer_read_req;
+
+// Function: udi_gfx_buffer_write_ack_op_t
+// function pointer prototype for writing raw hardware buffers
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_buffer_cb_t>
+//
+typedef void udi_gfx_buffer_write_ack_op_t (udi_gfx_buffer_cb_t *cb);
+udi_gfx_buffer_write_ack_op_t udi_gfx_buffer_write_ack;
+
+// Function: udi_gfx_buffer_write_ack_op_t
+// function pointer prototype for reading raw hardware buffers
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_buffer_cb_t>
+//
+typedef void udi_gfx_buffer_read_ack_op_t (udi_gfx_buffer_cb_t *cb);
+udi_gfx_buffer_read_ack_op_t udi_gfx_buffer_read_ack;
+
+// Function: udi_gfx_buffer_write_nak_op_t
+// error handling for buffer writes
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_buffer_cb_t>
+//
+typedef void udi_gfx_buffer_write_nak_op_t (udi_gfx_buffer_cb_t *cb, udi_status_t status);
+udi_gfx_buffer_write_nak_op_t udi_gfx_buffer_write_nak;
+
+// Function: udi_gfx_buffer_write_nak_op_t
+// error handling for buffer reads
+// 
+// in:
+//     cb - A pointer to a <udi_gfx_buffer_cb_t>
+//
+typedef void udi_gfx_buffer_read_nak_op_t (udi_gfx_buffer_cb_t *cb, udi_status_t status);
+udi_gfx_buffer_read_nak_op_t udi_gfx_buffer_read_nak;
+
+/* Structure: udi_gfx_provider_ops_t
+ * 
+ * The graphics metalanguage entry points (provider side)
+ */
+typedef const struct {
+    udi_channel_event_ind_op_t          *channel_event_ind_op;
+    udi_gfx_bind_req_op_t               *gfx_bind_req_op;
+    udi_gfx_unbind_req_op_t             *gfx_unbind_req_op;
+    udi_gfx_set_connector_req_op_t      *gfx_set_connector_req_op;
+    udi_gfx_set_engine_req_op_t         *gfx_set_engine_req_op;
+    udi_gfx_get_connector_req_op_t      *gfx_get_connector_req_op;
+    udi_gfx_get_engine_req_op_t         *gfx_get_engine_req_op;
+    udi_gfx_range_connector_req_op_t    *gfx_range_connector_req_op;
+    udi_gfx_range_engine_req_op_t       *gfx_range_engine_req_op;
+    udi_gfx_get_engine_operator_req_op_t*gfx_get_engine_operator_req_op;
+    udi_gfx_connector_command_req_op_t  *gfx_connector_command_req_op;
+    udi_gfx_engine_command_req_op_t     *gfx_engine_command_req_op;
+    udi_gfx_buffer_info_req_op_t        *gfx_buffer_info_req_op;
+    udi_gfx_buffer_read_req_op_t        *gfx_buffer_read_req_op;
+    udi_gfx_buffer_write_req_op_t       *gfx_buffer_write_req_op;
+} udi_gfx_provider_ops_t;
+
+/* Structure: udi_gfx_client_ops_t
+ *
+ * The graphics metalanguage entry points (client side)
+ */
+typedef const struct {
+    udi_channel_event_ind_op_t          *channel_event_ind_op;
+    udi_gfx_bind_ack_op_t               *gfx_bind_ack_op;
+    udi_gfx_unbind_ack_op_t             *gfx_unbind_ack_op;
+    udi_gfx_set_connector_ack_op_t      *gfx_set_connector_ack_op;
+    udi_gfx_set_engine_ack_op_t         *gfx_set_engine_ack_op;
+    udi_gfx_set_connector_nak_op_t      *gfx_set_connector_nak_op;
+    udi_gfx_set_engine_nak_op_t         *gfx_set_engine_nak_op;
+    udi_gfx_get_connector_ack_op_t      *gfx_get_connector_ack_op;
+    udi_gfx_get_engine_ack_op_t         *gfx_get_engine_ack_op;
+    udi_gfx_range_connector_ack_op_t    *gfx_range_connector_ack_op;
+    udi_gfx_range_engine_ack_op_t       *gfx_range_engine_ack_op;
+    udi_gfx_get_engine_operator_req_op_t*gfx_get_engine_operator_ack_op;
+    udi_gfx_connector_command_ack_op_t  *gfx_connector_command_ack_op;
+    udi_gfx_engine_command_ack_op_t     *gfx_engine_command_ack_op;
+    udi_gfx_buffer_info_ack_op_t        *gfx_buffer_info_ack_op;
+    udi_gfx_buffer_read_ack_op_t        *gfx_buffer_read_ack_op;
+    udi_gfx_buffer_write_ack_op_t       *gfx_buffer_write_ack_op;
+    udi_gfx_buffer_read_nak_op_t        *gfx_buffer_read_nak_op;
+    udi_gfx_buffer_write_nak_op_t       *gfx_buffer_write_nak_op;
+} udi_gfx_client_ops_t;
+
+
+// temporary
+#ifndef UDI_ANNOY_ME
+void EngineReturnSimpleRange (int source, int index, int prop, int first, int last, int modulus);
+void ConnectorReturnSimpleRange (int source, int index, int prop, int first, int last, int modulus);
+void EngineReturnConstantRange (int source, int index, int prop, int value);
+void ConnectorReturnConstantRange (int source, int index, int prop, int value);
+void EngineReturnBooleanRange (int source, int index, int prop, int value1, int value2);
+void ConnectorReturnBooleanRange (int source, int index, int prop, int value1, int value2);
+#endif
+
+#endif
diff --git a/UDI/include/udi_nic.h b/UDI/include/udi_nic.h
index 169a81bfa6566e653b78c1d42b5f6da339005372..7b5659a9226f0968c5e3e28011ae92ecb6e2e0a0 100644
--- a/UDI/include/udi_nic.h
+++ b/UDI/include/udi_nic.h
@@ -8,6 +8,10 @@
 #ifndef _UDI_NIC_H_
 #define _UDI_NIC_H_
 
+#ifndef UDI_NIC_VERSION
+# error "UDI_NIC_VERSION must be defined"
+#endif
+
 // === CBs ===
 #define UDI_NIC_STD_CB_NUM	1
 #define UDI_NIC_BIND_CB_NUM	2
diff --git a/UDI/include/udi_pci.h b/UDI/include/udi_pci.h
index f65c6fd5b763eb80ffb0a06672205f8ba47844a5..16e54a178328ce6939d30227759ff94dd9016d10 100644
--- a/UDI/include/udi_pci.h
+++ b/UDI/include/udi_pci.h
@@ -8,6 +8,9 @@
 #ifndef _UDI_PCI_H_
 #define _UDI_PCI_H_
 
+#if UDI_PCI_VERSION != 0x101
+# error "udi_pci.h requires UDI_PCI_VERSION set to 0x101"
+#endif
 #ifndef _UDI_PHYSIO_H_
 # error "udi_pci.h requires udi_physio.h"
 #endif
diff --git a/UDI/include/udi_physio.h b/UDI/include/udi_physio.h
index f7643c8e59ef76b01fea614baf05df322c27ea08..7023687b5f695ff26de7d0e66167ee91a49410cc 100644
--- a/UDI/include/udi_physio.h
+++ b/UDI/include/udi_physio.h
@@ -6,9 +6,9 @@
 
 #include <udi.h>
 
-//#ifndef UDI_PHYSIO_VERSION
-//# error "UDI_PHYSIO_VERSION must be defined"
-//#endif
+#ifndef UDI_PHYSIO_VERSION
+# error "UDI_PHYSIO_VERSION must be defined"
+#endif
 
 #define UDI_DL_PIO_HANDLE_T	200
 #define UDI_DL_DMA_CONSTRAINTS_T	201
@@ -128,6 +128,4 @@ struct udi_scgth_s
 #include <physio/meta_bus.h>
 #include "physio/pio.h"
 
-#include "physio/pci.h"
-
 #endif
diff --git a/Usermode/Applications/CLIShell_src/Makefile b/Usermode/Applications/CLIShell_src/Makefile
index e500edd273ba4d799fbb90b5a8dd3d4804a595e2..76d8f5736ecd9a19320d7a2a83a0599e515acd15 100644
--- a/Usermode/Applications/CLIShell_src/Makefile
+++ b/Usermode/Applications/CLIShell_src/Makefile
@@ -3,7 +3,8 @@
 -include ../Makefile.cfg
 
 CPPFLAGS += -I./include
-LDFLAGS  += -lreadline
+LDFLAGS  +=
+LIBS     += -lreadline
 
 BIN = CLIShell
 OBJ = main.o lib.o
diff --git a/Usermode/Applications/Makefile.cfg b/Usermode/Applications/Makefile.cfg
index ef17a140303105d0f30089e11fa8d87c7546b2b0..be17100fffd8f08f328445844ce62490fce7f4f2 100644
--- a/Usermode/Applications/Makefile.cfg
+++ b/Usermode/Applications/Makefile.cfg
@@ -7,25 +7,24 @@ include $(_appsdir)../Makefile.cfg
 
 ifeq ($(ARCHDIR),native)
  ASFLAGS = -felf
- CPPFLAGS = -Wall
- CFLAGS = $(CPPFLAGS)
- LDFLAGS = -L $(OUTPUTDIR)Libs -lacess-native -lc_acess
-#LIBGCC_PATH = $(ACESSDIR)/AcessNative/symbol_renames.ld
+ LDFLAGS :=
+ LIBS := -lacess-native -lc_acess
 else
  ASFLAGS = -felf
- CPPFLAGS = -ffreestanding
- CFLAGS   = -fno-stack-protector -fno-builtin $(CPPFLAGS) -Wall
- LDFLAGS  = -T $(OUTPUTDIR)Libs/acess.ld -L $(OUTPUTDIR)Libs -I /Acess/Libs/ld-acess.so -lld-acess -lc $(OUTPUTDIR)Libs/crtbegin.o $(OUTPUTDIR)Libs/crtend.o -lposix
- LIBGCC_PATH = $(shell $(CC) -print-libgcc-file-name)
+ LDFLAGS :=
+ LIBS = -lld-acess
 endif
 
+LDFLAGS += -rpath-link $(OUTPUTDIR)Libs	# Needed so that dynamic libraries are linked correctly
+CXXFLAGS += -std=gnu++11
+CPPFLAGS +=
+CFLAGS   += -Wall
+
 -include $(_appsdir)../common_settings.mk
-LDFLAGS += -rpath-link $(OUTPUTDIR)Libs
 
 # Extra-verbose errors!
 #CFLAGS += -Wall -Wextra -Wwrite-strings -Wshadow -Wswitch-default -Wswitch-enum -Wstrict-overflow=5 -Wfloat-equal -Wundef -Wmissing-declarations -Wlogical-op  -Wformat=2 -Winit-self -Wmissing-include-dirs -Wswitch-default -Wswitch-enum -Wsync-nand -Wunused -Wstrict-overflow=5 -Wfloat-equal -Wundef -Wno-endif-labels -Wshadow -Wunsafe-loop-optimizations -Wbad-function-cast -Wc++-compat -Wcast-qual -Wcast-align -Wwrite-strings -Wconversion -Wlogical-op -Waggregate-return -Wstrict-prototypes -Wold-style-definition -Wmissing-declarations -Wnormalized=nfc -Wpacked -Wpadded -Wredundant-decls -Wnested-externs -Winline -Winvalid-pch -Wdisabled-optimization -Woverlength-strings
 
-CRTBEGIN = $(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o)
-CRTEND = $(shell $(CC) $(CFLAGS) -print-file-name=crtend.o)
-
 DIR = Bin
+
+# vim: ft=make
diff --git a/Usermode/Applications/Makefile.tpl b/Usermode/Applications/Makefile.tpl
index a6c8508c4ce379df754d7e9569deb4b04ca230e8..d72ac6a24cfc32889454b33f6a9933f5c6e68321 100644
--- a/Usermode/Applications/Makefile.tpl
+++ b/Usermode/Applications/Makefile.tpl
@@ -3,23 +3,26 @@
 # - Application Template Makefile
 #
 
-CFLAGS  += -g
-LDFLAGS += -g
-
-LDFLAGS += -Map $(_OBJPREFIX)Map.txt
-
-ifneq ($(lastword $(subst -, ,$(basename $(LD)))),ld)
-  comma=,
-  LDFLAGS := $(subst -rpath-link ,-Wl$(comma)-rpath-link$(comma),$(LDFLAGS))
-  LDFLAGS := $(subst -Map ,-Wl$(comma)-Map$(comma),$(LDFLAGS))
-endif
+CFLAGS   += -g
+CXXFLAGS += -g
+LDFLAGS  += -g
 
 _BIN := $(OUTPUTDIR)$(DIR)/$(BIN)
 _OBJPREFIX := obj-$(ARCH)/
 
-_LIBS := $(filter -l%,$(LDFLAGS))
+LDFLAGS += -Map $(_OBJPREFIX)Map.txt
+
+comma=,
+LDFLAGS := $(subst -rpath-link ,-Wl$(comma)-rpath-link$(comma),$(LDFLAGS))
+LDFLAGS := $(subst -Map ,-Wl$(comma)-Map$(comma),$(LDFLAGS))
+
+_LIBS := $(filter -l%,$(LIBS))
 _LIBS := $(patsubst -l%,$(OUTPUTDIR)Libs/lib%.so,$(_LIBS))
 
+ifeq ($(ARCHDIR),native)
+ LDFLAGS := $(patsubst -lc++,-lc++_acess,$(LDFLAGS))
+ LIBS := $(patsubst -lc++,-lc++_acess,$(LIBS))
+endif
 ifeq ($(VERBOSE),)
 V := @
 else
@@ -28,6 +31,9 @@ endif
 
 OBJ := $(addprefix $(_OBJPREFIX),$(OBJ))
 
+#LINK_OBJS := $(CRTI) $(CRTBEGIN) $(CRT0) $(OBJ) $(LIBGCC_PATH) $(CRTEND) $(CRTN)
+LINK_OBJS := $(OBJ)
+
 DEPFILES := $(OBJ:%.o=%.dep)
 
 .PHONY : all clean install
@@ -45,10 +51,14 @@ install: $(_BIN)
 	@$(xCP) $(_BIN)_ $(DISTROOT)/$(DIR)/$(BIN)
 	@$(RM) $(_BIN)_
 
-$(_BIN): $(OUTPUTDIR)Libs/acess.ld $(OUTPUTDIR)Libs/crt0.o $(_LIBS) $(OBJ)
+$(_BIN): $(_LIBS) $(LINK_OBJS) $(CRT0) $(CRTI) $(CRTN)
 	@mkdir -p $(dir $(_BIN))
 	@echo [LD] -o $@
-	$V$(LD) -g $(LDFLAGS) -o $@ $(CRTBEGIN) $(OBJ) $(LIBGCC_PATH) $(CRTEND)
+ifneq ($(USE_CXX_LINK),)
+	$V$(CXX) -g $(LDFLAGS) -o $(_BIN) $(LINK_OBJS) $(LIBS)
+else
+	$V$(CC)  -g $(LDFLAGS) -o $(_BIN) $(LINK_OBJS) $(LIBS)
+endif
 	$V$(DISASM) $(_BIN) > $(_OBJPREFIX)$(BIN).dsm
 
 $(_OBJPREFIX)%.o: %.c
@@ -56,8 +66,7 @@ $(_OBJPREFIX)%.o: %.c
 ifneq ($(_OBJPREFIX),)
 	@mkdir -p $(dir $@)
 endif
-	$V$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
-	$V$(CC) -M -MP -MT $@ $(CPPFLAGS) $< -o $(_OBJPREFIX)$*.dep
+	$V$(CC)  $(CFLAGS)   $(CPPFLAGS) -c $< -o $@ -MQ $@ -MP -MD -MF $(_OBJPREFIX)$*.dep
 
 $(_OBJPREFIX)%.o: %.cpp
 	@echo [CXX] -o $@
diff --git a/Usermode/Applications/axwin3_src/Interface/Makefile b/Usermode/Applications/axwin3_src/Interface/Makefile
index d96cc5e9bbb92ef2bb9fc93edc0899b60f3c82c0..d5303649c0055f66a0b1bee11b1d9759f88efaea 100644
--- a/Usermode/Applications/axwin3_src/Interface/Makefile
+++ b/Usermode/Applications/axwin3_src/Interface/Makefile
@@ -8,7 +8,7 @@ DIR := Apps/AxWin/3.0
 BIN := AxWinUI
 OBJ := main.o
 
-LDFLAGS += -laxwin3
+LIBS += -laxwin3
 
 -include ../../Makefile.tpl
 
diff --git a/Usermode/Applications/axwin3_src/WM/Makefile b/Usermode/Applications/axwin3_src/WM/Makefile
index 5854a3959e1e923a5e7f1f0b85b6e481355bb56f..595044db101f2c92d21ac910542891daf6856ce4 100644
--- a/Usermode/Applications/axwin3_src/WM/Makefile
+++ b/Usermode/Applications/axwin3_src/WM/Makefile
@@ -8,7 +8,7 @@ BIN := AxWinWM
 OBJ := main.o input.o video.o ipc_acess.o
 include common.mk
 
-LDFLAGS += -lnet
+LIBS += -lnet
 
 -include ../../Makefile.tpl
 
diff --git a/Usermode/Applications/axwin3_src/WM/common.mk b/Usermode/Applications/axwin3_src/WM/common.mk
index e354e0cf132285c4b86ca3a69fc3f514a67d105b..81ab3279ace1b23f8da05680f22982336b8c66f6 100644
--- a/Usermode/Applications/axwin3_src/WM/common.mk
+++ b/Usermode/Applications/axwin3_src/WM/common.mk
@@ -18,7 +18,7 @@ OBJ += renderers/widget/textinput.o
 OBJ += renderers/widget/spacer.o
 OBJ += renderers/widget/subwin.o
 
-LDFLAGS += -limage_sif -luri -lunicode
+LIBS += -limage_sif -luri -lunicode
 
 PNGIMAGES := toolbar_new.png toolbar_save.png toolbar_open.png
 IMG2SIF = ../../../../Tools/img2sif
diff --git a/Usermode/Applications/axwin3_src/WM/renderers/widget/common.h b/Usermode/Applications/axwin3_src/WM/renderers/widget/common.h
index 2564716e48907b368f4db1911b064350146eef87..9c87bde11265a2654fa458a29e87ec1bcbf6d074 100644
--- a/Usermode/Applications/axwin3_src/WM/renderers/widget/common.h
+++ b/Usermode/Applications/axwin3_src/WM/renderers/widget/common.h
@@ -49,7 +49,7 @@ extern void	Widget_Fire(tElement *Element);
 #define DEFWIDGETTYPE(_type, _name, _flags, _attribs...) \
 tWidgetDef	_widget_typedef_##_type = {.Name=_name,.Flags=(_flags),_attribs};\
 void _widget_set_##_type(void) __attribute__((constructor));\
-void _widget_set_##_type(void) { _SysDebug("hai!\n"); Widget_int_SetTypeDef(_type, &_widget_typedef_##_type);}
+void _widget_set_##_type(void) { Widget_int_SetTypeDef(_type, &_widget_typedef_##_type); }
 
 #endif
 
diff --git a/Usermode/Applications/axwin3_src/WM/renderers/widget/textinput.c b/Usermode/Applications/axwin3_src/WM/renderers/widget/textinput.c
index 8335241707778e6df3f4794bf2e5b8f104e592da..de862bfe402efd0037fa21126e472e59343cc9f3 100644
--- a/Usermode/Applications/axwin3_src/WM/renderers/widget/textinput.c
+++ b/Usermode/Applications/axwin3_src/WM/renderers/widget/textinput.c
@@ -118,7 +118,7 @@ void Widget_TextInput_Init(tElement *Element)
 	info->DrawOfs = 0;
 	info->CursorXOfs = 0;
 	info->CursorByteOfs = 0;
-	info->Length = NULL;
+	info->Length = 0;
 
 	// No need to explicitly update parent min dims, as the AddElement routine does that	
 }
diff --git a/Usermode/Applications/axwin3_src/WM/wm.c b/Usermode/Applications/axwin3_src/WM/wm.c
index b304cacd966878894e6c3efcaeec2b7da34ca0cd..cc7e86ea96d20e22f924ff268ced3033e1c1dc82 100644
--- a/Usermode/Applications/axwin3_src/WM/wm.c
+++ b/Usermode/Applications/axwin3_src/WM/wm.c
@@ -13,6 +13,7 @@
 #include <wm_messages.h>
 #include <decorator.h>
 #include <axwin3/keysyms.h>
+#include <wm_hotkeys.h>
 
 // === IMPORTS ===
 extern int	Renderer_Menu_Init(void);
diff --git a/Usermode/Applications/axwin4_src/Common/include/ipc_proto.hpp b/Usermode/Applications/axwin4_src/Common/include/ipc_proto.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a2e1bc97cd86b53b477364ec500dd0500e716bf3
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Common/include/ipc_proto.hpp
@@ -0,0 +1,68 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * ipc_proto.hpp
+ * - IPC Protocol Header
+ */
+#ifndef _IPC_PROTO_H_
+#define _IPC_PROTO_H_
+
+namespace AxWin {
+
+enum
+{
+	IPCMSG_NULL,
+	IPCMSG_REPLY,
+	IPCMSG_PING,
+	IPCMSG_GETGLOBAL,
+	IPCMSG_SETGLOBAL,
+
+	IPCMSG_CREATEWIN,
+	IPCMSG_CLOSEWIN,
+	IPCMSG_SETWINATTR,
+	IPCMSG_GETWINATTR,
+	IPCMSG_SENDIPC,
+	IPCMSG_GETWINBUF,	// get a handle to the window's buffer
+	
+	// - Window drawing commands
+	IPCMSG_DAMAGERECT,	//  (u16 win, u16 x, u16 y, u16 w, u16 h) - Force reblit of area
+	//IPCMSG_DRAWGROUP,	// (u16 win, u16 group_id) - (hint) Switch to this group
+	//IPCMSG_CLEAR,	// (u16 win) - (hint) Clear current drawing group
+	IPCMSG_PUSHDATA,	// (u16 win, u16 x, u16 y, u16 w, u16 h, void data)
+	IPCMSG_BLIT,	// (win, sx, sy, dx, dy, w, h) - Blit locally
+	IPCMSG_DRAWCTL,	// (win, x, y, w, h, ctlid) - Draw
+	IPCMSG_DRAWTEXT,	// (win, x, y, fontid, text) - Draw text using an internal font
+	IPCMSG_FILLRECT,	// (win, x, y, w, h, colour)
+	IPCMSG_DRAWRECT,	// (win, x, y, w, h, colour)
+	
+	// - Client-bound commands
+	IPCMSG_INPUTEVENT,	// (u8 event, u16 win, ...)
+};
+
+enum eIPC_GlobalAttrs
+{
+	IPC_GLOBATTR_SCREENDIMS,	// Screen dimensions - Readonly
+	IPC_GLOBATTR_MAXAREA,	// Maximum window area for screen (hint only, not enforced)
+};
+
+enum eIPC_WinAttrs
+{
+	IPC_WINATTR_SHOW,	// u8	- Window shown
+	IPC_WINATTR_FLAGS,	// u32	- Decoration enabled, always-on-top
+	IPC_WINATTR_POSITION,	// s16, s16
+	IPC_WINATTR_DIMENSIONS,	// u16, u16
+	IPC_WINATTR_TITLE,	// string
+};
+
+enum eIPC_InputEvents
+{
+	IPC_INEV_KEYBOARD,	// (u16 keysym, u8 keydown, string text)
+	IPC_INEV_MOUSEBTN,	// (u16 x, u16 y)
+	IPC_INEV_MOUSEMOVE,	// (u16 x, u16 y, u8 btn, u8 btndown)
+};
+
+};
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Common/include/serialisation.hpp b/Usermode/Applications/axwin4_src/Common/include/serialisation.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..54293c1b29ec5e7a590d0ac633eac33547b4a37e
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Common/include/serialisation.hpp
@@ -0,0 +1,74 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * serialisation.hpp
+ * - Generic (de)serialisation code
+ */
+#ifndef _SERIALISATION_H_
+#define _SERIALISATION_H_
+
+#include <cstdint>
+#include <cstddef>
+#include <exception>
+#include <string>
+#include <vector>
+
+namespace AxWin {
+
+class CDeserialiseException:
+	public ::std::exception
+{
+};
+
+class CDeserialiser
+{
+	::std::vector<uint8_t>	m_vect;
+	size_t	m_offset;
+public:
+	CDeserialiser():
+		CDeserialiser(::std::vector<uint8_t>())
+	{}
+	CDeserialiser(const ::std::vector<uint8_t>& vect);
+	CDeserialiser(::std::vector<uint8_t>&& vect);
+	CDeserialiser(const CDeserialiser& x) { *this = x; };
+	CDeserialiser& operator=(const CDeserialiser& x);
+	bool	IsConsumed() const;
+	::uint8_t	ReadU8();
+	::uint16_t	ReadU16();
+	::int16_t	ReadS16();
+	::uint32_t	ReadU32();
+	::uint64_t	ReadU64();
+	const ::std::vector<uint8_t>	ReadBuffer();
+	const ::std::string	ReadString();
+private:
+	void RangeCheck(const char *Method, size_t bytes) throw(::std::out_of_range);
+};
+
+class CSerialiser
+{
+	::std::vector<uint8_t>	m_data;
+public:
+	CSerialiser();
+	void WriteU8(::uint8_t val);
+	void WriteU16(::uint16_t val);
+	void WriteS16(::int16_t val);
+	void WriteU32(::uint32_t val);
+	void WriteU64(::uint64_t val);
+	void WriteBuffer(size_t n, const void* val);
+	void WriteString(const char* val, size_t n);
+	void WriteString(const char* val) {
+		WriteString(val, ::std::char_traits<char>::length(val));
+	}
+	void WriteString(const ::std::string& val) {
+		WriteString(val.data(), val.size());
+	}
+	void WriteSub(const CSerialiser& val);
+	
+	const ::std::vector<uint8_t>& Compact();
+};
+
+};
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Common/serialisation.cpp b/Usermode/Applications/axwin4_src/Common/serialisation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..016b000456aaf99733c9d50e93355701f0a60474
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Common/serialisation.cpp
@@ -0,0 +1,182 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * serialisation.cpp
+ * - IPC Serialisation
+ */
+#include <serialisation.hpp>
+#include <cstddef>
+#include <stdexcept>
+#include <acess/sys.h>	// SysDebug
+
+namespace AxWin {
+
+CDeserialiser::CDeserialiser(const ::std::vector<uint8_t>& vector):
+	m_vect(vector),
+	m_offset(0)
+{
+}
+CDeserialiser::CDeserialiser(::std::vector<uint8_t>&& vector):
+	m_vect(vector),
+	m_offset(0)
+{
+}
+CDeserialiser& CDeserialiser::operator=(const CDeserialiser& x)
+{
+	m_vect = x.m_vect;
+	m_offset = x.m_offset;
+}
+
+bool CDeserialiser::IsConsumed() const
+{
+	return m_offset == m_vect.size();
+}
+
+::uint8_t CDeserialiser::ReadU8()
+{
+	RangeCheck("CDeserialiser::ReadU8", 1);
+	uint8_t rv = m_vect[m_offset];
+	m_offset ++;
+	return rv;
+}
+
+::uint16_t CDeserialiser::ReadU16()
+{
+	RangeCheck("CDeserialiser::ReadU16", 2);
+	uint16_t rv = m_vect[m_offset] | ((uint16_t)m_vect[m_offset+1] << 8);
+	m_offset += 2;
+	return rv;
+}
+
+::int16_t CDeserialiser::ReadS16()
+{
+	uint16_t rv_u = ReadU16();
+	if( rv_u < 0x8000 )
+		return rv_u;
+	else
+		return ~rv_u + 1;
+}
+
+::uint32_t CDeserialiser::ReadU32()
+{
+	uint32_t rv = ReadU16();
+	rv |= (uint32_t)ReadU16() << 16;
+	return rv;
+}
+
+::uint64_t CDeserialiser::ReadU64()
+{
+	uint64_t rv = ReadU32();
+	rv |= (uint64_t)ReadU32() << 32;
+	return rv;
+}
+
+const ::std::vector<uint8_t> CDeserialiser::ReadBuffer()
+{
+	RangeCheck("CDeserialiser::ReadBuffer(len)", 2);
+	size_t	size = ReadU16();
+	
+	auto range_start = m_vect.begin() + int(m_offset);
+	::std::vector<uint8_t> ret( range_start, range_start + int(size) );
+	m_offset += size;
+	return ret;
+}
+
+const ::std::string CDeserialiser::ReadString()
+{
+	RangeCheck("CDeserialiser::ReadString(len)", 1);
+	uint8_t len = ReadU8();
+	
+	RangeCheck("CDeserialiser::ReadString(data)", len);
+	::std::string ret( reinterpret_cast<const char*>(m_vect.data()+m_offset), len );
+	m_offset += len;
+	return ret;
+}
+
+void CDeserialiser::RangeCheck(const char *Method, size_t bytes) throw(::std::out_of_range)
+{
+	if( m_offset + bytes > m_vect.size() ) {
+		::_SysDebug("%s - out of range %i+%i >= %i", Method, m_offset, bytes, m_vect.size());
+		throw ::std::out_of_range(Method);
+	}
+}
+
+CSerialiser::CSerialiser()
+{
+}
+
+void CSerialiser::WriteU8(::uint8_t Value)
+{
+	m_data.push_back(Value);
+}
+
+void CSerialiser::WriteU16(::uint16_t Value)
+{
+	m_data.push_back(Value & 0xFF);
+	m_data.push_back(Value >> 8);
+}
+
+void CSerialiser::WriteS16(::int16_t Value)
+{
+	if( Value < 0 )
+	{
+		::uint16_t rawval = 0x10000 - (::int32_t)Value;
+		WriteU16(rawval);
+	}
+	else
+	{
+		WriteU16(Value);
+	}
+}
+
+void CSerialiser::WriteU32(::uint32_t Value)
+{
+	m_data.push_back(Value & 0xFF);
+	m_data.push_back(Value >>  8);
+	m_data.push_back(Value >> 16);
+	m_data.push_back(Value >> 24);
+}
+
+void CSerialiser::WriteU64(::uint64_t Value)
+{
+	WriteU32(Value);
+	WriteU32(Value>>32);
+}
+
+void CSerialiser::WriteBuffer(size_t n, const void* val)
+{
+	const uint8_t*	val8 = static_cast<const uint8_t*>(val);
+	if( n > 0xFFFF )
+		throw ::std::length_error("CSerialiser::WriteBuffer");
+	m_data.reserve( m_data.size() + 2 + n );
+	WriteU16(n);
+	for( size_t i = 0; i < n; i ++ )
+		m_data.push_back(val8[i]);
+}
+
+void CSerialiser::WriteString(const char* val, size_t n)
+{
+	if( n > 0xFF )
+		throw ::std::length_error("CSerialiser::WriteString");
+	m_data.reserve( m_data.size() + 1 + n );
+	WriteU8(n);
+	for( size_t i = 0; i < n; i ++ )
+		m_data.push_back(val[i]);
+}
+
+void CSerialiser::WriteSub(const CSerialiser& val)
+{
+	// TODO: Append reference to sub-buffer contents
+	m_data.reserve( m_data.size() + val.m_data.size() );
+	for( auto byte : val.m_data )
+		m_data.push_back( byte );
+}
+
+const ::std::vector<uint8_t>& CSerialiser::Compact()
+{
+	return m_data;
+}
+
+};	// namespace AxWin
+
diff --git a/Usermode/Applications/axwin4_src/Makefile b/Usermode/Applications/axwin4_src/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..28fb532fdaaff7bbeb2935b06594aed2cb23419d
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Makefile
@@ -0,0 +1,5 @@
+%:
+	@echo --- AxWin4 Server
+	@make -C Server/ $*
+	@echo --- AxWin4 UI
+	@make -C UI/ $*
diff --git a/Usermode/Applications/axwin4_src/Notes.txt b/Usermode/Applications/axwin4_src/Notes.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ee09686bb3e24771397e1764c76cb31a06aeb637
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Notes.txt
@@ -0,0 +1,47 @@
+Layers:
+
+IPC / Client management
+Compositor / Window Manager
+Renderer / Window Contents
+
+Renderers
+Window Management
+> "WM_CreateWindow(Parent, Class, Name)"
+Window Drawing
+> "WD_Fill"
+> "WD_Blit"
+> "WD_LockSurface"
+> "WD_UnlockSurface"
+Decorations
+> ".InitWindow"
+> ".Render"
++ "WM_SetBorder"
+Compositing
+> Dirty rectangling, use 2DCmd to selectively blit
+> Request kernel/server buffers if possible
+
+
+Clients own windows
+Windows are composed of multiple regions that conform to several types (see below)
+- Re-draw is handled by using these regions
+
+Server-side rendering primitives:
+ # Apply to regions, rendered in fixed order, each has an ID
+> Auto-scaling bitmaps
+ - Control backed by an image with three/five regions per axis
+  Edge Fixed, Fill, Center Fixed, Fill, Edge Fixed
+ - Definition is via two pixel counts (edge width, fill width), rest is derived
+ - Command to switch backing image to another already provided
+> Tiling bitmaps + filled rects
+> Text (single line)
+> Canvas (Takes drawing commands, draws to internal buffer)
+> Shared buffer (of an unspecified pixel format)
+
+=== Config options ===
+- Root App
+- Display device (- = stdout)
+- Keyboard device (- = stdin)
+- Mouse device
+- Pipe suffix, port number, etc.
+- Key bindings
+
diff --git a/Usermode/Applications/axwin4_src/Server/CClient.cpp b/Usermode/Applications/axwin4_src/Server/CClient.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2abcb9b5ed2714e4ca3e3b9b2bb839dfea684a3c
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/CClient.cpp
@@ -0,0 +1,79 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * CClient.cpp
+ * - IPC Client
+ */
+#include <CClient.hpp>
+#include <IIPCChannel.hpp>
+#include <ipc.hpp>
+#include <draw_text.hpp>	// for the fonts
+
+namespace AxWin {
+
+CClient::CClient(::AxWin::IIPCChannel& channel):
+	m_channel(channel),
+	m_id(0)
+{
+	
+}
+
+CClient::~CClient()
+{
+	::AxWin::IPC::DeregisterClient(*this);
+}
+
+CWindow* CClient::GetWindow(int ID)
+{
+	try {
+		return m_windows.at(ID);
+	}
+	catch(const std::exception& e) {
+		return NULL;
+	}
+}
+
+void CClient::SetWindow(int ID, CWindow* window)
+{
+	//_SysDebug("SetWindow(ID=%i,window=%p)", ID, window);
+	auto it = m_windows.find(ID);
+	if( it != m_windows.end() ) {
+		_SysDebug("CLIENT BUG: Window ID %i is already used by %p", ID, it->second);
+	}
+	else {
+		m_windows[ID] = window;
+	}
+}
+
+IFontFace& CClient::GetFont(unsigned int id)
+{
+	static CFontFallback	fallback_font;
+	if( id == 0 ) {
+		_SysDebug("GetFont: %i = %p", id, &fallback_font);
+		return fallback_font;
+	}
+	assert(!"TODO: CClient::GetFont id != 0");
+}
+
+void CClient::HandleMessage(CDeserialiser& message)
+{
+	try {
+		IPC::HandleMessage(*this, message);
+		if( !message.IsConsumed() )
+		{
+			_SysDebug("NOTICE - CClient::HandleMessage - Trailing data in message");
+		}
+	}
+	catch( const ::std::exception& e )
+	{
+		_SysDebug("ERROR - Exception while processing message from client: %s", e.what());
+	}
+	catch( ... )
+	{
+		_SysDebug("ERROR - Unknown exception while processing message from client");
+	}
+}
+
+};	// namespace AxWin
+
diff --git a/Usermode/Applications/axwin4_src/Server/CConfig.cpp b/Usermode/Applications/axwin4_src/Server/CConfig.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eec783b44b04a4b018f2acd277fe8ba45951829d
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/CConfig.cpp
@@ -0,0 +1,35 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * CConfig.cpp
+ * - Configuration
+ */
+#include <CConfig.hpp>
+
+namespace AxWin {
+
+CConfig::CConfig()
+{
+}
+
+bool CConfig::parseCommandline(int argc, char *argv[])
+{
+	return false;
+}
+
+CConfigVideo::CConfigVideo()
+{
+}
+
+CConfigInput::CConfigInput():
+	mouse_device("/Devices/Mouse/system")
+{
+}
+
+CConfigIPC::CConfigIPC()
+{
+}
+
+};	// namespace AxWin
+
diff --git a/Usermode/Applications/axwin4_src/Server/CIPCChannel_AcessIPCPipe.cpp b/Usermode/Applications/axwin4_src/Server/CIPCChannel_AcessIPCPipe.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0d894e3d25c9ccab0386e28780cb6097a9bee2b6
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/CIPCChannel_AcessIPCPipe.cpp
@@ -0,0 +1,120 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * CIPCChannel_AcessIPCPipe.cpp
+ * - IPC Channel :: Acess' IPC Pipe /Devices/ipcpipe/<name>
+ */
+#include <ipc.hpp>
+#include <CIPCChannel_AcessIPCPipe.hpp>
+#include <cerrno>
+#include <system_error>
+#include <acess/sys.h>
+#include <algorithm>
+
+namespace AxWin {
+
+CIPCChannel_AcessIPCPipe::CIPCChannel_AcessIPCPipe(const ::std::string& suffix)
+{
+	::std::string	path = "/Devices/ipcpipe/" + suffix;
+	m_fd = _SysOpen(path.c_str(), OPENFLAG_CREATE);
+	if(m_fd == -1) {
+		_SysDebug("Failed to open %s: %s", path.c_str(), strerror(errno));
+		throw ::std::system_error(errno, ::std::system_category());
+	}
+}
+CIPCChannel_AcessIPCPipe::~CIPCChannel_AcessIPCPipe()
+{
+	_SysClose(m_fd);
+}
+
+int CIPCChannel_AcessIPCPipe::FillSelect(fd_set& rfds)
+{
+	 int	maxfd = m_fd;
+	FD_SET(m_fd, &rfds);
+	
+	for( auto& clientref : m_clients )
+	{
+		maxfd = ::std::max(maxfd, clientref.m_fd);
+		FD_SET(clientref.m_fd, &rfds);
+	}
+	
+	return maxfd+1;
+}
+
+void CIPCChannel_AcessIPCPipe::HandleSelect(const fd_set& rfds)
+{
+	if( FD_ISSET(m_fd, &rfds) )
+	{
+		int newfd = _SysOpenChild(m_fd, "newclient", OPENFLAG_READ|OPENFLAG_WRITE);
+		if( newfd == -1 ) {
+			_SysDebug("ERROR - Failure to open new client on FD%i", m_fd);
+		}
+		else {
+			_SysDebug("CIPCChannel_AcessIPCPipe::HandleSelect - New client on FD %i with FD%i",
+				m_fd, newfd);
+			
+			// emplace creates a new object within the list
+			m_clients.emplace( m_clients.end(), *this, newfd );
+			IPC::RegisterClient( m_clients.back() );
+		}
+	}
+
+	for( auto it = m_clients.begin(); it != m_clients.end();  )
+	{
+		CClient_AcessIPCPipe& clientref = *it;
+		++ it;
+		
+		if( FD_ISSET(clientref.m_fd, &rfds) )
+		{
+			try {
+				clientref.HandleReceive();
+			}
+			catch( const ::std::exception& e ) {
+				_SysDebug("ERROR - Exception processing IPCPipe FD%i: '%s', removing",
+					clientref.m_fd, e.what()
+					);
+				it = m_clients.erase(--it);
+			}
+		}
+	}
+}
+
+
+CClient_AcessIPCPipe::CClient_AcessIPCPipe(::AxWin::IIPCChannel& channel, int fd):
+	CClient(channel),
+	m_fd(fd)
+{
+}
+
+CClient_AcessIPCPipe::~CClient_AcessIPCPipe()
+{
+	_SysClose(m_fd);
+	_SysDebug("Closed client FD%i", m_fd);
+}
+
+void CClient_AcessIPCPipe::SendMessage(CSerialiser& message)
+{
+	const ::std::vector<uint8_t>&	data = message.Compact();
+	
+	_SysDebug("CClient_AcessIPCPipe::SendMessage - %i bytes to %i", data.size(), m_fd);
+	//_SysDebugHex("CClient_AcessIPCPipe::SendMessage", data.data(), data.size());
+	_SysWrite(m_fd, data.data(), data.size());
+}
+
+void CClient_AcessIPCPipe::HandleReceive()
+{
+	::std::vector<uint8_t>	rxbuf(0x1000);
+	size_t len = _SysRead(m_fd, rxbuf.data(), rxbuf.capacity());
+	if( len == (size_t)-1 )
+		throw ::std::system_error(errno, ::std::system_category());
+	_SysDebug("CClient_AcessIPCPipe::HandleReceive - Rx %i/%i bytes", len, rxbuf.capacity());
+	//_SysDebugHex("CClient_AcessIPCPipe::HandleReceive", rxbuf.data(), len);
+	rxbuf.resize(len);
+	
+	CDeserialiser	msg( ::std::move(rxbuf) );
+	CClient::HandleMessage( msg );
+}
+
+};
+
diff --git a/Usermode/Applications/axwin4_src/Server/CRect.cpp b/Usermode/Applications/axwin4_src/Server/CRect.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..acbcc27e30b3b7bb51a46089a5c168f1d88654a1
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/CRect.cpp
@@ -0,0 +1,78 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * CRect.cpp
+ * - Rectangle
+ */
+#include <CRect.hpp>
+#include <algorithm>
+#include <acess/sys.h>
+
+namespace AxWin {
+
+CRect::CRect(int x, int y, unsigned int w, unsigned int h):
+	m_x(x), m_y(y),
+	m_w(w), m_h(h),
+	m_x2(x+w), m_y2(y+h)
+{
+}
+
+void CRect::Move(int NewX, int NewY)
+{
+	// TODO: Add a parent rectangle, and prevent this from fully leaving its bounds
+	m_x = NewX;
+	m_y = NewY;
+	m_x2 = m_x + m_w;
+	m_y2 = m_y + m_h;
+}
+
+void CRect::Resize(int NewW, int NewH)
+{
+	m_w = NewW;
+	m_h = NewH;
+	m_x2 = m_x + m_w;
+	m_y2 = m_y + m_h;
+}
+
+bool CRect::HasIntersection(const CRect& other) const
+{
+	// If other's origin is past our far corner
+	if( m_x2 < other.m_x )
+		return false;
+	if( m_y2 < other.m_y )
+		return false;
+	
+	// If other's far corner is before our origin
+	if( m_x > other.m_x2 )
+		return false;
+	if( m_y > other.m_y2 )
+		return false;
+	return true;
+}
+
+CRect CRect::Intersection(const CRect& other) const
+{
+	int x1 = ::std::max(m_x, other.m_x);
+	int y1 = ::std::max(m_y, other.m_y);
+	int x2 = ::std::min(m_x2, other.m_x2);
+	int y2 = ::std::min(m_y2, other.m_y2);
+	
+	if( x2 <= x1 || y2 <= y1 )
+		return CRect();
+	
+	return CRect(x1, y1, x2-x1, y2-y1);
+}
+
+CRect CRect::RelativeIntersection(const CRect& area)
+{
+	CRect	ret = Intersection(area);
+	ret.m_x -= m_x;
+	ret.m_x2 -= m_x;
+	ret.m_y -= m_y;
+	ret.m_y2 -= m_y;
+	return ret;
+}
+
+};
+
diff --git a/Usermode/Applications/axwin4_src/Server/CSurface.cpp b/Usermode/Applications/axwin4_src/Server/CSurface.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d0eddc428ef3101a7abb5bd54746c2808619e7a4
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/CSurface.cpp
@@ -0,0 +1,143 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * CWindow.cpp
+ * - Window
+ */
+#include <CSurface.hpp>
+#include <cassert>
+#include <stdexcept>
+#include <cstring>
+#include <system_error>
+#include <cerrno>
+#include <CColour.hpp>
+
+namespace AxWin {
+
+CSurface::CSurface(int x, int y, unsigned int w, unsigned int h):
+	m_rect(x,y, w,h),
+	m_fd(-1),
+	m_data(0)
+{
+	if( w > 0 && h > 0 )
+	{
+		m_data = new uint32_t[w * h];
+	}
+}
+
+CSurface::~CSurface()
+{
+	if( m_fd == -1 ) {
+		delete[] m_data;
+	}
+	else {
+		size_t	size = m_rect.m_w*m_rect.m_h*4;
+		_SysMUnMap(m_data, size);
+	}
+}
+
+uint64_t CSurface::GetSHMHandle()
+{
+	size_t	size = m_rect.m_w*m_rect.m_h*4;
+	if( m_fd == -1 )
+	{
+		// 2. Allocate a copy in SHM
+		m_fd = _SysOpen("/Devices/shm/anon", OPENFLAG_WRITE|OPENFLAG_READ);
+		if(m_fd == -1) {
+			_SysDebug("GetSHMHandle: Unable to open anon SHM");
+			return -1;
+		}
+		// 1. Free local buffer
+		delete m_data;
+		_SysTruncate(m_fd, size);
+	}
+	else
+	{
+		_SysMUnMap(m_data, size);
+	}
+	// 3. mmap shm copy
+	m_data = static_cast<uint32_t*>( _SysMMap(nullptr, size, MMAP_PROT_WRITE, 0, m_fd, 0) );
+	if(!m_data)	throw ::std::system_error(errno, ::std::system_category());
+	
+	return _SysMarshalFD(m_fd);
+}
+
+void CSurface::Resize(unsigned int W, unsigned int H)
+{
+	if( m_fd == -1 )
+	{
+		// Easy realloc
+		// TODO: Should I maintain window contents sanely? NOPE!
+		delete m_data;
+		m_data = new uint32_t[W * H];
+	}
+	else
+	{
+		//_SysIOCtl(m_fd, SHM_IOCTL_SETSIZE, W*H*4);
+	}
+	m_rect.Resize(W, H);
+}
+
+void CSurface::DrawScanline(unsigned int row, unsigned int x_ofs, unsigned int w, const void* data)
+{
+	if( row >= m_rect.m_h )
+		throw ::std::out_of_range("CSurface::DrawScanline row");
+	if( x_ofs >= m_rect.m_w )
+		throw ::std::out_of_range("CSurface::DrawScanline x_ofs");
+
+	if( w > m_rect.m_w )
+		throw ::std::out_of_range("CSurface::DrawScanline width");
+	
+	size_t	ofs = row*m_rect.m_w + x_ofs;
+	::memcpy( &m_data[ofs], data, w*4 );
+}
+
+void CSurface::BlendScanline(unsigned int row, unsigned int x_ofs, unsigned int w, const void* data)
+{
+	if( row >= m_rect.m_h )
+		throw ::std::out_of_range("CSurface::DrawScanline row");
+	if( x_ofs >= m_rect.m_w )
+		throw ::std::out_of_range("CSurface::DrawScanline x_ofs");
+
+	if( w > m_rect.m_w )
+		throw ::std::out_of_range("CSurface::DrawScanline width");
+	
+	const uint32_t*	in_data = (const uint32_t*)data;
+	size_t	ofs = row*m_rect.m_w + x_ofs;
+	for( unsigned int x = 0; x < w; x ++ )
+	{
+		CColour	out = CColour::from_argb(m_data[ofs+x]).blend( CColour::from_argb(in_data[x]) );
+		m_data[ofs+x] = out.to_argb();
+	}
+}
+
+void CSurface::FillScanline(unsigned int row, unsigned int x_ofs, unsigned int w, uint32_t colour)
+{
+	if( row >= m_rect.m_h )
+		throw ::std::out_of_range("CSurface::FillScanline row");
+	if( x_ofs >= m_rect.m_w )
+		throw ::std::out_of_range("CSurface::FillScanline x_ofs");
+
+	if( w > m_rect.m_w )
+		throw ::std::out_of_range("CSurface::FillScanline width");
+	
+	size_t	ofs = row*m_rect.m_w + x_ofs;
+	while( w -- )
+		m_data[ofs++] = colour;
+}
+
+const uint32_t* CSurface::GetScanline(unsigned int row, unsigned int x_ofs) const
+{
+	if( row >= m_rect.m_h )
+		throw ::std::out_of_range("CSurface::GetScanline row");
+	if( x_ofs >= m_rect.m_w )
+		throw ::std::out_of_range("CSurface::GetScanline x_ofs");
+
+	return &m_data[row * m_rect.m_w + x_ofs];
+}
+
+
+};	// namespace AxWin
+
+
diff --git a/Usermode/Applications/axwin4_src/Server/CWindow.cpp b/Usermode/Applications/axwin4_src/Server/CWindow.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..022d6b49be1d9690c01824fd03397fe2909f7911
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/CWindow.cpp
@@ -0,0 +1,107 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * CWindow.cpp
+ * - Window
+ */
+#include <CWindow.hpp>
+#include <CCompositor.hpp>
+#include <assert.h>
+#include <ipc.hpp>
+
+namespace AxWin {
+
+CWindow::CWindow(CCompositor& compositor, CClient& client, const ::std::string& name, unsigned int id):
+	m_surface(0,0,0,0),
+	m_compositor(compositor),
+	m_client(client),
+	m_id(id),
+	m_name(name),
+	m_is_shown(false)
+{
+	_SysDebug("CWindow::CWindow()");
+}
+
+CWindow::~CWindow()
+{
+}
+
+void CWindow::Repaint(const CRect& rect)
+{
+	if( m_is_shown )
+	{
+		CRect	outrect(
+			m_surface.m_rect.m_x + rect.m_x, 
+			m_surface.m_rect.m_y + rect.m_y, 
+			rect.m_w, rect.m_h
+			);
+		m_compositor.DamageArea(outrect);
+	}
+}
+
+void CWindow::Show(bool bShow)
+{
+	if( m_is_shown == bShow )
+		return;
+	
+	if( bShow )
+		m_compositor.ShowWindow( this );
+	else
+		m_compositor.HideWindow( this );
+	m_is_shown = bShow;
+}
+
+void CWindow::Move(int X, int Y)
+{
+	m_surface.m_rect.Move(X, Y);
+}
+void CWindow::Resize(unsigned int W, unsigned int H)
+{
+	m_surface.Resize(W, H);
+	IPC::SendMessage_NotifyDims(m_client, m_id, W, H);
+}
+void CWindow::SetFlags(uint32_t Flags)
+{
+	// TODO: CWindow::SetFlags
+	_SysDebug("TOOD: CWindow::SetFlags");
+}
+uint64_t CWindow::ShareSurface()
+{
+	assert(!"TODO: CWindow::ShareSurface");
+	return 0;
+}
+
+void CWindow::MouseButton(int ButtonID, int X, int Y, bool Down)
+{
+	IPC::SendMessage_MouseButton(m_client, m_id, X, Y, ButtonID, Down);
+}
+
+void CWindow::MouseMove(int NewX, int NewY)
+{
+	// TODO: Only enable move events if client requests them
+	//IPC::SendMessage_MouseMove(m_client, m_id, NewX, NewY);
+}
+
+void CWindow::KeyEvent(::uint32_t Scancode, const ::std::string &Translated, bool Down)
+{
+	IPC::SendMessage_KeyEvent(m_client, m_id, Scancode, Down, Translated.c_str());
+}
+
+
+void CWindow::DrawScanline(unsigned int row, unsigned int x, unsigned int w, const uint8_t *data)
+{
+	m_surface.DrawScanline(row, x, w, data);
+	CRect	damaged( m_surface.m_rect.m_x+x, m_surface.m_rect.m_y+row, w, 1 );
+	m_compositor.DamageArea(damaged);
+}
+
+void CWindow::FillScanline(unsigned int row, unsigned int x, unsigned int w, const uint32_t colour)
+{
+	m_surface.FillScanline(row, x, w, colour);
+	CRect	damaged( m_surface.m_rect.m_x+x, m_surface.m_rect.m_y+row, w, 1 );
+	m_compositor.DamageArea(damaged);
+}
+
+};
+
diff --git a/Usermode/Applications/axwin4_src/Server/Makefile b/Usermode/Applications/axwin4_src/Server/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..b85fc08876ca58cabae28885d4094a3be6859e87
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/Makefile
@@ -0,0 +1,26 @@
+
+include ../../Makefile.cfg
+
+DIR := Apps/AxWin/4.0
+
+CPPFLAGS += -Iinclude/ -I../Common/include/
+OBJ := main.o ipc.o CConfig.o video.o input.o timing.o
+OBJ += compositor.o CWindow.o
+OBJ += Common__serialisation.o
+OBJ += CClient.o
+OBJ += CIPCChannel_AcessIPCPipe.o
+OBJ += CRect.o CSurface.o
+OBJ += draw_control.o draw_text.o
+BIN := AxWinServer
+
+LIBS += -lc++ -lunicode
+#CXXFLAGS += -O3
+USE_CXX_LINK = 1 
+
+include ../../Makefile.tpl
+
+$(_OBJPREFIX)Common__%.o: ../Common/%.cpp
+	@echo [CXX] -o $@
+	@mkdir -p $(dir $@)
+	$V$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ -MQ $@ -MP -MD -MF $(@:%.o=%.dep)
+
diff --git a/Usermode/Applications/axwin4_src/Server/compositor.cpp b/Usermode/Applications/axwin4_src/Server/compositor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e488380fce4dedea6fa521dff1958cade6dbd4e2
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/compositor.cpp
@@ -0,0 +1,186 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * compositor.cpp
+ * - Window compositor
+ */
+#include <video.hpp>
+#include <CCompositor.hpp>
+#include <CClient.hpp>
+#include <ipc.hpp>
+#include <cassert>
+
+namespace AxWin {
+
+CCompositor::CCompositor(CVideo& video):
+	// TODO: Support multiple screens
+	m_video(video),
+	m_focussed_window(nullptr),
+	m_windowIDBuffer(video.width(), video.height())
+{
+	// 
+}
+
+void CCompositor::ShowWindow(CWindow* window)
+{
+	DamageArea(window->m_surface.m_rect);
+	// TODO: Append to separate sub-lists (or to separate lists all together)
+	//   if flags AXWIN4_WNDFLAG_KEEPBELOW or AXWIN4_WNDFLAG_KEEPABOVE are set
+	m_windows.push_back(window);
+}
+void CCompositor::HideWindow(CWindow* window)
+{
+	DamageArea(window->m_surface.m_rect);
+	m_windows.remove(window);
+}
+
+bool CCompositor::GetScreenDims(unsigned int ScreenID, unsigned int* W, unsigned int* H)
+{
+	assert(W && H);
+	if( ScreenID != 0 )
+	{
+		*W = 0;
+		*H = 0;
+		return false;
+	}
+	else
+	{
+		m_video.GetDims(*W, *H);
+		return true;
+	}
+}
+
+void CCompositor::Redraw()
+{
+	// Redraw the screen and clear damage rects
+	if( m_damageRects.empty() ) {
+		//_SysDebug("- No damaged regions");
+		return ;
+	}
+	
+	// Build up foreground grid (Rects and windows)
+	// - This should already be built (mutated on window move/resize/reorder)
+	
+	// For all windows, check for intersection with damage rects
+	for( auto rect : m_damageRects )
+	{
+		// window list should be sorted by draw order (lowest first)
+		for( auto window : m_windows )
+		{
+			if( window->m_is_shown && rect.HasIntersection( window->m_surface.m_rect ) )
+			{
+				// TODO: just reblit
+				CRect	rel_rect = window->m_surface.m_rect.RelativeIntersection(rect);
+				_SysDebug("Reblit (%i,%i) %ix%i", rel_rect.m_x, rel_rect.m_y, rel_rect.m_w, rel_rect.m_h);
+				BlitFromSurface( window->m_surface, rel_rect );
+				//window->Repaint( rel_rect );
+				m_windowIDBuffer.set(rel_rect.m_x, rel_rect.m_y, rel_rect.m_w, rel_rect.m_h, window);
+			}
+		}
+		
+		// TODO: Blit from windows to a local surface, then blit from there to screen here
+	}
+
+	m_damageRects.clear();
+	m_video.Flush();
+}
+
+void CCompositor::DamageArea(const CRect& area)
+{
+	m_damageRects.push_back( area );
+	// 1. Locate intersection with any existing damaged areas
+	// 2. Append after removing intersections
+}
+
+void CCompositor::BlitFromSurface(const CSurface& dest, const CRect& src_rect)
+{
+	for( unsigned int i = 0; i < src_rect.m_h; i ++ )
+	{
+		m_video.BlitLine(
+			dest.GetScanline(src_rect.m_y+i, src_rect.m_x),
+			dest.m_rect.m_y + src_rect.m_y + i,
+			dest.m_rect.m_x + src_rect.m_x,
+			src_rect.m_w
+			);
+	}
+}
+
+void CCompositor::MouseMove(unsigned int Cursor, unsigned int X, unsigned int Y, int dX, int dY)
+{
+	//_SysDebug("MouseButton(%i, %i,%i, %+i,%+i)", Cursor, X, Y, dX, dY);
+	m_video.SetCursorPos(X+dX, Y+dY);
+	CWindow	*dstwin = getWindowForCoord(X, Y);
+	if( dstwin )
+	{
+		// Pass event on to window
+		dstwin->MouseMove(X, Y);
+	}
+}
+
+void CCompositor::MouseButton(unsigned int Cursor, unsigned int X, unsigned int Y, eMouseButton Button, bool Press)
+{
+	_SysDebug("MouseButton(%i, %i,%i, %i=%i)", Cursor, X, Y, Button, Press);
+	CWindow	*dstwin = getWindowForCoord(X, Y);
+	_SysDebug("- dstwin = %p", dstwin);
+	if( dstwin )
+	{
+		// 1. Give focus and bring to front
+		// 2. Send event
+		dstwin->MouseButton(Button, X, Y, Press);
+	}
+}
+
+void CCompositor::KeyState(unsigned int KeyboardID, uint32_t KeySym, bool Press, uint32_t Codepoint)
+{
+	_SysDebug("KeyState(%i, 0x%x, %b, 0x%x)", KeyboardID, KeySym, Press, Codepoint);
+	// TODO: Global hotkeys
+	if( m_focussed_window )
+	{
+		m_focussed_window->KeyEvent(KeySym, "", Press);
+	}
+}
+
+CWindow* CCompositor::getWindowForCoord(unsigned int X, unsigned int Y)
+{
+	return m_windowIDBuffer.get(X, Y);
+}
+
+// --------------------------------------------------------------------
+CWindowIDBuffer::CWindowIDBuffer(unsigned int W, unsigned int H):
+	m_w(W),
+	m_buf(W*H)
+{
+}
+void CWindowIDBuffer::set(unsigned int X, unsigned int Y, unsigned int W, unsigned int H, CWindow* win)
+{
+	TWindowID	ent = {
+		.Client = win->client().id(),
+		.Window = win->id(),
+		};
+	for( unsigned int row = 0; row < H; row ++ )
+	{
+		TWindowID* dst = &m_buf[ (Y+row) * m_w ];
+		for( unsigned int col = 0; col < W; col ++ )
+			dst[col] = ent;
+	}
+}
+CWindow* CWindowIDBuffer::get(unsigned int X, unsigned int Y)
+{
+	if( X >= m_w )
+		return nullptr;
+	unsigned int pos = Y*m_w + X;
+	if( pos >= m_buf.size() )
+		return nullptr;
+	auto id = m_buf[pos];
+	//_SysDebug("CWindowIDBuffer::get id = {%i,%i}", id.Client, id.Window);
+	auto client = ::AxWin::IPC::GetClientByID(id.Client);
+	if( client == nullptr ) {
+		//_SysDebug("CWindowIDBuffer::get client=%p", client);
+		return nullptr;
+	}
+	return client->GetWindow(id.Window);
+}
+
+}	// namespace AxWin
+
diff --git a/Usermode/Applications/axwin4_src/Server/draw_control.cpp b/Usermode/Applications/axwin4_src/Server/draw_control.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ef105e77d433b259ddfe6f4bb11b12d32f554bb9
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/draw_control.cpp
@@ -0,0 +1,178 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * draw_control.cpp
+ * - Common "Control" Drawing
+ *
+ * Handles drawing of resizable controls defined by a bitmap and four region sizes
+ */
+#include <draw_control.hpp>
+#include <axwin4/definitions.h>
+#include <acess/sys.h>
+#include <cassert>
+
+// === CODE ===
+namespace AxWin {
+
+CControl::CControl(int EdgeX, int FillX, int InnerX, int EdgeY, int FillY, int InnerY, ::std::vector<uint32_t>&& data):
+	m_edge_x(EdgeX),
+	m_fill_x(FillX),
+	m_inner_x(InnerX),
+	m_edge_y(EdgeY),
+	m_fill_y(FillY),
+	m_inner_y(InnerY),
+	m_data(data)
+{
+	_SysDebug("CControl(X={E:%i,F:%i,I:%i}, Y={E:%i,F:%i,I:%i}, data={Size:%i})",
+		m_edge_x, m_fill_x, m_inner_x, m_edge_y, m_fill_y, m_inner_y, m_data.size());
+}
+
+void CControl::Render(CSurface& dest, const CRect& rect) const
+{
+	if( rect.m_w < m_edge_x*2 + m_fill_x*2 + m_inner_x )
+		return ;
+	if( rect.m_h < m_edge_y*2 + m_fill_y*2 + m_inner_y )
+		return ;
+	
+	const int ctrl_width = m_edge_x + m_fill_x + m_inner_x + (m_inner_x ? m_fill_x : 0) + m_edge_x;
+	
+	const int top_fill_end = rect.m_h / 2 - m_inner_y;
+	const int bot_fill_start = top_fill_end + m_inner_y;
+	const int bot_fill_end   = rect.m_h - m_edge_y;
+	
+	::std::vector<uint32_t>	scanline( rect.m_w );
+	 int	y = 0;
+	 int	base_ofs = 0;
+	// EdgeY
+	for( int i = 0; i < m_edge_y; i ++ )
+		renderLine(dest, y++, scanline, rect, &m_data[(base_ofs+i)*ctrl_width]);
+	base_ofs += m_edge_y;
+	// FillY
+	assert(m_fill_y > 0 || y == top_fill_end);
+	while( y < top_fill_end )
+	{
+		for( int i = 0; i < m_fill_y && y < top_fill_end; i ++ )
+			renderLine(dest, y++, scanline, rect, &m_data[(base_ofs+i)*ctrl_width]);
+	}
+	base_ofs += m_fill_y;
+	// InnerY
+	if( m_inner_y > 0 )
+	{
+		for( int i = 0; i < m_inner_y; i ++ )
+			renderLine(dest, y++, scanline, rect, &m_data[(base_ofs+i)*ctrl_width]);
+		base_ofs += m_inner_y;
+	}
+	else
+	{
+		base_ofs -= m_fill_x;
+	}
+	// FillY
+	while( y < bot_fill_end )
+	{
+		for( int i = 0; i < m_fill_y && y < bot_fill_end; i ++ )
+			renderLine(dest, y++, scanline, rect, &m_data[(base_ofs+i)*ctrl_width]);
+	}
+	base_ofs += m_fill_y;
+	// EdgeY
+	for( int i = 0; i < m_edge_y; i ++ )
+		renderLine(dest, y++, scanline, rect, &m_data[(base_ofs+i)*ctrl_width]);
+	base_ofs += m_edge_y;
+}
+
+void CControl::renderLine(CSurface& dest, int y, ::std::vector<uint32_t>& scanline, const CRect& rect, const uint32_t* ctrl_line) const
+{
+	//_SysDebug("renderLine: (y=%i,rect={(%i,%i) %ix%i}", y, rect.m_x, rect.m_y, rect.m_w, rect.m_h);
+	const int left_fill_end = rect.m_w / 2 - m_inner_x;
+	const int right_fill_end = rect.m_w - m_edge_x;
+	
+	 int	x = 0;
+	 int	base_ofs = 0;
+	// EdgeX
+	for( int i = 0; i < m_edge_x; i ++ )
+		scanline[x++] = ctrl_line[base_ofs + i];
+	base_ofs += m_edge_x;
+	// FillX
+	while( x < left_fill_end )
+	{
+		for( int i = 0; i < m_fill_x && x < left_fill_end; i ++ )
+			scanline[x++] = ctrl_line[base_ofs + i];
+	}
+	base_ofs += m_fill_x;
+	// InnerX
+	if( m_inner_x > 0 )
+	{
+		for( int i = 0; i < m_inner_x; i ++ )
+			scanline[x++] = ctrl_line[base_ofs + i];
+		base_ofs += m_inner_x;
+	}
+	else
+	{
+		base_ofs -= m_fill_x;
+	}
+	// FillX
+	while( x < right_fill_end )
+	{
+		for( int i = 0; i < m_fill_x && x < right_fill_end; i ++ )
+			scanline[x++] = ctrl_line[base_ofs + i];
+	}
+	base_ofs += m_fill_x;
+	// EdgeX
+	for( int i = 0; i < m_edge_x; i ++ )
+		scanline[x++] = ctrl_line[base_ofs + i];
+	base_ofs += m_edge_x;
+	
+	dest.DrawScanline(rect.m_y + y, rect.m_x, rect.m_w, scanline.data());
+}
+
+// ---- Standard Controls ---
+// Standard button control
+CControl StdButton(2, 1, 0, 2, 1, 0, ::std::vector<uint32_t> {
+	0xC0C0C0, 0xC0C0C0, 0xC0C0C0, 0xC0C0C0, 0xC0C0C0,
+	0xC0C0C0, 0xFFD0D0, 0xFFD0D0, 0xFFD0D0, 0xC0C0C0,
+	0xC0C0C0, 0xFFD0D0, 0xFFD0D0, 0xFFD0D0, 0xC0C0C0,
+	0xC0C0C0, 0xFFD0D0, 0xFFD0D0, 0xFFD0D0, 0xC0C0C0,
+	0xC0C0C0, 0xC0C0C0, 0xC0C0C0, 0xC0C0C0, 0xC0C0C0,
+	});
+
+// Toolbar
+CControl StdToolbar(2, 1, 0, 2, 1, 0, ::std::vector<uint32_t> {
+	0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0xA0A0A0, 0x0A0000, 0xA0A0A0, 0x000000,
+	0x000000, 0xA0A0A0, 0xFFFFFF, 0xA0A0A0, 0x000000,
+	0x000000, 0xA0A0A0, 0x0A0000, 0xA0A0A0, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
+	});
+
+// Text Area
+CControl StdText(2, 1, 0, 2, 1, 0, ::std::vector<uint32_t> {
+	0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
+	0x000000, 0xA0A0A0, 0x0A0000, 0xA0A0A0, 0x000000,
+	0x000000, 0xA0A0A0, 0xFFFFFF, 0xA0A0A0, 0x000000,
+	0x000000, 0xA0A0A0, 0x0A0000, 0xA0A0A0, 0x000000,
+	0x000000, 0x000000, 0x000000, 0x000000, 0x000000,
+	});
+
+const CControl* CControl::GetByName(const ::std::string& name)
+{
+	if( name == "StdButton" )
+		return &StdButton;
+	if( name == "StdText" )
+		return &StdText;
+	// TODO: Use another exception
+	return nullptr;
+}
+
+const CControl* CControl::GetByID(uint16_t id)
+{
+	switch(id)
+	{
+	case AXWIN4_CTL_BUTTON:	return &StdButton;
+	case AXWIN4_CTL_TOOLBAR:	return &StdToolbar;
+	case AXWIN4_CTL_TEXTBOX:	return &StdText;
+	default:	return nullptr;
+	}
+}
+
+};	// AxWin
+
diff --git a/Usermode/Applications/axwin4_src/Server/draw_text.cpp b/Usermode/Applications/axwin4_src/Server/draw_text.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7e9e7b8ede2bce15707e6bd9b02d7e98e9d92d2f
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/draw_text.cpp
@@ -0,0 +1,167 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * draw_text.cpp
+ * - Text Drawing
+ *
+ * Handles font selection and drawing of text to windows
+ */
+#include <draw_text.hpp>
+#include <axwin4/definitions.h>
+#include <unicode.h>	// libunicode (acess)
+extern "C" {
+#include <assert.h>	// assert... and _SysDebug
+};
+#include "resources/font_8x16.h"
+
+// === CODE ===
+namespace AxWin {
+
+// -- Primitive fallback font
+CFontFallback::CFontFallback()
+{
+}
+
+CRect CFontFallback::Size(const ::std::string& text, unsigned int Size) const
+{
+	return CRect(0,0, text.size() * Size * FONT_WIDTH / FONT_HEIGHT, Size);
+}
+
+/**
+ * \param Text height in pixels (not in points)
+ */
+void CFontFallback::Render(CSurface& dest, const CRect& rect, const ::std::string& text, unsigned int Size)
+{
+	unsigned int font_step = Size * FONT_WIDTH / FONT_HEIGHT;
+	CRect	pos = rect;
+	for( auto codepoint : ::libunicode::utf8string(text) )
+	{
+		renderAtRes(dest, pos, codepoint, Size, 0x000000);
+		pos.Translate(font_step, 0);
+	}
+}
+
+void CFontFallback::renderAtRes(CSurface& dest, const CRect& rect, uint32_t cp, unsigned int Size, uint32_t FGC)
+{
+	unsigned int char_idx = unicodeToCharmap(cp);
+	assert(char_idx < 256);
+	const uint8_t*	char_ptr = &VTermFont[char_idx * FONT_HEIGHT];
+	unsigned int	out_h = Size;
+	unsigned int	out_w = (Size * FONT_WIDTH / FONT_HEIGHT);
+	uint32_t	char_data[out_w];
+	if( Size == FONT_HEIGHT ) {
+		// Standard blit
+		for( unsigned int row = 0; row < out_h; row ++ )
+		{
+			for( unsigned int col = 0; col < out_w; col ++ )
+			{
+				uint8_t alpha = getValueAtRaw(char_ptr, col, row);
+				char_data[col] = ((uint32_t)alpha << 24) | (FGC & 0xFFFFFF);
+			}
+			dest.BlendScanline(rect.m_y + row, rect.m_x, FONT_WIDTH, char_data);
+		}
+	}
+	else if( Size < FONT_HEIGHT ) {
+		// Down-scaled blit
+		// NOTE: uses the same code as the upscale blit (probably not correct, need to replace)
+		for( unsigned int row = 0; row < out_h; row ++ )
+		{
+			unsigned int yf16 = row * FONT_HEIGHT * 0x10000 / out_h;
+			for( unsigned int col = 0; col < out_w; col ++ )
+			{
+				unsigned int xf16 = col * FONT_WIDTH * 0x10000 / out_w;
+				uint8_t alpha = getValueAtPt(char_ptr, xf16, yf16);
+				//_SysDebug("row %i (%05x), col %i (%05x): alpha = %02x", row, yf16, col, xf16, alpha);
+				char_data[col] = ((uint32_t)alpha << 24) | (FGC & 0xFFFFFF);
+			}
+			dest.BlendScanline(rect.m_y + row, rect.m_x, out_w, char_data);
+		}
+	}
+	else {
+		// up-scaled blit
+		for( unsigned int row = 0; row < out_h; row ++ )
+		{
+			unsigned int yf16 = row * FONT_HEIGHT * 0x10000 / out_h;
+			for( unsigned int col = 0; col < out_w; col ++ )
+			{
+				unsigned int xf16 = col * FONT_WIDTH * 0x10000 / out_w;
+				uint8_t alpha = getValueAtPt(char_ptr, xf16, yf16);
+				//_SysDebug("row %i (%05x), col %i (%05x): alpha = %02x", row, yf16, col, xf16, alpha);
+				char_data[col] = ((uint32_t)alpha << 24) | (FGC & 0xFFFFFF);
+			}
+			dest.BlendScanline(rect.m_y + row, rect.m_x, out_w, char_data);
+		}
+	}
+}
+// X and Y are fixed-point 16.16 values
+uint8_t CFontFallback::getValueAtPt(const uint8_t* char_ptr, unsigned int xf16, unsigned int yf16)
+{
+	unsigned int ix = xf16 >> 16;
+	unsigned int iy = yf16 >> 16;
+	unsigned int fx = xf16 & 0xFFFF;
+	unsigned int fy = yf16 & 0xFFFF;
+	
+	if( fx == 0 && fy == 0 ) {
+		return getValueAtRaw(char_ptr, ix, iy);
+	}
+	else if( fx == 0 ) {
+		float y = (float)fy / 0x10000;
+		uint8_t v0 = getValueAtRaw(char_ptr, ix, iy  );
+		uint8_t v1 = getValueAtRaw(char_ptr, ix, iy+1);
+		return v0 * (1 - y) + v1 * y;
+	}
+	else if( fy == 0 ) {
+		float x = (float)fx / 0x10000;
+		uint8_t v0 = getValueAtRaw(char_ptr, ix  , iy);
+		uint8_t v1 = getValueAtRaw(char_ptr, ix+1, iy);
+		return v0 * (1 - x) + v1 * x;
+	}
+	else {
+		float x = (float)fx / 0x10000;
+		float y = (float)fx / 0x10000;
+		// [0,0](1 - x)(1 - y) + [1,0]x(1-y) + [0,1](1-x)y + [1,1]xy
+		uint8_t v00 = getValueAtRaw(char_ptr, ix, iy);
+		uint8_t v01 = getValueAtRaw(char_ptr, ix, iy+1);
+		uint8_t v10 = getValueAtRaw(char_ptr, ix+1, iy);
+		uint8_t v11 = getValueAtRaw(char_ptr, ix+1, iy+1);
+		//_SysDebug("x,y = %04x %04x", (unsigned)(x * 0x10000), (unsigned)(y * 0x10000));
+		//_SysDebug("v = %02x %02x %02x %02x", v00, v01, v10, v11);
+		float val1 = v00 * (1 - x) * (1 - y);
+		float val2 = v10 * x * (1 - y);
+		float val3 = v01 * (1 - x) * y;
+		float val4 = v11 * x * y;
+		//_SysDebug("vals = %04x %04x %04x %04x",
+		//	(unsigned)(val1 * 0x10000),
+		//	(unsigned)(val2 * 0x10000),
+		//	(unsigned)(val3 * 0x10000),
+		//	(unsigned)(val4 * 0x10000)
+		//	);
+		
+		return (uint8_t)(val1 + val2 + val3 + val4);
+	}
+}
+
+uint8_t CFontFallback::getValueAtRaw(const uint8_t* char_ptr, unsigned int x, unsigned int y)
+{
+	//if( x == 0 || y == 0 )
+	//	return 0;
+	//x --; y --;
+	if(x >= FONT_WIDTH || y >= FONT_HEIGHT)
+		return 0;
+	return (char_ptr[y] & (1 << (7-x))) ? 255 : 0;
+}
+
+unsigned int CFontFallback::unicodeToCharmap(uint32_t cp) const
+{
+	if(cp >= ' ' && cp < 0x7F)
+		return cp;
+	switch(cp)
+	{
+	default:
+		return 0;
+	}
+}
+
+};	// namespace AxWin
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/CClient.hpp b/Usermode/Applications/axwin4_src/Server/include/CClient.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2a23f155580d49ee96892a27714d116e5e1a6768
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/CClient.hpp
@@ -0,0 +1,48 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * CClient.hpp
+ * - IPC Client
+ */
+#ifndef _CCLIENT_H_
+#define _CCLIENT_H_
+
+#include "CWindow.hpp"
+#include "serialisation.hpp"
+#include <map>
+#include <cassert>
+#include "IFontFace.hpp"
+
+namespace AxWin {
+
+class IIPCChannel;
+
+class CClient
+{
+	unsigned int	m_id;
+	IIPCChannel&	m_channel;
+	
+	::std::map<unsigned int,CWindow*>	m_windows;
+	//CWindow*	m_windows[1];
+public:
+	CClient(::AxWin::IIPCChannel& channel);
+	virtual ~CClient();
+	
+	void set_id(unsigned int id) { assert(m_id == 0); m_id = id; }
+	unsigned int id() const { return m_id; }
+	
+	CWindow*	GetWindow(int ID);
+	void	SetWindow(int ID, CWindow* window);
+	
+	IFontFace&	GetFont(unsigned int id);
+	
+	virtual void	SendMessage(CSerialiser& reply) = 0;
+	void	HandleMessage(CDeserialiser& message);
+};
+
+
+};
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/CColour.hpp b/Usermode/Applications/axwin4_src/Server/include/CColour.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d47d7e044ec1a444b40c8b76654299affaab602e
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/CColour.hpp
@@ -0,0 +1,93 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * CColour.hpp
+ * - Generic colour handling and blending
+ */
+#ifndef _CCOLOUR_HPP_
+#define _CCOLOUR_HPP_
+
+namespace AxWin {
+
+class CColour
+{
+	static const uint8_t	uint8_max = 0xFF;
+	static const unsigned int comp_max = 0x7FFF;
+	
+	unsigned int	m_alpha;
+	unsigned int	m_red;
+	unsigned int	m_green;
+	unsigned int	m_blue;
+
+private:
+	static unsigned int u8_to_ui(uint8_t u8v) {
+		return (unsigned int)u8v * comp_max / uint8_max;
+	}
+	static uint8_t ui_to_u8(unsigned int uiv) {
+		return uiv * uint8_max / comp_max;
+	}
+	// Perform an alpha-based blend on two components
+	static unsigned int alpha_blend(unsigned int alpha_comp, unsigned int left, unsigned int right) {
+		return (left * (comp_max - alpha_comp) + right * alpha_comp) / comp_max;
+	}
+	// Float values:
+	// - infinity == saturation, 1 == nothing
+	// fv = MAX / (MAX - uiv)
+	static float ui_to_float(unsigned int uiv) {
+		return (float)comp_max / (comp_max - uiv);
+	}
+	// uiv = MAX - MAX / fv
+	static unsigned int float_to_ui(float fv) {
+		return comp_max - comp_max / fv;
+	}
+	// perform a non-oversaturating blend of two colours (using an inverse relationship)
+	static unsigned int add_blend(unsigned int a, unsigned int b) {
+		return float_to_ui( ui_to_float(a) + ui_to_float(b) );
+	}
+
+	CColour(unsigned int r, unsigned int g, unsigned int b, unsigned int a):
+		m_alpha(a), m_red(r), m_green(g), m_blue(b)
+	{
+	}
+public:
+
+	static CColour from_argb(uint32_t val) {
+		return CColour(
+			u8_to_ui((val>>16) & 0xFF),
+			u8_to_ui((val>> 8) & 0xFF),
+			u8_to_ui((val>> 0) & 0xFF),
+			u8_to_ui((val>>24) & 0xFF)
+			);
+	}
+	
+	uint32_t to_argb() const {
+		uint32_t	rv = 0;
+		rv |= (uint32_t)ui_to_u8(m_red)   << 16;
+		rv |= (uint32_t)ui_to_u8(m_green) <<  8;
+		rv |= (uint32_t)ui_to_u8(m_blue)  <<  0;
+		rv |= (uint32_t)ui_to_u8(m_alpha) << 24;
+		return rv;
+	}
+	
+	// performs a blend of the two colours, maintaining the target alpha, using the source alpha as the blend control
+	CColour& blend(const CColour& other) {
+		m_red   = alpha_blend(other.m_alpha, m_red  , other.m_red  );
+		m_green = alpha_blend(other.m_alpha, m_green, other.m_green);
+		m_blue  = alpha_blend(other.m_alpha, m_blue , other.m_blue );
+		return *this;
+	}
+	// Add all components
+	CColour& operator+(const CColour& other) {
+		m_alpha = add_blend(m_alpha, other.m_alpha);
+		m_red   = add_blend(m_red  , other.m_red  );
+		m_green = add_blend(m_green, other.m_green);
+		m_blue  = add_blend(m_blue , other.m_blue );
+		return *this;
+	}
+};
+
+}	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/CCompositor.hpp b/Usermode/Applications/axwin4_src/Server/include/CCompositor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe24303929c9502981e59b704053d9d25dbec15f
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/CCompositor.hpp
@@ -0,0 +1,83 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * CCompositor.hpp
+ * - Window Compositor
+ */
+#ifndef _CCOMPOSITOR_H_
+#define _CCOMPOSITOR_H_
+
+#include <string>
+#include <list>
+#include <vector>
+#include "CRect.hpp"
+#include "CWindow.hpp"
+
+namespace AxWin {
+
+class CClient;
+class CVideo;
+
+
+enum eMouseButton
+{
+	MOUSEBTN_MAIN,	// Left
+	MOUSEBTN_SECONDARY,	// Right
+	MOUSEBTN_MIDDLE,	// Scroll wheel
+	MOUSEBTN_BTN4,
+	MOUSEBTN_BTN5,
+};
+
+class CWindowIDBuffer
+{
+	struct TWindowID
+	{
+		uint16_t	Client;
+		uint16_t	Window;
+	};
+	unsigned int	m_w;
+	::std::vector<TWindowID>	m_buf;
+public:
+	CWindowIDBuffer(unsigned int W, unsigned int H);
+	
+	void set(unsigned int X, unsigned int Y, unsigned int W, unsigned int H, CWindow* win);
+	CWindow* get(unsigned int X, unsigned int Y);
+};
+
+class CCompositor
+{
+	CVideo&	m_video;
+	::std::list<CRect>	m_damageRects;
+	::std::list<CWindow*>	m_windows;
+	CWindow*	m_focussed_window;
+
+	CWindowIDBuffer	m_windowIDBuffer;	// One 32-bit value per pixel
+	
+public:
+	CCompositor(CVideo& video);
+
+	CWindow* CreateWindow(CClient& client, const ::std::string& name);
+
+	void	ShowWindow(CWindow* window);
+	void	HideWindow(CWindow* window);
+
+	bool	GetScreenDims(unsigned int ScrenID, unsigned int *Width, unsigned int *Height);
+
+	void	Redraw();
+	void	DamageArea(const CRect& rect);
+	void	BlitFromSurface(const CSurface& dest, const CRect& src_rect);
+	
+	void	MouseMove(unsigned int Cursor, unsigned int X, unsigned int Y, int dX, int dY);
+	void	MouseButton(unsigned int Cursor, unsigned int X, unsigned int Y, eMouseButton Button, bool Press);
+	
+	void	KeyState(unsigned int KeyboardID, uint32_t KeySym, bool Press, uint32_t Codepoint);
+public:
+	CWindow*	getWindowForCoord(unsigned int X, unsigned int Y);
+};
+
+
+};
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/CConfig.hpp b/Usermode/Applications/axwin4_src/Server/include/CConfig.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..aff7fc8cf77a429b455daee1800429c94c024ed9
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/CConfig.hpp
@@ -0,0 +1,32 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * CConfig.hpp
+ * - Configuration class
+ */
+#ifndef _CCONFIG_H_
+#define _CCONFIG_H_
+
+#include "CConfigInput.hpp"
+#include "CConfigVideo.hpp"
+#include "CConfigIPC.hpp"
+
+namespace AxWin {
+
+class CConfig
+{
+public:
+	CConfig();
+	
+	bool parseCommandline(int argc, char *argv[]);
+	
+	CConfigInput	m_input;
+	CConfigVideo	m_video;
+	CConfigIPC	m_ipc;
+};
+
+}
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/CConfigIPC.hpp b/Usermode/Applications/axwin4_src/Server/include/CConfigIPC.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..053e37616c144d26c04cd59a419ed0bf19b283fd
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/CConfigIPC.hpp
@@ -0,0 +1,31 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * CConfigIPC.hpp
+ * - Configuration class
+ */
+#ifndef _CCONFIGIPC_H_
+#define _CCONFIGIPC_H_
+
+#include <string>
+
+namespace AxWin {
+
+class CConfigIPC_Channel
+{
+public:
+	::std::string	m_name;
+	::std::string	m_argument;
+};
+
+class CConfigIPC
+{
+public:
+	CConfigIPC();
+};
+
+};	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/CConfigInput.hpp b/Usermode/Applications/axwin4_src/Server/include/CConfigInput.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..28553ca0df1e668189c557773d1764f6d0117767
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/CConfigInput.hpp
@@ -0,0 +1,26 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * CConfig.hpp
+ * - Configuration class
+ */
+#ifndef _CCONFIGINPUT_H_
+#define _CCONFIGINPUT_H_
+
+#include <string>
+
+namespace AxWin {
+
+class CConfigInput
+{
+public:
+	CConfigInput();
+	
+	::std::string	mouse_device;
+};
+
+};	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/CConfigVideo.hpp b/Usermode/Applications/axwin4_src/Server/include/CConfigVideo.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9a81fb706942f2b36b792cc2edc51eaaa3b0d0fd
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/CConfigVideo.hpp
@@ -0,0 +1,22 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * CConfigVideo.hpp
+ * - Configuration class
+ */
+#ifndef _CCONFIGVIDEO_H_
+#define _CCONFIGVIDEO_H_
+
+namespace AxWin {
+
+class CConfigVideo
+{
+public:
+	CConfigVideo();
+};
+
+};	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/CIPCChannel_AcessIPCPipe.hpp b/Usermode/Applications/axwin4_src/Server/include/CIPCChannel_AcessIPCPipe.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c4ec004206fa7922b5832f5457f098a5aa3e2781
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/CIPCChannel_AcessIPCPipe.hpp
@@ -0,0 +1,48 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * CIPCChannel_AcessIPCPipe.hpp
+ * - IPC Channel :: Acess' IPC Pipe /Devices/ipcpipe/<name>
+ */
+#ifndef _CIPCCHANNEL_ACESSIPCPIPE_HPP_
+#define _CIPCCHANNEL_ACESSIPCPIPE_HPP_
+
+#include <IIPCChannel.hpp>
+#include <CClient.hpp>
+#include <string>
+#include <list>
+
+namespace AxWin {
+
+class CClient_AcessIPCPipe:
+	public CClient
+{
+	friend class CIPCChannel_AcessIPCPipe;
+	 int	m_fd;
+public:
+	CClient_AcessIPCPipe(IIPCChannel& channel, int fd);
+	~CClient_AcessIPCPipe();
+	
+	void SendMessage(CSerialiser& message);
+	
+	void HandleReceive();
+};
+
+class CIPCChannel_AcessIPCPipe:
+	public IIPCChannel
+{
+	 int	m_fd;
+	::std::list<CClient_AcessIPCPipe>	m_clients;
+public:
+	CIPCChannel_AcessIPCPipe(const ::std::string& suffix);
+	virtual ~CIPCChannel_AcessIPCPipe();
+	
+	virtual int  FillSelect(fd_set& rfds);
+	virtual void HandleSelect(const fd_set& rfds);
+};
+
+}	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/CRect.hpp b/Usermode/Applications/axwin4_src/Server/include/CRect.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3c344b259f796e62aaf27dd992fc5cb6a3f5832b
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/CRect.hpp
@@ -0,0 +1,40 @@
+/*
+ */
+#ifndef _CRECT_H_
+#define _CRECT_H_
+
+namespace AxWin {
+
+class CRect
+{
+public:
+	CRect():
+		CRect(0,0,0,0)
+	{
+	};
+	CRect(int X, int Y, unsigned int W, unsigned int H);
+	
+	void Translate(int DX, int DY) {
+		m_x += DX;	m_x2 += DX;
+		m_y += DY;	m_y2 += DY;
+	}
+	void Move(int NewX, int NewY);
+	void Resize(int NewW, int NewH);
+	
+	bool HasIntersection(const CRect& other) const;
+	CRect Intersection(const CRect& other) const;
+	
+	CRect RelativeIntersection(const CRect& area);
+	
+	int	m_x;
+	int	m_y;
+	int	m_w;
+	int	m_h;
+	int	m_x2;
+	int	m_y2;
+};
+
+};	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/CSurface.hpp b/Usermode/Applications/axwin4_src/Server/include/CSurface.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ac20d0f57019b3c0c9a4232601bc3e171446c4a8
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/CSurface.hpp
@@ -0,0 +1,35 @@
+/*
+ */
+#ifndef _CSURFACE_H_
+#define _CSURFACE_H_
+
+#include <cstdint>
+#include "CRect.hpp"
+
+namespace AxWin {
+
+class CSurface
+{
+public:
+	CSurface(int x, int y, unsigned int w, unsigned int h);
+	~CSurface();
+
+	uint64_t GetSHMHandle();
+	
+	void Resize(unsigned int new_w, unsigned int new_h);
+	
+	void DrawScanline(unsigned int row, unsigned int x_ofs, unsigned int w, const void* data);
+	void BlendScanline(unsigned int row, unsigned int x_ofs, unsigned int w, const void* data);
+	void FillScanline(unsigned int row, unsigned int x, unsigned int w, uint32_t colour);
+	const uint32_t* GetScanline(unsigned int row, unsigned int x_ofs) const;
+	
+	CRect	m_rect;
+	uint32_t*	m_data;
+private:
+	int	m_fd;
+};
+
+};	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/CWindow.hpp b/Usermode/Applications/axwin4_src/Server/include/CWindow.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e9026593098b65d063c1e36935936f8070fb7696
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/CWindow.hpp
@@ -0,0 +1,62 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * CWindow.hpp
+ * - Window class
+ */
+#ifndef _CWINDOW_HPP_
+#define _CWINDOW_HPP_
+
+#include <string>
+#include <vector>
+#include <cstdint>
+#include "CRect.hpp"
+#include "CSurface.hpp"
+
+namespace AxWin {
+
+class CClient;
+class CCompositor;
+class CRegion;
+
+class CWindow
+{
+public:
+	CWindow(CCompositor& compositor, CClient& client, const ::std::string &name, unsigned int id);
+	~CWindow();
+	
+	const CClient& client() const { return m_client; }
+	const unsigned int id() const { return m_id; }
+	
+	void Repaint(const CRect& rect);
+
+	void Show(bool bShow);	
+	void Move(int X, int Y);
+	void Resize(unsigned int W, unsigned int H);
+	void SetFlags(uint32_t Flags);
+	
+	uint64_t ShareSurface();
+	
+	void MouseButton(int ButtonID, int X, int Y, bool Down);
+	void MouseMove(int NewX, int NewY);
+	void KeyEvent(::uint32_t Scancode, const ::std::string &Translated, bool Down);
+
+	void DrawScanline(unsigned int row, unsigned int x, unsigned int w, const uint8_t *data);
+	void FillScanline(unsigned int row, unsigned int x, unsigned int w, const uint32_t colour);
+	
+	bool	m_is_shown;
+	CSurface	m_surface;
+private:
+	CCompositor&	m_compositor;
+	CClient&	m_client;
+	unsigned int	m_id;
+	const ::std::string	m_name;
+	::std::vector<CRegion*>	m_regions;
+};
+
+};	// namespace AxWin
+
+#endif
+
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/IFontFace.hpp b/Usermode/Applications/axwin4_src/Server/include/IFontFace.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..76ba042259edd2c20de4401281560c31458c5e7d
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/IFontFace.hpp
@@ -0,0 +1,27 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * IFont.hpp
+ * - Text drawing (font rendering) primitive
+ */
+#ifndef _IFONT_HPP_
+#define _IFONT_HPP_
+
+#include <string>
+#include "CRect.hpp"
+#include "CSurface.hpp"
+
+namespace AxWin {
+
+class IFontFace
+{
+public:
+	virtual CRect Size(const ::std::string& text, unsigned int Size) const = 0;
+	virtual void Render(CSurface& dest, const CRect& rect, const ::std::string& text, unsigned int Size) = 0;
+};
+
+};
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/IIPCChannel.hpp b/Usermode/Applications/axwin4_src/Server/include/IIPCChannel.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5dc88e2f91158f0adecec285d8bd4f02d04b51d9
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/IIPCChannel.hpp
@@ -0,0 +1,30 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * IIPCChannel.hpp
+ * - IPC Channel interface
+ */
+#ifndef _IIPCCHANNEL_H_
+#define _IIPCCHANNEL_H_
+
+extern "C" {
+#include <acess/sys.h>
+}
+
+namespace AxWin {
+
+class IIPCChannel
+{
+public:
+	virtual ~IIPCChannel();
+	
+	virtual int	FillSelect(::fd_set& rfds) = 0;
+	virtual void	HandleSelect(const ::fd_set& rfds) = 0;
+};
+
+
+};
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/IRegion.hpp b/Usermode/Applications/axwin4_src/Server/include/IRegion.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8208aa573aa3700174ef790d9a51355c461eeed3
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/IRegion.hpp
@@ -0,0 +1,32 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * IRegion.hpp
+ * - Representation of a primitive region in a window (part of the window render list)
+ */
+#ifndef _IREGION_HPP_
+#define _IREGION_HPP_
+
+#include <string>
+#include <vector>
+#include "CRect.hpp"
+
+namespace AxWin {
+
+class IRegion
+{
+protected:
+	CWindow&	m_parentWindow;
+public:
+	virtual IRegion(CWindow& parent, const ::AxWin::Rect& position);
+	virtual ~IRegion();
+	
+	virtual void Redraw(const ::AxWin::Rect& area) = 0;
+	virtual bool SetAttr(unsigned int Index, const IPCAttrib& Value) = 0;
+};
+
+};
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/IVideo.hpp b/Usermode/Applications/axwin4_src/Server/include/IVideo.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..aa4fc52bfa1f5abcc160b7731d2bdc295535829a
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/IVideo.hpp
@@ -0,0 +1,28 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * IVideo.hpp
+ * - Graphics backend abstraction
+ */
+#ifndef _IVIDEO_HPP_
+#define _IVIDEO_HPP_
+
+namespace AxWin {
+
+class IVideo
+{
+public:
+	virtual ~IVideo();
+	
+	// Allocate a new hardware surface
+	IHWSurface&	AllocateHWSurface(uint16_t Width, uint16_t Height);
+	
+	// Request redraw of backbuffer
+	void	Flip();
+};
+
+};
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/IWindow.hpp b/Usermode/Applications/axwin4_src/Server/include/IWindow.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..83dd97f81b7c18d649f0f51fb458dea8c2c0deb9
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/IWindow.hpp
@@ -0,0 +1,36 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * IWindow.hpp
+ * - Window abstract base class
+ */
+#ifndef _IWINDOW_HPP_
+#define _IWINDOW_HPP_
+
+#include <string>
+#include <vector>
+#include <cstdint>
+#include "CRect.hpp"
+
+namespace AxWin {
+
+class IWindow
+{
+public:
+	IWindow(const ::std::string &name);
+	virtual ~IWindow();
+	
+	virtual void Repaint() = 0;
+	
+	virtual void MouseButton(int ButtonID, int X, int Y, bool Down);
+	virtual void MouseMove(int NewX, int NewY);
+	virtual void KeyEvent(::uint32_t Scancode, const ::std::string &Translated, bool Down);
+protected:
+	const ::std::string	m_name;
+};
+
+}	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/common.hpp b/Usermode/Applications/axwin4_src/Server/include/common.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1a1facc3bceedecdd9b8abfc6ca03e7be57324c0
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/common.hpp
@@ -0,0 +1,26 @@
+/*
+ */
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include <exception>
+
+namespace AxWin {
+
+class InitFailure:
+	public ::std::exception
+{
+	const char *m_what;
+public:
+	InitFailure(const char *reason):
+		m_what(reason)
+	{
+	}
+	
+	virtual const char* what() const throw();
+};
+
+}	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/compositor.hpp b/Usermode/Applications/axwin4_src/Server/include/compositor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..403d943c23e99712dbc9d3ab1dcb66c04e3790f0
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/compositor.hpp
@@ -0,0 +1,22 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * compositor.hpp
+ * - Compositor Interface Header
+ */
+#ifndef _COMPOSITOR_H_
+#define _COMPOSITOR_H_
+
+namespace AxWin {
+
+class CCompositor;
+
+namespace Compositor {
+
+};	// namespace Compositor
+
+};	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/draw_control.hpp b/Usermode/Applications/axwin4_src/Server/include/draw_control.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3797e2e98068d6fe3f983ba76701e3730a03f251
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/draw_control.hpp
@@ -0,0 +1,37 @@
+/*
+ */
+#ifndef _DRAW_CONTROL_HPP_
+#define _DRAW_CONTROL_HPP_
+
+#include <vector>
+#include <string>
+#include <cstdint>
+#include <CSurface.hpp>
+
+namespace AxWin {
+
+class CControl
+{
+	unsigned int	m_edge_x;
+	unsigned int	m_edge_y;
+	unsigned int	m_fill_x;
+	unsigned int	m_fill_y;
+	unsigned int	m_inner_x;
+	unsigned int	m_inner_y;
+	::std::vector<uint32_t>	m_data;
+public:
+	CControl(int EdgeX, int FillX, int InnerX, int EdgeY, int FillY, int InnerY, ::std::vector<uint32_t>&& data);
+	void Render(CSurface& dest, const CRect& rect) const;
+	
+	static const CControl*	GetByName(const ::std::string& name);
+	static const CControl*	GetByID(uint16_t id);
+	
+private:
+	void renderLine(CSurface& dest, int y, ::std::vector<uint32_t>& scanline, const CRect& rect, const uint32_t* ctrl_line) const;
+};
+
+
+}
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/draw_text.hpp b/Usermode/Applications/axwin4_src/Server/include/draw_text.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4bf28a61dd56c0182f03ed6ebddf77fd289f4878
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/draw_text.hpp
@@ -0,0 +1,47 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * draw_text.hpp
+ * - Text drawing classes
+ */
+#ifndef _DRAW_TEXT_HPP_
+#define _DRAW_TEXT_HPP_
+
+#include "IFontFace.hpp"
+
+namespace AxWin {
+
+class CFontFallback:
+	public IFontFace
+{
+public:
+	CFontFallback();
+
+	CRect Size(const ::std::string& text, unsigned int Size) const override;
+	void Render(CSurface& dest, const CRect& rect, const ::std::string& text, unsigned int Size) override;
+private:
+	void renderAtRes(CSurface& dest, const CRect& rect, uint32_t cp, unsigned int Size, uint32_t FGC);
+	static uint8_t getValueAtPt(const uint8_t* char_ptr, unsigned int xf16, unsigned int yf16);
+	static uint8_t getValueAtRaw(const uint8_t* char_ptr, unsigned int x, unsigned int y);
+	unsigned int unicodeToCharmap(uint32_t cp) const;
+};
+
+#if FREETYPE_ENABLED
+class CFontFT:
+	public IFontFace
+{
+	FT_Face	m_face;
+public:
+	CFontFT(const char *Filename);
+	~CFontFT();
+
+	CRect Size(const ::std::string& text, unsigned int Size) const override;
+	void Render(CSurface& dest, const CRect& rect, const ::std::string& text, unsigned int Size) override;
+};
+#endif	// FREETYPE_ENABLED
+
+}
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/input.hpp b/Usermode/Applications/axwin4_src/Server/include/input.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f8b27cea1c6ccbff3be5c700b6d12a97e0d40628
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/input.hpp
@@ -0,0 +1,35 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * input.hpp
+ * - Input Interface Header
+ */
+#ifndef _INPUT_H_
+#define _INPUT_H_
+
+#include <acess/sys.h>
+
+namespace AxWin {
+
+class CCompositor;
+
+class CInput
+{
+	CCompositor&	m_compositor;
+	 int	m_keyboardFD;
+	 int	m_mouseFD;
+	
+	unsigned int m_mouseX;
+	unsigned int m_mouseY;
+	unsigned int m_mouseBtns;
+public:
+	CInput(const CConfigInput& config, CCompositor& compositor);
+	 int FillSelect(::fd_set& rfds);
+	void HandleSelect(::fd_set& rfds);
+};
+
+};	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/ipc.hpp b/Usermode/Applications/axwin4_src/Server/include/ipc.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..50b0dbe25e3041f129980679d83739cd578c03bb
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/ipc.hpp
@@ -0,0 +1,55 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * ipc.hpp
+ * - IPC Interface Header
+ */
+#ifndef _IPC_H_
+#define _IPC_H_
+
+#include <exception>
+
+extern "C" {
+#include <acess/sys.h>
+};
+
+#include <serialisation.hpp>
+#include <CConfigIPC.hpp>
+
+namespace AxWin {
+
+class CCompositor;
+class CClient;
+
+namespace IPC {
+
+extern void	Initialise(const CConfigIPC& config, CCompositor& compositor);
+extern int	FillSelect(::fd_set& rfds);
+extern void	HandleSelect(const ::fd_set& rfds);
+extern void	RegisterClient(CClient& client);
+extern CClient*	GetClientByID(uint16_t id);
+extern void	DeregisterClient(CClient& client);
+
+extern void	SendMessage_NotifyDims(CClient& client, unsigned int WinID, unsigned int NewW, unsigned int NewH);
+extern void	SendMessage_MouseButton(CClient& client, unsigned int WinID, unsigned int X, unsigned int Y, uint8_t Button, bool Pressed);
+extern void	SendMessage_MouseMove(CClient& client, unsigned int WinID, unsigned int X, unsigned int Y);
+extern void	SendMessage_KeyEvent(CClient& client, unsigned int WinID, uint32_t KeySym, bool Pressed, const char *Translated);
+
+extern void	HandleMessage(CClient& client, CDeserialiser& message);
+
+class CClientFailure:
+	public ::std::exception
+{
+	const std::string m_what;
+public:
+	CClientFailure(std::string&& what);
+	~CClientFailure() throw();
+	const char *what() const throw();
+};
+
+};	// namespace IPC
+};	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/timing.hpp b/Usermode/Applications/axwin4_src/Server/include/timing.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..df5d814c5be9846c1ee218902b64f4d9e195e9de
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/timing.hpp
@@ -0,0 +1,23 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * timing.hpp
+ * - Timing Interface Header
+ */
+#ifndef _TIMING_H_
+#define _TIMING_H_
+
+#include <cstdint>
+
+namespace AxWin {
+namespace Timing {
+
+extern ::int64_t	GetTimeToNextEvent();
+extern void	CheckEvents();
+
+};	// namespace Timing
+};	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/include/video.hpp b/Usermode/Applications/axwin4_src/Server/include/video.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3738099b4e6e1d8a636b8951dc45d136c86af2ee
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/include/video.hpp
@@ -0,0 +1,41 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang) 
+ *
+ * video.hpp
+ * - Graphics Interface Header
+ */
+#ifndef _VIDEO_H_
+#define _VIDEO_H_
+
+#include <cstdint>
+#include "CConfigVideo.hpp"
+
+namespace AxWin {
+
+class CVideo
+{
+	 int	m_fd;
+	unsigned int	m_width;
+	unsigned int	m_height;
+	 int	m_bufferFormat;
+public:
+	CVideo(const CConfigVideo& config);
+
+	void GetDims(unsigned int& w, unsigned int& h);	
+	unsigned int width()  const { return m_width;  }
+	unsigned int height() const { return m_height; }
+
+	void BlitLine(const uint32_t* src, unsigned int dst_y, unsigned int dst_x, unsigned int width);
+	void Flush();
+	void SetCursorPos(int X, int Y);
+
+private:
+	void SetBufFormat(unsigned int FormatID);
+	void SetCursorBitmap();
+};
+
+};
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/input.cpp b/Usermode/Applications/axwin4_src/Server/input.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a0ab64663b197446e9216a2b92f25d7be5b9dccb
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/input.cpp
@@ -0,0 +1,154 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * input.cpp
+ * - Input
+ */
+#include <CConfigInput.hpp>
+#include <input.hpp>
+#include <CCompositor.hpp>
+#include <algorithm>
+#include <acess/devices/joystick.h>
+#include <cerrno>
+#include <system_error>
+
+namespace AxWin {
+
+CInput::CInput(const ::AxWin::CConfigInput& config, CCompositor& compositor):
+	m_compositor(compositor),
+	m_keyboardFD(0),
+	m_mouseFD(-1)
+{
+	m_mouseFD = _SysOpen(config.mouse_device.c_str(), OPENFLAG_READ|OPENFLAG_WRITE);
+	if( m_mouseFD == -1 )
+		throw ::std::system_error(errno, ::std::system_category());
+	
+	m_mouseX = 640/2;
+	m_mouseY = 480/2;
+	
+	struct mouse_attribute	attr;
+	// X : Limit + Position
+	attr.Num = 0;
+	attr.Value = 640;
+	_SysIOCtl(m_mouseFD, JOY_IOCTL_GETSETAXISLIMIT, &attr);
+	attr.Value = m_mouseX;
+	_SysIOCtl(m_mouseFD, JOY_IOCTL_GETSETAXISPOSITION, &attr);
+	// Y: Limit + Position
+	attr.Num = 1;
+	attr.Value = 480;
+	_SysIOCtl(m_mouseFD, JOY_IOCTL_GETSETAXISLIMIT, &attr);
+	attr.Value = m_mouseY;
+	_SysIOCtl(m_mouseFD, JOY_IOCTL_GETSETAXISPOSITION, &attr);
+}
+
+int CInput::FillSelect(::fd_set& rfds)
+{
+	FD_SET(m_keyboardFD, &rfds);
+	if( m_mouseFD != -1 )
+		FD_SET(m_mouseFD, &rfds);
+	return ::std::max(m_keyboardFD, m_mouseFD)+1;
+}
+
+void CInput::HandleSelect(::fd_set& rfds)
+{
+	if( FD_ISSET(m_keyboardFD, &rfds) )
+	{
+		uint32_t	codepoint;
+		static uint32_t	scancode;
+		#define KEY_CODEPOINT_MASK	0x3FFFFFFF
+		
+		size_t readlen = _SysRead(m_keyboardFD, &codepoint, sizeof(codepoint));
+		if( readlen != sizeof(codepoint) )
+		{
+			// oops, error
+			_SysDebug("Terminal read failed? (%i != %i)", readlen, sizeof(codepoint));
+		}
+	
+//		_SysDebug("Keypress 0x%x", codepoint);
+	
+		switch(codepoint & 0xC0000000)
+		{
+		case 0x00000000:	// Key pressed
+			m_compositor.KeyState(0, scancode, true, codepoint & KEY_CODEPOINT_MASK);
+			break;
+		case 0x40000000:	// Key release
+			m_compositor.KeyState(0, scancode, false, codepoint & KEY_CODEPOINT_MASK);
+			scancode = 0;
+			break;
+		case 0x80000000:	// Key refire
+			m_compositor.KeyState(0, scancode, true, codepoint & KEY_CODEPOINT_MASK);
+			scancode = 0;
+			break;
+		case 0xC0000000:	// Raw scancode
+			scancode = codepoint & KEY_CODEPOINT_MASK;
+			break;
+		}
+	}
+	
+	if( m_mouseFD != -1 && FD_ISSET(m_mouseFD, &rfds) )
+	{
+		const int c_n_axies = 4;
+		const int c_n_buttons = 5;
+		struct mouse_axis	*axies;
+		uint8_t	*buttons;
+
+		char data[sizeof(struct mouse_header) + sizeof(*axies)*c_n_axies + c_n_buttons];
+		struct mouse_header	*mouseinfo = (struct mouse_header*)data;
+
+		_SysSeek(m_mouseFD, 0, SEEK_SET);
+		int len = _SysRead(m_mouseFD, data, sizeof(data));
+		if( len < 0 )
+			throw ::std::system_error(errno, ::std::system_category());
+		
+		len -= sizeof(*mouseinfo);
+		if( len < 0 ) {
+			_SysDebug("Mouse data undersized (%i bytes short on header)", len);
+			return ;
+		}
+		if( mouseinfo->NAxies > c_n_axies || mouseinfo->NButtons > c_n_buttons ) {
+			_SysDebug("%i axies, %i buttons above prealloc counts (%i, %i)",
+				mouseinfo->NAxies, mouseinfo->NButtons, c_n_axies, c_n_buttons
+				);
+			return ;
+		}
+		if( len < sizeof(*axies)*mouseinfo->NAxies + mouseinfo->NButtons ) {
+			_SysDebug("Mouse data undersized (body doesn't fit %i < %i)",
+				len, sizeof(*axies)*mouseinfo->NAxies + mouseinfo->NButtons
+				);
+			return ;
+		}
+
+		// What? No X/Y?
+		if( mouseinfo->NAxies < 2 ) {
+			_SysDebug("Mouse data lacks X/Y");
+			return ;
+		}
+	
+		axies = (struct mouse_axis*)( mouseinfo + 1 );
+		buttons = (uint8_t*)( axies + mouseinfo->NAxies );
+
+		// TODO: Use cursor range only to caputre motion (ignore reported position)
+		m_compositor.MouseMove(0,
+			m_mouseX, m_mouseY,
+			axies[0].CursorPos - m_mouseX, axies[1].CursorPos - m_mouseY
+			);
+		m_mouseX = axies[0].CursorPos;
+		m_mouseY = axies[1].CursorPos;
+
+		for( int i = 0; i < mouseinfo->NButtons; i ++ )
+		{
+			 int	bit = 1 << i;
+			 int	cur = buttons[i] > 128;
+			if( !!(m_mouseBtns & bit) != cur )
+			{
+				m_compositor.MouseButton(0, m_mouseX, m_mouseY, (eMouseButton)i, cur);
+				// Flip button state
+				m_mouseBtns ^= bit;
+			}
+		}
+	}
+}
+
+};	// namespace AxWin
+
diff --git a/Usermode/Applications/axwin4_src/Server/ipc.cpp b/Usermode/Applications/axwin4_src/Server/ipc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f522ac22767ea79b4d3b9808d1e924f65a528459
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/ipc.cpp
@@ -0,0 +1,514 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * ipc.cpp
+ * - Client-Server communication (dispatch)
+ */
+#define __STDC_LIMIT_MACROS
+#include <ipc.hpp>
+#include <list>
+#include <IIPCChannel.hpp>
+#include <algorithm>
+#include <CClient.hpp>
+#include <serialisation.hpp>
+#include <ipc_proto.hpp>
+#include <CCompositor.hpp>
+extern "C" {
+#include <assert.h>
+};
+#include <CIPCChannel_AcessIPCPipe.hpp>
+#include <draw_control.hpp>
+#include <draw_text.hpp>
+
+namespace AxWin {
+namespace IPC {
+
+CCompositor*	gpCompositor;
+::std::list<IIPCChannel*>	glChannels;
+::std::map<uint16_t,CClient*>	glClients;
+uint16_t	giNextClient = 1;
+
+void Initialise(const CConfigIPC& config, CCompositor& compositor)
+{
+	gpCompositor = &compositor;
+	
+	::std::string pipe_basepath = "axwin4";
+	glChannels.push_back( new CIPCChannel_AcessIPCPipe( pipe_basepath ) );
+
+	//glChannels.push_back( new CIPCChannel_TCP("0.0.0.0:2100") );
+	
+	//for( auto channel : config.m_channels )
+	//{
+	//	channels.push_back(  );
+	//}
+}
+
+int FillSelect(fd_set& rfds)
+{
+	int ret = 0;
+	for( const auto channel : glChannels )
+	{
+		assert(channel);
+		ret = ::std::max(ret, channel->FillSelect(rfds));
+	}
+	return ret;
+}
+
+void HandleSelect(const fd_set& rfds)
+{
+	for( const auto channel : glChannels )
+	{
+		assert(channel);
+		channel->HandleSelect(rfds);
+	}
+}
+
+void RegisterClient(CClient& client)
+{
+	_SysDebug("RegisterClient(&client=%p)", &client);
+	// allocate a client ID, and save
+	for( int i = 0; i < 100; i ++ )
+	{
+		uint16_t id = giNextClient++;
+		if(giNextClient == 0)	giNextClient = 1;
+		auto r = glClients.insert( ::std::pair<uint16_t,CClient*>(id, &client) );
+		if( r.second == true )
+		{
+			client.set_id(id);
+			return;
+		}
+	}
+	// Wut? 100 attempts and fail!
+	assert(!"Todo - Better way of handling client ID reuse");
+}
+
+CClient* GetClientByID(uint16_t id)
+{
+	auto it = glClients.find(id);
+	if(it == glClients.end()) {
+		//_SysDebug("Client %i not registered", id);
+		return nullptr;
+	}
+	else {
+		//_SysDebug("Client %i %i = %p", id, it->first, it->second);
+		return it->second;
+	}
+}
+
+void DeregisterClient(CClient& client)
+{
+	glClients.erase( client.id() );
+}
+
+
+void SendMessage_NotifyDims(CClient& client, unsigned int WinID, unsigned int NewW, unsigned int NewH)
+{
+	_SysDebug("TODO: IPC::SendMessage_NotifyDims");
+}
+void SendMessage_MouseButton(CClient& client, unsigned int WinID, unsigned int X, unsigned int Y, uint8_t Button, bool Pressed)
+{
+	CSerialiser	msg;
+	msg.WriteU8(IPCMSG_INPUTEVENT);
+	msg.WriteU8(IPC_INEV_MOUSEBTN);
+	msg.WriteU16(WinID);
+	msg.WriteU16(X);
+	msg.WriteU16(Y);
+	msg.WriteU8(Button);
+	msg.WriteU8(Pressed ? 0 : 1);
+	client.SendMessage(msg);
+}
+void SendMessage_MouseMove(CClient& client, unsigned int WinID, unsigned int X, unsigned int Y)
+{
+	_SysDebug("TODO: IPC::SendMessage_MouseMove");
+}
+void SendMessage_KeyEvent(CClient& client, unsigned int WinID, uint32_t KeySym, bool Pressed, const char *Translated)
+{
+	CSerialiser	msg;
+	msg.WriteU8(IPCMSG_INPUTEVENT);
+	msg.WriteU8(IPC_INEV_KEYBOARD);
+	msg.WriteU16(WinID);
+	msg.WriteU16(KeySym);
+	msg.WriteU8(Pressed ? 0 : 1);
+	msg.WriteString(Translated);
+	client.SendMessage(msg);
+}
+
+
+void HandleMessage_Nop(CClient& client, CDeserialiser& message)
+{
+	// Do nothing
+}
+void HandleMessage_Reply(CClient& client, CDeserialiser& message)
+{
+	// Reply to a sent message
+	// - Not many messages need server-bound replies
+	int orig_command = message.ReadU8();
+	switch(orig_command)
+	{
+	case IPCMSG_PING:
+		// Ping reply, mark client as still responding
+		break;
+	default:
+		// Unexpected reply
+		break;
+	}
+}
+
+void HandleMessage_Ping(CClient& client, CDeserialiser& message)
+{
+	// A client has asked for a ping, we pong them back
+	CSerialiser	reply;
+	reply.WriteU8(IPCMSG_REPLY);
+	reply.WriteU8(IPCMSG_PING);
+	client.SendMessage(reply);
+}
+
+void HandleMessage_GetGlobalAttr(CClient& client, CDeserialiser& message)
+{
+	uint16_t	attr_id = message.ReadU16();
+	
+	CSerialiser	reply;
+	reply.WriteU8(IPCMSG_REPLY);
+	reply.WriteU8(IPCMSG_GETGLOBAL);
+	reply.WriteU16(attr_id);
+	
+	switch(attr_id)
+	{
+	case IPC_GLOBATTR_SCREENDIMS: {
+		uint8_t	screen_id = message.ReadU8();
+		unsigned int w, h;
+		gpCompositor->GetScreenDims(screen_id, &w, &h);
+		reply.WriteU16( (w <= UINT16_MAX ? w : UINT16_MAX) );
+		reply.WriteU16( (h <= UINT16_MAX ? h : UINT16_MAX) );
+		break; }
+	case IPC_GLOBATTR_MAXAREA:
+		assert(!"TODO: IPC_GLOBATTR_MAXAREA");
+		break;
+	default:
+		throw IPC::CClientFailure("Bad global attribute ID");
+	}
+	
+	client.SendMessage(reply);
+}
+
+void HandleMessage_SetGlobalAttr(CClient& client, CDeserialiser& message)
+{
+	uint16_t	attr_id = message.ReadU16();
+	
+	switch(attr_id)
+	{
+	case IPC_GLOBATTR_SCREENDIMS:
+		// Setting readonly
+		break;
+	case IPC_GLOBATTR_MAXAREA:
+		assert(!"TODO: IPC_GLOBATTR_MAXAREA");
+		break;
+	default:
+		throw IPC::CClientFailure("Bad global attribute ID");
+	}
+}
+
+void HandleMessage_CreateWindow(CClient& client, CDeserialiser& message)
+{
+	uint16_t	new_id = message.ReadU16();
+	//uint16_t	parent_id = message.ReadU16();
+	//CWindow* parent = client.GetWindow( parent_id );
+	::std::string	name = message.ReadString();
+	
+	::_SysDebug("_CreateWindow: (%i, '%s')", new_id, name.c_str());
+	client.SetWindow( new_id, new CWindow(*gpCompositor, client, name, new_id) );
+}
+
+void HandleMessage_DestroyWindow(CClient& client, CDeserialiser& message)
+{
+	uint16_t	win_id = message.ReadU16();
+	_SysDebug("_DestroyWindow: (%i)", win_id);
+	
+	CWindow*	win = client.GetWindow(win_id);
+	if(!win) {
+		throw IPC::CClientFailure("_DestroyWindow: Bad window");
+	}
+	client.SetWindow(win_id, 0);	
+	
+	// TODO: Directly inform compositor?
+	delete win;
+}
+
+void HandleMessage_SetWindowAttr(CClient& client, CDeserialiser& message)
+{
+	uint16_t	win_id = message.ReadU16();
+	uint16_t	attr_id = message.ReadU16();
+	_SysDebug("_SetWindowAttr: (Win=%i, ID=%i)", win_id, attr_id);
+	
+	CWindow*	win = client.GetWindow(win_id);
+	if(!win) {
+		throw IPC::CClientFailure("_SetWindowAttr - Bad window");
+	}
+	
+	switch(attr_id)
+	{
+	case IPC_WINATTR_DIMENSIONS: {
+		uint16_t new_w = message.ReadU16();
+		uint16_t new_h = message.ReadU16();
+		win->Resize(new_w, new_h);
+		break; }
+	case IPC_WINATTR_POSITION: {
+		int16_t new_x = message.ReadS16();
+		int16_t new_y = message.ReadS16();
+		win->Move(new_x, new_y);
+		break; }
+	case IPC_WINATTR_SHOW:
+		win->Show( message.ReadU8() != 0 );
+		break;
+	case IPC_WINATTR_FLAGS:
+		win->SetFlags( message.ReadU8() );	// TODO: U8? why so small?
+		break;
+	case IPC_WINATTR_TITLE:
+		assert(!"TODO: IPC_WINATTR_TITLE");
+		break;
+	default:
+		_SysDebug("HandleMessage_SetWindowAttr - Bad attr %u", attr_id);
+		throw IPC::CClientFailure("Bad window attr");
+	}
+}
+
+void HandleMessage_GetWindowAttr(CClient& client, CDeserialiser& message)
+{
+	assert(!"TODO HandleMessage_GetWindowAttr");
+}
+
+void HandleMessage_SendIPC(CClient& client, CDeserialiser& message)
+{
+	assert(!"TODO HandleMessage_SendIPC");
+}
+
+void HandleMessage_GetWindowBuffer(CClient& client, CDeserialiser& message)
+{
+	uint16_t	win_id = message.ReadU16();
+	_SysDebug("_GetWindowBuffer: (%i)", win_id);
+	
+	CWindow*	win = client.GetWindow(win_id);
+	if(!win) {
+		throw IPC::CClientFailure("_PushData: Bad window");
+	}
+	
+	uint64_t handle = win->m_surface.GetSHMHandle();
+	
+	CSerialiser	reply;
+	reply.WriteU8(IPCMSG_REPLY);
+	reply.WriteU8(IPCMSG_GETWINBUF);
+	reply.WriteU16(win_id);
+	reply.WriteU64(handle);
+	client.SendMessage(reply);
+}
+
+void HandleMessage_DamageRect(CClient& client, CDeserialiser& message)
+{
+	uint16_t	winid = message.ReadU16();
+	uint16_t	x = message.ReadU16();
+	uint16_t	y = message.ReadU16();
+	uint16_t	w = message.ReadU16();
+	uint16_t	h = message.ReadU16();
+	
+	_SysDebug("_DamageRect: (%i %i,%i %ix%i)", winid, x, y, w, h);
+	
+	CWindow*	win = client.GetWindow(winid);
+	if(!win) {
+		throw IPC::CClientFailure("_PushData: Bad window");
+	}
+	
+	CRect	area(x,y,w,h);
+	
+	win->Repaint(area);
+}
+
+void HandleMessage_PushData(CClient& client, CDeserialiser& message)
+{
+	uint16_t	win_id = message.ReadU16();
+	uint16_t	x = message.ReadU16();
+	uint16_t	y = message.ReadU16();
+	uint16_t	w = message.ReadU16();
+	uint16_t	h = message.ReadU16();
+	_SysDebug("_PushData: (%i, (%i,%i) %ix%i)", win_id, x, y, w, h);
+	
+	CWindow*	win = client.GetWindow(win_id);
+	if(!win) {
+		throw IPC::CClientFailure("_PushData: Bad window");
+	}
+	
+	for( unsigned int row = 0; row < h; row ++ )
+	{
+		const ::std::vector<uint8_t> scanline_data = message.ReadBuffer();
+		if( scanline_data.size() != w * 4 ) {
+			_SysDebug("ERROR _PushData: Scanline buffer size mismatch (%i,%i)",
+				scanline_data.size(), w*4);
+			continue ;
+		}
+		win->DrawScanline(y+row, x, w, scanline_data.data());
+	}
+}
+void HandleMessage_Blit(CClient& client, CDeserialiser& message)
+{
+	assert(!"TODO HandleMessage_Blit");
+}
+void HandleMessage_DrawCtl(CClient& client, CDeserialiser& message)
+{
+	uint16_t	win_id = message.ReadU16();
+	uint16_t	x = message.ReadU16();
+	uint16_t	y = message.ReadU16();
+	uint16_t	w = message.ReadU16();
+	uint16_t	h = message.ReadU16();
+	uint16_t	ctrl_id = message.ReadU16();
+	uint16_t 	frame = message.ReadU16();
+	_SysDebug("_DrawCtl: (%i, (%i,%i) %ix%i Ctl%i frame?=0x%04x)", win_id, x, y, w, h, ctrl_id, frame);
+	
+	CWindow*	win = client.GetWindow(win_id);
+	if(!win) {
+		throw IPC::CClientFailure("_DrawCtl: Bad window");
+	}
+	
+	const CControl* ctrl = CControl::GetByID(ctrl_id);
+	if(!ctrl) {
+		throw IPC::CClientFailure("_DrawCtl: Invalid control ID");
+	}
+	
+	CRect	area(x,y,w,h);
+	ctrl->Render(win->m_surface, area);
+}
+void HandleMessage_DrawText(CClient& client, CDeserialiser& message)
+{
+	uint16_t	win_id = message.ReadU16();
+	uint16_t	x = message.ReadU16();
+	uint16_t	y = message.ReadU16();
+	uint16_t	w = message.ReadU16();
+	uint16_t	h = message.ReadU16();
+	uint16_t	font_id = message.ReadU16();
+	::std::string	str = message.ReadString();
+	_SysDebug("_DrawText: (%i (%i,%i) %ix%i Font%i \"%s\")", win_id, x, y, w, h, font_id, str.c_str());
+	
+	CWindow*	win = client.GetWindow(win_id);
+	if(!win) {
+		throw IPC::CClientFailure("_DrawText: Bad window");
+	}
+	
+	// 1. Get font from client structure
+	IFontFace& fontface = client.GetFont(font_id);
+	
+	// 2. Render
+	CRect	area(x, y, w, h);
+	fontface.Render(win->m_surface, area, str, h);
+}
+
+void HandleMessage_FillRect(CClient& client, CDeserialiser& message)
+{
+	uint16_t	win_id = message.ReadU16();
+	uint16_t	x = message.ReadU16();
+	uint16_t	y = message.ReadU16();
+	uint16_t	w = message.ReadU16();
+	uint16_t	h = message.ReadU16();
+	uint32_t	colour = message.ReadU32();
+	_SysDebug("_FillRect: (%i (%i,%i) %ix%i %06x)", win_id, x, y, w, h, colour);
+	
+	CWindow*	win = client.GetWindow(win_id);
+	if(!win) {
+		throw IPC::CClientFailure("_FillRect: Bad window");
+	}
+	
+	while(h -- ) {
+		win->FillScanline(y++, x, w, colour);
+	}
+}
+
+void HandleMessage_DrawRect(CClient& client, CDeserialiser& message)
+{
+	uint16_t	win_id = message.ReadU16();
+	uint16_t	x = message.ReadU16();
+	uint16_t	y = message.ReadU16();
+	uint16_t	w = message.ReadU16();
+	uint16_t	h = message.ReadU16();
+	uint32_t	colour = message.ReadU32();
+	_SysDebug("_DrawRect: (%i (%i,%i) %ix%i %06x)", win_id, x, y, w, h, colour);
+	
+	CWindow*	win = client.GetWindow(win_id);
+	if(!win) {
+		throw IPC::CClientFailure("_DrawRect: Bad window");
+	}
+	
+	if(h == 0) {
+	}
+	else if(h == 1) {
+		win->FillScanline(y, x, w, colour);
+	}
+	else if(h == 2) {
+		win->FillScanline(y++, x, w, colour);
+		win->FillScanline(y++, x, w, colour);
+	}
+	else {
+		win->FillScanline(y++, x, w, colour);
+		while( h -- > 2 ) {
+			win->FillScanline(y, x, 1, colour);
+			win->FillScanline(y, x+w-1, 1, colour);
+			y ++;
+		}
+		win->FillScanline(y++, x, w, colour);
+	}
+}
+
+typedef void	MessageHandler_op_t(CClient& client, CDeserialiser& message);
+
+MessageHandler_op_t	*message_handlers[] = {
+	[IPCMSG_NULL]       = &HandleMessage_Nop,
+	[IPCMSG_REPLY]      = &HandleMessage_Reply,
+	[IPCMSG_PING]       = &HandleMessage_Ping,
+	[IPCMSG_GETGLOBAL]  = &HandleMessage_GetGlobalAttr,
+	[IPCMSG_SETGLOBAL]  = &HandleMessage_SetGlobalAttr,
+	
+	[IPCMSG_CREATEWIN]  = &HandleMessage_CreateWindow,
+	[IPCMSG_CLOSEWIN]   = &HandleMessage_DestroyWindow,
+	[IPCMSG_SETWINATTR] = &HandleMessage_SetWindowAttr,
+	[IPCMSG_GETWINATTR] = &HandleMessage_GetWindowAttr,
+	[IPCMSG_SENDIPC]    = &HandleMessage_SendIPC,	// Use the GUI server for low-bandwith IPC
+	[IPCMSG_GETWINBUF]  = &HandleMessage_GetWindowBuffer,
+	[IPCMSG_DAMAGERECT] = &HandleMessage_DamageRect,
+	[IPCMSG_PUSHDATA]   = &HandleMessage_PushData,	// to a window's buffer
+	[IPCMSG_BLIT]       = &HandleMessage_Blit,	// Copy data from one part of the window to another
+	[IPCMSG_DRAWCTL]    = &HandleMessage_DrawCtl,	// Draw a control
+	[IPCMSG_DRAWTEXT]   = &HandleMessage_DrawText,	// Draw text
+	[IPCMSG_FILLRECT]   = &HandleMessage_FillRect,	// Fill a rectangle
+	[IPCMSG_DRAWRECT]   = &HandleMessage_DrawRect,	// Draw (outline) a rectangle
+};
+
+void HandleMessage(CClient& client, CDeserialiser& message)
+{
+	const unsigned int num_commands = sizeof(message_handlers)/sizeof(IPC::MessageHandler_op_t*);
+	unsigned int command = message.ReadU8();
+	if( command >= num_commands ) {
+		// Drop, invalid command
+		_SysDebug("HandleMessage: Command %u is invalid (out of range for %u)", command, num_commands);
+		return ;
+	}
+	
+	(message_handlers[command])(client, message);
+}
+
+CClientFailure::CClientFailure(std::string&& what):
+	m_what(what)
+{
+}
+const char *CClientFailure::what() const throw()
+{
+	return m_what.c_str();
+}
+CClientFailure::~CClientFailure() throw()
+{
+}
+
+};	// namespace IPC
+
+IIPCChannel::~IIPCChannel()
+{
+}
+
+};	// namespace AxWin
+
diff --git a/Usermode/Applications/axwin4_src/Server/main.cpp b/Usermode/Applications/axwin4_src/Server/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1b64da4e324ea86a0ca3daf7051d4d7e4af2611e
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/main.cpp
@@ -0,0 +1,152 @@
+/*
+ * Acess2 GUI v4 (AxWin4)
+ * - By John Hodge (thePowesGang)
+ * 
+ * main.cpp
+ * - Program main
+ */
+#include <CConfig.hpp>
+#include <ipc.hpp>
+#include <input.hpp>
+#include <video.hpp>
+#include <CCompositor.hpp>
+#include <timing.hpp>
+#include <exception>
+#include <algorithm>
+#include <common.hpp>
+
+#include <system_error>
+#include <cerrno>
+
+extern "C" {
+#include <stdio.h>
+#include <stdint.h>
+};
+
+using namespace AxWin;
+
+// === CODE ===
+int main(int argc, char *argv[])
+{
+	// - Load configuration (from file and argv)
+	CConfig	config;
+	try {
+		config.parseCommandline(argc, argv);
+	}
+	catch(const std::exception& e) {
+		fprintf(stderr, "Exception: %s\n", e.what());
+		_SysDebug("Config threw exception: %s", e.what());
+		return 1;
+	}
+	
+	CVideo*	vid = 0;
+	CCompositor*	compositor = 0;
+	CInput*	input = 0;
+	
+	try {
+		// - Open graphics
+		vid = new CVideo(config.m_video);
+		::_SysDebug("vid = %p", vid);
+		// - Initialise compositor structures
+		compositor = new CCompositor(/*config.m_compositor,*/ *vid);
+		::_SysDebug("compositor = %p", compositor);
+		// - Open input
+		input = new CInput(config.m_input, *compositor);
+		::_SysDebug("input = %p", input);
+		//  > Handles hotkeys?
+		// - Bind IPC channels
+		IPC::Initialise(config.m_ipc, *compositor);
+		::_SysDebug("IPC up");
+		// - Start root child process (from config)
+		// TODO: Spin up child process
+		
+		{
+			const char *interface_app = "/Acess/Apps/AxWin/4.0/AxWinUI";
+			const char *argv[] = {interface_app, NULL};
+			 int	rv = _SysSpawn(interface_app, argv, NULL, 0, NULL, NULL);
+			if( rv < 0 ) {
+				_SysDebug("_SysSpawn chucked a sad, rv=%i, errno=%i", rv, _errno);
+				throw ::std::system_error(errno, ::std::system_category());
+			}
+		}
+	}
+	catch(const ::std::exception& e) {
+		fprintf(stderr, "Startup threw exception: %s\n", e.what());
+		_SysDebug("Startup threw exception: %s", e.what());
+		delete input;
+		delete compositor;
+		delete vid;
+		return 1;
+	}
+	
+	// - Event loop
+	for( ;; )
+	{
+		 int	nfd = 0;
+		fd_set	rfds, efds;
+		
+		FD_ZERO(&rfds);
+		FD_ZERO(&efds);
+	
+		nfd = ::std::max(nfd, input->FillSelect(rfds));
+		nfd = ::std::max(nfd, IPC::FillSelect(rfds));
+		
+		for( int i = 0; i < nfd; i ++ )
+			if( FD_ISSET(i, &rfds) )
+				FD_SET(i, &efds);
+
+		#if 0
+		for( int i = 0; i < nfd; i ++ ) {
+			if( FD_ISSET(i, &rfds) ) {
+				_SysDebug("FD%i", i);
+			}
+		}
+		#endif
+		
+		// TODO: Support _SysSendMessage IPC?
+		int64_t	timeout = Timing::GetTimeToNextEvent();
+		int64_t	*timeoutp;
+		if( timeout >= 0 ) {
+			::_SysDebug("Calling select with timeout %lli", timeout);
+			timeoutp = &timeout;
+		}
+		else {
+			//::_SysDebug("Calling select with no timeout");
+			timeoutp = 0;
+		}
+		int rv = ::_SysSelect(nfd, &rfds, NULL, NULL/*&efds*/, timeoutp, 0);
+		
+		#if 0
+		for( int i = 0; i < nfd; i ++ ) {
+			if( FD_ISSET(i, &rfds) ) {
+				_SysDebug("FD%i", i);
+			}
+		}
+		#endif
+		//_SysDebug("rv=%i, timeout=%lli", rv, timeout);
+		
+		try {
+			Timing::CheckEvents();
+			
+			input->HandleSelect(rfds);
+			IPC::HandleSelect(rfds);
+			
+			compositor->Redraw();
+		}
+		catch(...) {
+			::_SysDebug("Exception during select handling");
+		}
+	}
+	return 0;
+}
+
+namespace AxWin {
+
+const char* InitFailure::what() const throw()
+{
+	return m_what;
+}
+
+
+}
+
diff --git a/Usermode/Applications/axwin4_src/Server/resources/cursor.h b/Usermode/Applications/axwin4_src/Server/resources/cursor.h
new file mode 100644
index 0000000000000000000000000000000000000000..be2d77b34480a75e981b1fe74d914dfdcbd2f2d0
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/resources/cursor.h
@@ -0,0 +1,34 @@
+/*
+ */
+#ifndef _RESORUCE_CURSOR_H
+#define _RESORUCE_CURSOR_H
+
+#include <stdint.h>
+
+static struct {
+	uint16_t	W, H;
+	uint32_t	Data[8*16];
+} __attribute__((packed)) cCursorBitmap = {
+	8, 16,
+	{
+		0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFF000000, 0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFF000000, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000, 0x00000000,
+		0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000,
+		0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000,
+		0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0xFF000000, 0xFF000000,
+		0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000,
+		0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000,
+		0xFF000000, 0xFF000000, 0x00000000, 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000,
+		0xFF000000, 0x00000000, 0x00000000, 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF000000, 0xFF000000, 0x00000000,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000
+	}
+};
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/Server/resources/font_8x16.h b/Usermode/Applications/axwin4_src/Server/resources/font_8x16.h
new file mode 100644
index 0000000000000000000000000000000000000000..b9bad5db2f427350fcf0ce46651d47e5e6770099
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/resources/font_8x16.h
@@ -0,0 +1,265 @@
+/*
+ * Taken from http://cvs.savannah.gnu.org/viewvc/vgabios/vgafonts.h?root=vgabios&view=markup
+ * Altered for Acess2
+ */
+#define FONT_WIDTH	8
+#define FONT_HEIGHT	16
+static uint8_t VTermFont[256*16]=
+{
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+	0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00,
+	0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+	0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00,
+	0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00,
+	0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00,
+	0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00,
+	0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+	0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+	0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
+	0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00,
+	0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+	0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00,
+	0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+	0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+	0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+	0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/Usermode/Applications/axwin4_src/Server/timing.cpp b/Usermode/Applications/axwin4_src/Server/timing.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e61c8e2c589fc330126245e1b0ffcebfbb70721b
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/timing.cpp
@@ -0,0 +1,25 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * timing.cpp
+ * - Timing code
+ */
+#include <timing.hpp>
+
+namespace AxWin {
+namespace Timing {
+
+int64_t GetTimeToNextEvent()
+{
+	return -1;
+}
+
+void CheckEvents()
+{
+	
+}
+
+};	// namespace Timing
+};	// namespace AxWin
+
diff --git a/Usermode/Applications/axwin4_src/Server/video.cpp b/Usermode/Applications/axwin4_src/Server/video.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..50686c61be17f5e1d7b22b31a5ea8206c1b2ddf8
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/Server/video.cpp
@@ -0,0 +1,120 @@
+/*
+ * Acess2 GUI v4
+ * - By John Hodge (thePowersGang)
+ *
+ * video.cpp
+ * - Graphics output
+ */
+#include <cstddef>
+#include <video.hpp>
+#include <common.hpp>
+
+extern "C" {
+#include <acess/sys.h>
+#include <acess/devices/pty.h>
+#include <acess/devices/pty_cmds.h>
+#include "resources/cursor.h"
+}
+
+namespace AxWin {
+
+CVideo::CVideo(const CConfigVideo& config):
+	m_fd(1),
+	m_width(0),
+	m_height(0),
+	m_bufferFormat(PTYBUFFMT_TEXT)
+{
+	// Obtain dimensions
+	{
+		if( _SysIOCtl(m_fd, DRV_IOCTL_TYPE, NULL) != DRV_TYPE_TERMINAL )
+			throw AxWin::InitFailure("stdin isn't a terminal");
+		struct ptydims	dims;
+		if( _SysIOCtl(m_fd, PTY_IOCTL_GETDIMS, &dims) == -1 )
+			throw AxWin::InitFailure("Failed to get dimensions from stdin");
+		m_width = dims.PW;
+		m_height = dims.PH;
+		if( m_width == 0 || m_height == 0 )
+			throw AxWin::InitFailure("Terminal not capable of graphics");
+	}
+	
+	_SysDebug("m_width=%i, m_height=%i", m_width, m_height);
+	SetCursorBitmap();
+	
+	SetCursorPos( m_width/2, m_height/2 );
+	
+	SetBufFormat(PTYBUFFMT_FB);
+	uint32_t	data[m_width];
+	for( unsigned int i = 0; i < m_height; i ++ )
+		BlitLine(data, i, 0, m_width);
+}
+
+void CVideo::GetDims(unsigned int& w, unsigned int& h)
+{
+	w = m_width;
+	h = m_height;
+}
+
+void CVideo::BlitLine(const uint32_t* src, unsigned int dst_y, unsigned int dst_x, unsigned int width)
+{
+	//_SysDebug("CVideo::BlitLine (%p, %i, %i, %i)", src, dst_y, dst_x, width);
+	//_SysDebugHex("CVideo::BlitLine", src, width*4);
+	size_t	cmdlen = (sizeof(struct ptycmd_senddata) + width*4)/4;
+	//_SysDebug(" - Offset = %i, cmdlen = %i", (dst_y * m_width + dst_x) * 4, cmdlen);
+	struct ptycmd_senddata	cmd = {
+		{PTY2D_CMD_SEND, uint8_t(cmdlen & 0xFF), uint16_t(cmdlen>>8)},
+		(dst_y * m_width + dst_x)
+	};
+	SetBufFormat(PTYBUFFMT_2DCMD);
+	_SysWrite(m_fd, &cmd, sizeof(cmd));
+	_SysWrite(m_fd, src, width*4);
+}
+
+void CVideo::Flush()
+{
+	// TODO: Write to a local copy of the framebuffer in BlitLine, and then flush out in this function
+}
+
+void CVideo::SetBufFormat(unsigned int FormatID)
+{
+	if( m_bufferFormat != FormatID )
+	{
+		
+		struct ptymode mode = {.OutputMode = FormatID, .InputMode = 0};
+		int rv = _SysIOCtl( m_fd, PTY_IOCTL_SETMODE, &mode );
+		if( rv ) {
+			throw ::AxWin::InitFailure("Can't set PTY to framebuffer mode");
+		}
+		
+		m_bufferFormat = FormatID;
+	}
+}
+
+void CVideo::SetCursorBitmap()
+{
+	// Set cursor position and bitmap
+	struct ptycmd_header	hdr = {PTY2D_CMD_SETCURSORBMP,0,0};
+	size_t size = sizeof(hdr) + sizeof(cCursorBitmap) + cCursorBitmap.W*cCursorBitmap.H*4;
+	hdr.len_low = size / 4;
+	hdr.len_hi = size / (256*4);
+	
+	SetBufFormat(PTYBUFFMT_2DCMD);
+	_SysWrite(m_fd, &hdr, sizeof(hdr));
+	_SysDebug("SetCursorBitmap - size = %i (%04x:%02x * 4)", size, hdr.len_hi, hdr.len_low);
+	_SysWrite(m_fd, &cCursorBitmap, size-sizeof(hdr));
+}
+
+void CVideo::SetCursorPos(int X, int Y)
+{
+	struct ptycmd_setcursorpos	cmd;
+	cmd.hdr.cmd = PTY2D_CMD_SETCURSORPOS;
+	cmd.hdr.len_low = sizeof(cmd)/4;
+	cmd.hdr.len_hi = 0;
+	cmd.x = X;
+	cmd.y = Y;
+
+	SetBufFormat(PTYBUFFMT_2DCMD);	
+	_SysWrite(m_fd, &cmd, sizeof(cmd));
+}
+
+};	// namespace AxWin
+
diff --git a/Usermode/Applications/axwin4_src/UI/Makefile b/Usermode/Applications/axwin4_src/UI/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..f225d5f735b25ef03e2441b5d0547a0df360ed63
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/UI/Makefile
@@ -0,0 +1,12 @@
+
+include ../../Makefile.cfg
+
+DIR := Apps/AxWin/4.0
+
+OBJ := main.o taskbar.o
+
+BIN := AxWinUI
+
+LIBS += -laxwin4
+
+include ../../Makefile.tpl
diff --git a/Usermode/Applications/axwin4_src/UI/include/common.h b/Usermode/Applications/axwin4_src/UI/include/common.h
new file mode 100644
index 0000000000000000000000000000000000000000..f51cca647d24ab83de7d3230251ef008f57b8024
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/UI/include/common.h
@@ -0,0 +1,10 @@
+/*
+ */
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+extern unsigned int	giScreenWidth;
+extern unsigned int	giScreenHeight;
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/UI/include/taskbar.h b/Usermode/Applications/axwin4_src/UI/include/taskbar.h
new file mode 100644
index 0000000000000000000000000000000000000000..728ceceefae57d56571a1e856d3309bbf9b324f7
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/UI/include/taskbar.h
@@ -0,0 +1,12 @@
+/*
+ */
+#ifndef _TASKBAR_H_
+#define _TASKBAR_H_
+
+#include <stdbool.h>
+
+extern void Taskbar_Create(void);
+extern void Taskbar_Redraw(void);
+
+#endif
+
diff --git a/Usermode/Applications/axwin4_src/UI/main.c b/Usermode/Applications/axwin4_src/UI/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..624e381a716726b6d0bd1747b1fcd8162970914f
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/UI/main.c
@@ -0,0 +1,69 @@
+/*
+ * AxWin4 GUI - UI Core
+ * - By John Hodge (thePowersGang)
+ *
+ * main.c
+ * - Program core
+ */
+#include <axwin4/axwin.h>
+#include <assert.h>
+#include "include/common.h"
+#include "include/taskbar.h"
+
+// === PROTOTYPES ===
+tAxWin4_Window *CreateBGWin(int w, int h);
+
+// === GLOABLS ===
+unsigned int	giScreenWidth = 640;
+unsigned int	giScreenHeight = 480;
+
+// === CODE ===
+int main(int argc, const char *argv[])
+{
+	assert( AxWin4_Connect("ipcpipe:///Devices/ipcpipe/axwin4") );
+	
+	AxWin4_GetScreenDimensions(0, &giScreenWidth, &giScreenHeight);
+	
+	tAxWin4_Window *bgwin = CreateBGWin(giScreenWidth, giScreenHeight);
+	Taskbar_Create();
+	
+	_SysDebug("Beginning queue");
+	while( AxWin4_WaitEventQueue(0) )
+		;
+	_SysDebug("Clean exit");
+	
+	return 0;
+}
+
+tAxWin4_Window *CreateBGWin(int w, int h)
+{
+	tAxWin4_Window	*bgwin = AxWin4_CreateWindow("background");
+	AxWin4_MoveWindow(bgwin, 0,0);
+	AxWin4_ResizeWindow(bgwin, w,h);
+	AxWin4_SetWindowFlags(bgwin, AXWIN4_WNDFLAG_NODECORATE|AXWIN4_WNDFLAG_KEEPBELOW);
+	
+	// Load background image
+	uint32_t *buf = AxWin4_GetWindowBuffer(bgwin);
+	if( buf )
+	{
+		for( size_t y = 0; y < h; y ++ )
+		{
+			for(size_t x = 0; x < w; x ++ )
+			{
+				uint8_t	r = y * 256 / h;
+				uint8_t	g = 0;
+				uint8_t	b = x * 256 / w;
+				buf[y*w+x] = (r << 16) | (g << 8) | b;
+			}
+		}
+	}
+	else
+	{
+		AxWin4_FillRect(bgwin, 0, 0, w, h, 0x8888CC);
+	}
+	//AxWin4_DamageRect(bgwin, 0, 0, w, h);
+	AxWin4_ShowWindow(bgwin, true);
+	
+	return bgwin;
+}
+
diff --git a/Usermode/Applications/axwin4_src/UI/taskbar.c b/Usermode/Applications/axwin4_src/UI/taskbar.c
new file mode 100644
index 0000000000000000000000000000000000000000..b62a0420555fa8e62436820d5ef64515c0e8929b
--- /dev/null
+++ b/Usermode/Applications/axwin4_src/UI/taskbar.c
@@ -0,0 +1,92 @@
+/*
+ * AxWin4 GUI - UI Core
+ * - By John Hodge (thePowersGang)
+ *
+ * taskbar.c
+ * - Main toolbar (aka Taskbar) 
+ */
+#include <axwin4/axwin.h>
+#include "include/common.h"
+#include "include/taskbar.h"
+#include <time.h>
+
+// === CONSTANTS ===
+#define TASKBAR_HEIGHT	30
+#define TASKBAR_BORDER	3	// Border between window edge and controls
+#define TASKBAR_SYSBTN_SIZE	(TASKBAR_HEIGHT-TASKBAR_BORDER*2)
+#define TASKBAR_WIN_MAXSIZE	100
+#define TASKBAR_WIN_MINSIZE	24
+#define TASKBAR_CLOCKSIZE	60
+
+// === TYPES ===
+typedef struct sTaskbar_Win	tTaskbar_Win;
+struct sTaskbar_Win
+{
+	tTaskbar_Win	*Next;
+	const char	*Title;
+};
+
+// === PROTOTYPES ===
+
+// === GLOBALS ===
+tAxWin4_Window	*gpTaskbar_Window;
+unsigned int giTaskbar_NumWins = 0;
+tTaskbar_Win	*gpTaskbar_FirstWin;
+
+// === CODE ===
+void Taskbar_Create(void)
+{
+	gpTaskbar_Window = AxWin4_CreateWindow("taskbar");
+	tAxWin4_Window * const win = gpTaskbar_Window;
+
+	AxWin4_MoveWindow(win, 0, 0);
+	AxWin4_ResizeWindow(win, giScreenWidth, TASKBAR_HEIGHT);
+	
+	AxWin4_SetWindowFlags(win, AXWIN4_WNDFLAG_NODECORATE);
+	Taskbar_Redraw();
+	AxWin4_ShowWindow(win, true);
+}
+
+void Taskbar_Redraw(void)
+{
+	const int	w = giScreenWidth;
+	const int	h = TASKBAR_HEIGHT;
+	
+	const int	active_height = h - TASKBAR_BORDER*2;
+	const int	winlist_start_x = TASKBAR_BORDER+TASKBAR_SYSBTN_SIZE+TASKBAR_BORDER;
+	const int	clock_start_x = w - (TASKBAR_BORDER+TASKBAR_CLOCKSIZE);
+	
+	// Window background: Toolbar skin
+	AxWin4_DrawControl(gpTaskbar_Window, 0, 0, w, h, AXWIN4_CTL_TOOLBAR, 0);
+	
+	// System button
+	// TODO: Use an image instead
+	AxWin4_DrawControl(gpTaskbar_Window, TASKBAR_BORDER, TASKBAR_BORDER, TASKBAR_SYSBTN_SIZE, active_height, AXWIN4_CTL_BUTTON, 0);
+	
+	// Windows
+	// TODO: Maintain/request a list of windows
+	if( giTaskbar_NumWins )
+	{
+		int winbutton_size = (clock_start_x - winlist_start_x) / giTaskbar_NumWins;
+		if(winbutton_size > TASKBAR_WIN_MAXSIZE)	winbutton_size = TASKBAR_WIN_MAXSIZE;
+		int x = winlist_start_x;
+		for(tTaskbar_Win *win = gpTaskbar_FirstWin; win; win = win->Next )
+		{
+			AxWin4_DrawControl(gpTaskbar_Window, x, TASKBAR_BORDER, winbutton_size, active_height, AXWIN4_CTL_BUTTON, 0);
+		}
+	}
+	
+	// Clock
+	{
+		char timestr[5];
+		time_t	rawtime;
+		time(&rawtime);
+		strftime(timestr, 5, "%H%M", localtime(&rawtime));
+		//AxWin4_DrawControl(gpTaskbar_Window, clock_start_x, TASKBAR_BORDER, TASKBAR_CLOCKSIZE, active_height, AXWIN4_CTL_BOX);
+		
+		AxWin4_DrawText(gpTaskbar_Window, clock_start_x, TASKBAR_BORDER, TASKBAR_CLOCKSIZE, active_height, 0, timestr);
+	}
+	
+	AxWin4_DamageRect(gpTaskbar_Window, 0, 0, w, h);
+}
+
diff --git a/Usermode/Applications/gui_ate_src/Makefile b/Usermode/Applications/gui_ate_src/Makefile
index fe1b2148ccd6bb46abd4a48c35cd659847727270..25e256ef0682ea71e08e314fac179df699b1e674 100644
--- a/Usermode/Applications/gui_ate_src/Makefile
+++ b/Usermode/Applications/gui_ate_src/Makefile
@@ -2,7 +2,7 @@
 
 -include ../Makefile.cfg
 
-LDFLAGS += -laxwin3
+LIBS += -laxwin3
 
 OBJ = main.o strings.o toolbar.o
 BIN = ate
diff --git a/Usermode/Applications/gui_terminal_src/Makefile b/Usermode/Applications/gui_terminal_src/Makefile
index a38f1355efcad0c9dd3d670779731a954d055a97..be6450093f972f93203c7716a574591987c47cd0 100644
--- a/Usermode/Applications/gui_terminal_src/Makefile
+++ b/Usermode/Applications/gui_terminal_src/Makefile
@@ -2,7 +2,7 @@
 
 -include ../Makefile.cfg
 
-LDFLAGS += -laxwin3 -lunicode
+LIBS += -laxwin3 -lunicode
 
 OBJ = main.o vt100.o display.o
 BIN = terminal
diff --git a/Usermode/Applications/gui_terminal_src/vt100.c b/Usermode/Applications/gui_terminal_src/vt100.c
index 0f00bd994e4b9802a42d80b697c26e3a04d4a4d8..6a2321ea7e91f88168ef81e50e333434229f7297 100644
--- a/Usermode/Applications/gui_terminal_src/vt100.c
+++ b/Usermode/Applications/gui_terminal_src/vt100.c
@@ -17,6 +17,8 @@
 # include <assert.h>
 # include <stdlib.h>	// malloc/free
 
+# define ASSERTC(a, r, b)	assert(a r b)
+
 static inline int MIN(int a, int b)
 {
 	return a < b ? a : b;
@@ -170,7 +172,7 @@ int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf)
 			if( ret <= old_inc_len ) {
 				_SysDebug("Term_HandleVT100: ret(%i) <= old_inc_len(%i), inc_len=%i, '%*C'",
 					ret, old_inc_len, st->cache_len, st->cache_len, st->cache);
-				assert(ret > old_inc_len);
+				ASSERTC(ret, >, old_inc_len);
 			}
 			st->cache_len = 0;
 			//_SysDebug("%i bytes of escape code '%.*s' (return %i)",
@@ -186,6 +188,10 @@ int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf)
 
 	switch( *Buf )
 	{
+	case '\a':
+		// Alarm, aka bell
+		//Display_SoundBell(Term);
+		break;
 	case '\b':
 		// backspace is aprarently just supposed to cursor left (if possible)
 		Display_MoveCursor(Term, 0, -1);
@@ -442,9 +448,13 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
 			Display_MoveCursor(Term, 0, -(args[0] != 0 ? args[0] : 1));
 			break;
 		case 'H':
-			if( argc != 2 ) {
+			if( argc == 0 ) {
+				Display_SetCursor(Term, 0, 0);
 			}
-			else {
+			else if( argc == 1 ) {
+				Display_SetCursor(Term, args[0]-1, 0);
+			}
+			else if( argc == 2 ) {
 				// Adjust 1-based cursor position to 0-based
 				Display_SetCursor(Term, args[0]-1, args[1]-1);
 			}
@@ -483,12 +493,12 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
 				break;
 			}
 			break;
-		case 'S':	// Scroll text up n=1
-			Display_ScrollDown(Term, (argc >= 1 ? args[0] : 1));
-			break;
-		case 'T':	// Scroll text down n=1
+		case 'S':	// Scroll text up n=1 (expose bottom)
 			Display_ScrollDown(Term, -(argc >= 1 ? args[0] : 1));
 			break;
+		case 'T':	// Scroll text down n=1 (expose top)
+			Display_ScrollDown(Term, (argc >= 1 ? args[0] : 1));
+			break;
 		case 'c':	// Send Device Attributes
 			switch(args[0])
 			{
@@ -529,6 +539,40 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
 				// Reset
 				Display_ResetAttributes(Term);
 			}
+			else if( args[0] == 48 )
+			{
+				// ISO-8613-3 Background
+				if( args[1] == 2 ) {
+					uint32_t	col = 0;
+					col |= (uint32_t)args[2] << 16;
+					col |= (uint32_t)args[3] << 8;
+					col |= (uint32_t)args[4] << 0;
+					Display_SetBackground(Term, col);
+				}
+				else if( args[1] == 5 ) {
+					_SysDebug("TODO: Support xterm palette BG %i", args[2]);
+				}
+				else {
+					_SysDebug("VT100 Unknown mode set \e[48;%im", args[1]);
+				}
+			}
+			else if( args[0] == 38 )
+			{
+				// ISO-8613-3 Foreground
+				if( args[1] == 2 ) {
+					uint32_t	col = 0;
+					col |= (uint32_t)args[2] << 16;
+					col |= (uint32_t)args[3] << 8;
+					col |= (uint32_t)args[4] << 0;
+					Display_SetForeground(Term, col);
+				}
+				else if( args[1] == 5 ) {
+					_SysDebug("TODO: Support xterm palette FG %i", args[2]);
+				}
+				else {
+					_SysDebug("VT100 Unknown mode set \e[38;%im", args[1]);
+				}
+			}
 			else
 			{
 				for( int i = 0; i < argc; i ++ )
@@ -549,6 +593,9 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
 					case 4:
 						_SysDebug("TODO: \\e[4m - Underscore");
 						break;
+					//case 5:
+					//	_SysDebug("TODO: \\e[5m - Blink/bold");
+					//	break;
 					case 7:
 						_SysDebug("TODO: \\e[7m - Reverse");
 						break;
@@ -570,6 +617,14 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
 						st->CurBG = 0;
 						Display_SetBackground( Term, caVT100Colours[ st->CurBG ] );
 						break;
+					case 90 ... 97:
+						st->CurFG = args[i]-90 + 8;
+						Display_SetForeground( Term, caVT100Colours[ st->CurBG ] );
+						break;;
+					case 100 ... 107:
+						st->CurBG = args[i]-100 + 8;
+						Display_SetBackground( Term, caVT100Colours[ st->CurBG ] );
+						break;;
 					default:
 						_SysDebug("Unknown mode set \\e[%im", args[i]);
 						break;
@@ -582,7 +637,7 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
 			break;
 		// Set scrolling region
 		case 'r':
-			Display_SetScrollArea(Term, args[0], (args[1] - args[0]));
+			Display_SetScrollArea(Term, args[0]-1, (args[1] - args[0])+1);
 			break;
 		
 		case 's':
diff --git a/Usermode/Applications/init_src/main.c b/Usermode/Applications/init_src/main.c
index 11833a07f85bb47e4a23bfb191210ccb36acc3cf..73411b9345be1ca5362278ae834212ad07d87831 100644
--- a/Usermode/Applications/init_src/main.c
+++ b/Usermode/Applications/init_src/main.c
@@ -7,6 +7,7 @@
 #include <string.h>
 #include "common.h"
 #include <ctype.h>
+#include <inttypes.h>
 
 // === CONSTANTS ===
 #define DEFAULT_SHELL	"/Acess/SBin/login"
@@ -452,6 +453,8 @@ int SpawnKTerm(tInitProgram *Program)
 	 int	in = _SysOpen(path, OPENFLAG_READ);
 	 int	out = _SysOpen(path, OPENFLAG_WRITE);
 	
+	_SysDebug("Spawning virtual terminal '%s' with term '%s'",
+		path, Program->Command[0]);
 	return SpawnCommand(in, out, out, Program->Command, env);
 }
 
@@ -475,6 +478,8 @@ int SpawnSTerm(tInitProgram *Program)
 	_SysIOCtl(in, SERIAL_IOCTL_GETSETFORMAT, &Program->TypeInfo.STerm.FormatBits);
 	#endif
 
+	_SysDebug("Spawning serial terminal '%s' with term '%s'",
+		Program->TypeInfo.STerm.Path, Program->Command[0]);
 	return SpawnCommand(in, out, out, Program->Command, NULL);
 }
 
@@ -495,17 +500,15 @@ int SpawnDaemon(tInitProgram *Program)
 	// Log spawn header
 	{
 		char	buffer[101];
-		size_t len = snprintf(buffer, 100, "[%lli] init spawning ", _SysTimestamp());
+		size_t len = snprintf(buffer, 100, "[%"PRIi64"] init spawning ", _SysTimestamp());
 		_SysWrite(out, buffer, len);
-		char ch = '\'';
 		for( int i = 0; Program->Command[i]; i ++ )
 		{
-			_SysWrite(out, &ch, 1);
+			_SysWrite(out, "'", 1);
 			_SysWrite(out, Program->Command[i], strlen(Program->Command[i]));
-			_SysWrite(out, &ch, 1);
+			_SysWrite(out, "'", 1);
 		}
-		ch = '\n';
-		_SysWrite(out, &ch, 1);
+		_SysWrite(out, "\n", 1);
 	}
 	
 	return SpawnCommand(in, out, err, Program->Command, NULL);
diff --git a/Usermode/Applications/ip_src/Makefile b/Usermode/Applications/ip_src/Makefile
index 1c976b500c44e8c5e2701176ccd7037eee41c834..e3ff4e9bd9c7d55f3bceefcfdf7e41f560ddb219 100644
--- a/Usermode/Applications/ip_src/Makefile
+++ b/Usermode/Applications/ip_src/Makefile
@@ -2,7 +2,8 @@
 
 -include ../Makefile.cfg
 
-LDFLAGS += -lnet
+LDFLAGS +=
+LIBS    += -lnet
 
 OBJ = main.o addr.o routes.o
 BIN = ip
diff --git a/Usermode/Applications/irc_src/Makefile b/Usermode/Applications/irc_src/Makefile
index 9fab060040e8f0ac3851af7e6ae3fc576be284d5..78d803e308ecf05fb401c4ecfca582fefd9bdcf4 100644
--- a/Usermode/Applications/irc_src/Makefile
+++ b/Usermode/Applications/irc_src/Makefile
@@ -2,9 +2,10 @@
 
 -include ../Makefile.cfg
 
-LDFLAGS += -lnet -lreadline
+LIBS += -lnet -lreadline
 
-OBJ = main.o
+OBJ = main.o server.o input.o
+OBJ += window.o pseudo_curses.o
 BIN = irc
 
 -include ../Makefile.tpl
diff --git a/Usermode/Applications/irc_src/common.h b/Usermode/Applications/irc_src/common.h
new file mode 100644
index 0000000000000000000000000000000000000000..e374328bb0372c66ad026e235f5c263a4eb4db11
--- /dev/null
+++ b/Usermode/Applications/irc_src/common.h
@@ -0,0 +1,20 @@
+/*
+ */
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include "pseudo_curses.h"
+
+typedef struct sServer	tServer;
+
+extern void	_SysDebug(const char *format, ...);
+
+extern int	writef(int FD, const char *Format, ...);
+extern int	OpenTCP(const char *AddressString, short PortNumber);
+extern char	*GetValue(char *Src, int *Ofs);
+
+extern void	Redraw_Screen(void);
+extern void	Exit(const char *Reason) __attribute__((noreturn));
+
+#endif
+
diff --git a/Usermode/Applications/irc_src/input.c b/Usermode/Applications/irc_src/input.c
new file mode 100644
index 0000000000000000000000000000000000000000..b99935c05e6560b841884c68a3527c194e0362eb
--- /dev/null
+++ b/Usermode/Applications/irc_src/input.c
@@ -0,0 +1,171 @@
+/*
+ */
+#include "input.h"
+#include "window.h"
+#include "server.h"
+#include <readline.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+ 
+// === PROTOTYPES ===
+void	Input_FillSelect(int *nfds, fd_set *rfds);
+void	Input_HandleSelect(int nfds, const fd_set *rfds);
+ int	ParseUserCommand(char *String);
+
+// === GLOBALS ===
+tReadline	*gpInput_ReadlineInfo;
+
+// === CODE ===
+void Input_FillSelect(int *nfds, fd_set *rfds)
+{
+	if( !gpInput_ReadlineInfo ) {
+		gpInput_ReadlineInfo = Readline_Init(1);
+	}
+	
+	FD_SET(0, rfds);
+	if(*nfds < 0+1)
+		*nfds = 0+1;
+}
+
+void Input_HandleSelect(int nfds, const fd_set *rfds)
+{
+	// User input
+	if(FD_ISSET(0, rfds))
+	{
+		char	*cmd = Readline_NonBlock(gpInput_ReadlineInfo);
+		if( cmd )
+		{
+			if( cmd[0] )
+			{
+				ParseUserCommand(cmd);
+			}
+			free(cmd);
+			// Prompt
+			SetCursorPos(giTerminal_Height, 1);
+			printf("\x1B[2K");	// Clear line
+			int prompt_len = printf("[%s] ", Window_GetName(NULL));
+			SetCursorPos(giTerminal_Height, prompt_len+1);
+			fflush(stdout);
+		}
+	}
+}
+
+void Cmd_join(char *ArgString)
+{
+	 int	pos=0;
+	char	*channel_name = GetValue(ArgString, &pos);
+	
+	tServer	*srv = Window_GetServer(NULL);
+	
+	if( srv )
+	{
+		Windows_SwitchTo( Window_Create(srv, channel_name) );
+		Redraw_Screen();
+		Server_SendCommand(srv, "JOIN :%s", channel_name);
+	}
+}
+
+void Cmd_quit(char *ArgString)
+{
+	const char *quit_message = ArgString;
+	if( quit_message == NULL || quit_message[0] == '\0' )
+		quit_message = "/quit - Acess2 IRC Client";
+	
+	Servers_CloseAll(quit_message);
+	
+	Exit(NULL);	// NULL = user requested
+}
+
+void Cmd_window(char *ArgString)
+{
+	 int	pos = 0;
+	char	*window_id = GetValue(ArgString, &pos);
+	 int	window_num = atoi(window_id);
+	
+	if( window_num > 0 )
+	{
+		// Get `window_num`th window
+		tWindow	*win = Windows_GetByIndex(window_num-1);
+		if( win )
+		{
+			Windows_SwitchTo( win );
+		}
+		else
+		{
+			// Otherwise, silently ignore
+		}
+	}
+	else
+	{
+		window_num = 1;
+		for( tWindow *win; (win = Windows_GetByIndex(window_num-1)); window_num ++ )
+		{
+			Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, NULL, "%i: %s", window_num, Window_GetName(win));
+		}
+	}
+}
+
+void Cmd_me(char *ArgString)
+{
+	tServer	*srv = Window_GetServer(NULL);
+	if( srv && Window_IsChat(NULL) ) {
+		Window_AppendMessage(NULL, MSG_CLASS_ACTION, Server_GetNick(srv), "%s", ArgString);
+		Server_SendCommand(srv, "PRIVMSG %s :\1ACTION %s\1\n", Window_GetName(NULL), ArgString);
+	}
+}
+
+const struct {
+	const char *Name;
+	void	(*Fcn)(char *ArgString);
+} caCommands[] = {
+	{"join", Cmd_join},
+	{"quit", Cmd_quit},
+	{"window", Cmd_window},
+	{"win",    Cmd_window},
+	{"w",      Cmd_window},
+};
+const int ciNumCommands = sizeof(caCommands)/sizeof(caCommands[0]);
+
+/**
+ * \brief Handle a line from the prompt
+ */
+int ParseUserCommand(char *String)
+{
+	if( String[0] == '/' )
+	{
+		char	*command;
+		 int	pos = 0;
+		
+		command = GetValue(String, &pos)+1;
+
+		// TODO: Prefix matches
+		 int	cmdIdx = -1;
+		for( int i = 0; i < ciNumCommands; i ++ )
+		{
+			if( strcmp(command, caCommands[i].Name) == 0 ) {
+				cmdIdx = i;
+				break;
+			}
+		}
+		if( cmdIdx != -1 ) {
+			caCommands[cmdIdx].Fcn(String+pos);
+		}
+		else
+		{
+			Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, NULL, "Unknown command %s", command);
+		}
+	}
+	else
+	{
+		// Message
+		// - Only send if server is valid and window name is non-empty
+		tServer	*srv = Window_GetServer(NULL);
+		if( srv && Window_IsChat(NULL) ) {
+			Window_AppendMessage(NULL, MSG_CLASS_MESSAGE, Server_GetNick(srv), "%s", String);
+			Server_SendCommand(srv, "PRIVMSG %s :%s\n", Window_GetName(NULL), String);
+		}
+	}
+	
+	return 0;
+}
diff --git a/Usermode/Applications/irc_src/input.h b/Usermode/Applications/irc_src/input.h
new file mode 100644
index 0000000000000000000000000000000000000000..dee859ee71573736ebd869a07e8f4305e17183df
--- /dev/null
+++ b/Usermode/Applications/irc_src/input.h
@@ -0,0 +1,12 @@
+/*
+ */
+#ifndef _INPUT_H_
+#define _INPUT_H_
+
+#include <acess/sys.h>
+
+extern void	Input_FillSelect(int *nfds, fd_set *rfds);
+extern void	Input_HandleSelect(int nfds, const fd_set *rfds);
+
+#endif
+
diff --git a/Usermode/Applications/irc_src/main.c b/Usermode/Applications/irc_src/main.c
index 1ca53c2b5b2b39be8291771c76cc3d17f6d2b569..6169519c6e5ce92df6b833897032b0cc9a3a5c17 100755
--- a/Usermode/Applications/irc_src/main.c
+++ b/Usermode/Applications/irc_src/main.c
@@ -6,94 +6,47 @@
 #include <stdio.h>
 #include <string.h>
 #include <net.h>
-#include <readline.h>
-#include <acess/devices/pty.h>
 #include <stdarg.h>
+#include <acess/devices/pty.h>
 
-// === TYPES ===
-typedef struct sServer {
-	struct sServer	*Next;
-	 int	FD;
-	char	InBuf[BUFSIZ+1];
-	 int	ReadPos;
-	char	Name[];
-} tServer;
-
-typedef struct sMessage
-{
-	struct sMessage	*Next;
-	time_t	Timestamp;
-	tServer	*Server;
-	 int	Type;
-	char	*Source;	// Pointer into `Data`
-	char	Data[];
-}	tMessage;
-
-typedef struct sWindow
-{
-	struct sWindow	*Next;
-	tMessage	*Messages;
-	tServer	*Server;	//!< Canonical server (can be NULL)
-	 int	ActivityLevel;
-	char	Name[];	// Channel name / remote user
-}	tWindow;
+#include "common.h"
+#include "input.h"
+#include "window.h"
+#include "server.h"
 
-enum eMessageTypes
-{
-	MSG_TYPE_NULL,
-	MSG_TYPE_SERVER,	// Server message
-	
-	MSG_TYPE_NOTICE,	// NOTICE command
-	MSG_TYPE_JOIN,	// JOIN command
-	MSG_TYPE_PART,	// PART command
-	MSG_TYPE_QUIT,	// QUIT command
-	
-	MSG_TYPE_STANDARD,	// Standard line
-	MSG_TYPE_ACTION,	// /me
-	
-	MSG_TYPE_UNK
-};
+// === TYPES ===
 
 // === PROTOTYPES ===
  int	main(int argc, const char *argv[], const char *envp[]);
  int	MainLoop(void);
  int	ParseArguments(int argc, const char *argv[]);
- int	ParseUserCommand(char *String);
 // --- 
-tServer	*Server_Connect(const char *Name, const char *AddressString, short PortNumber);
-tMessage	*Message_AppendF(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message, ...) __attribute__((format(__printf__,5,6)));
-tMessage	*Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message);
-tWindow	*Window_Create(tServer *Server, const char *Name);
 void	Redraw_Screen(void);
-
- int	ProcessIncoming(tServer *Server);
 // --- Helpers
-void	SetCursorPos(int Row, int Col);
+void	Exit(const char *Reason);
  int	writef(int FD, const char *Format, ...);
  int	OpenTCP(const char *AddressString, short PortNumber);
 char	*GetValue(char *Str, int *Ofs);
-static inline int	isdigit(int ch);
 
 // === GLOBALS ===
-char	*gsUsername = "user";
-char	*gsHostname = "acess";
-char	*gsRealName = "Acess2 IRC Client";
-char	*gsNickname = "acess";
-tServer	*gpServers;
-tWindow	gWindow_Status = {
-	NULL, NULL, NULL,	// No next, empty list, no server
-	0, {""}	// No activity, empty name (rendered as status)
-};
-tWindow	*gpWindows = &gWindow_Status;
-tWindow	*gpCurrentWindow = &gWindow_Status;
- int	giTerminal_Width = 80;
- int	giTerminal_Height = 25;
+const char	*gsExitReason = "No reason [BUG]";
 
 // ==== CODE ====
 void ExitHandler(void)
 {
 	printf("\x1B[?1047l");
-	printf("Quit\n");
+	printf("Quit: %s\n", gsExitReason);
+
+	// stty +echo,canon
+	struct ptymode	mode = {.InputMode = 0, .OutputMode = 0};
+	mode.InputMode = PTYIMODE_CANON|PTYIMODE_ECHO;
+	_SysIOCtl(0, PTY_IOCTL_SETMODE, &mode);
+}
+
+void Exit(const char *Reason)
+{
+	gsExitReason = (Reason ? Reason : "User Requested");
+	exit( (Reason ? 1 : 0) );
 }
 
 int main(int argc, const char *argv[], const char *envp[])
@@ -105,117 +58,61 @@ int main(int argc, const char *argv[], const char *envp[])
 	
 	atexit(ExitHandler);
 	
-	if( _SysIOCtl(1, DRV_IOCTL_TYPE, NULL) != DRV_TYPE_TERMINAL ) {
-		fprintf(stderr, "note: assuming 80x25, can't get terminal dimensions\n");
-		giTerminal_Width = 80;
-		giTerminal_Height = 25;
-	}
-	else {
-		struct ptydims	dims;
-		_SysIOCtl(1, PTY_IOCTL_GETDIMS, &dims);
-		giTerminal_Width = dims.W;
-		giTerminal_Height = dims.H;
-	}
+	ACurses_Init();
 	
 	printf("\x1B[?1047h");
-	printf("\x1B[%i;%ir", 0, giTerminal_Height-1);
-	
-	SetCursorPos(giTerminal_Height-1, 1);
-	printf("[(status)] ");
+	printf("\x1B[%i;%ir", 2, giTerminal_Height-2);
 	
 	// HACK: Static server entry
 	// UCC (University [of Western Australia] Computer Club) IRC Server
-	gWindow_Status.Server = Server_Connect( "UCC", "130.95.13.18", 6667 );
+//	tServer	*starting_server = Server_Connect( "UCC", "130.95.13.18", 6667 );
 	// Freenode (#osdev)
-//	gWindow_Status.Server = Server_Connect( "Freenode", "89.16.176.16", 6667 );
+	tServer *starting_server = Server_Connect( "Freenode", "84.240.3.129", 6667 );
 	// Local servers
 //	gWindow_Status.Server = Server_Connect( "VMHost", "10.0.2.2", 6667 );
 //	gWindow_Status.Server = Server_Connect( "BitlBee", "192.168.1.39", 6667 );
 	
-	if( !gWindow_Status.Server )
-		return -1;
+	Windows_SetStatusServer(starting_server);
+	Windows_RepaintCurrent();
+	SetCursorPos(giTerminal_Height-1, 1);
+	printf("[(status)] ");
 	
 	MainLoop();
-	
-	for( tServer *srv = gpServers; srv; srv = srv->Next )
-		_SysClose(srv->FD);
+
+	Servers_CloseAll("Client closing");
 	
 	return 0;
 }
 
 int MainLoop(void)
 {
-	SetCursorPos(giTerminal_Height-1, 1);
+	SetCursorPos(giTerminal_Height, 1);
 	printf("[(status)] ");
 	fflush(stdout);
 	
-	tReadline *readline_info = Readline_Init(1);
+	// stty -echo,canon
+	struct ptymode	mode = {.InputMode = 0, .OutputMode = 0};
+	_SysIOCtl(0, PTY_IOCTL_SETMODE, &mode);
 	
 	for( ;; )
 	{
 		fd_set	readfds, errorfds;
-		 int	maxFD = 0;
+		 int	nfds = 1;
 		
 		FD_ZERO(&readfds);
 		FD_ZERO(&errorfds);
-		FD_SET(0, &readfds);	// stdin
-		
-		fflush(stdout);
 		
-		// Fill server FDs in fd_set
-		for( tServer *srv = gpServers; srv; srv = srv->Next )
-		{
-			FD_SET(srv->FD, &readfds);
-			FD_SET(srv->FD, &errorfds);
-			if( srv->FD > maxFD )
-				maxFD = srv->FD;
-		}
+		Input_FillSelect(&nfds, &readfds);
+		Servers_FillSelect(&nfds, &readfds, &errorfds);
 		
-		int rv = _SysSelect(maxFD+1, &readfds, 0, &errorfds, NULL, 0);
-		if( rv == -1 )	break;
+		int rv = _SysSelect(nfds, &readfds, 0, &errorfds, NULL, 0);
+		if( rv < 0 )	break;
 		
-		if(FD_ISSET(0, &readfds))
-		{
-			// User input
-			char	*cmd = Readline_NonBlock(readline_info);
-			if( cmd )
-			{
-				if( cmd[0] )
-				{
-					ParseUserCommand(cmd);
-				}
-				free(cmd);
-				// Prompt
-				SetCursorPos(giTerminal_Height-1, 1);
-				printf("\x1B[2K");	// Clear line
-				if( gpCurrentWindow->Name[0] )
-					printf("[%s:%s] ",
-						gpCurrentWindow->Server->Name, gpCurrentWindow->Name);
-				else
-					printf("[(status)] ");
-			}
-		}
+		// user input
+		Input_HandleSelect(nfds, &readfds);
 		
 		// Server response
-		for( tServer *srv = gpServers; srv; srv = srv->Next )
-		{
-			if(FD_ISSET(srv->FD, &readfds))
-			{
-				if( ProcessIncoming(srv) != 0 ) {
-					// Oops, error
-					_SysDebug("ProcessIncoming failed on FD%i (Server %p %s)",
-						srv->FD, srv, srv->Name);
-					return 1;
-				}
-			}
-			
-			if(FD_ISSET(srv->FD, &errorfds))
-			{
-				_SysDebug("Error on FD%i (Server %p %s)",
-					srv->FD, srv, srv->Name);
-				return 1;
-			}
-		}
+		Servers_HandleSelect(nfds, &readfds, &errorfds);
 	}
 	return 0;
 }
@@ -228,547 +125,12 @@ int ParseArguments(int argc, const char *argv[])
 	return 0;
 }
 
-
-void Cmd_join(char *ArgString)
-{
-	 int	pos=0;
-	char	*channel_name = GetValue(ArgString, &pos);
-	
-	if( gpCurrentWindow->Server )
-	{
-		gpCurrentWindow = Window_Create(gpCurrentWindow->Server, channel_name);
-		Redraw_Screen();
-		writef(gpCurrentWindow->Server->FD, "JOIN :%s\n", channel_name);
-	}
-}
-
-void Cmd_quit(char *ArgString)
-{
-	const char *quit_message = ArgString;
-	if( quit_message == NULL || quit_message[0] == '\0' )
-		quit_message = "/quit - Acess2 IRC Client";
-	
-	for( tServer *srv = gpServers; srv; srv = srv->Next )
-	{
-		writef(srv->FD, "QUIT :%s\n", quit_message);
-	}
-	
-	exit(0);
-}
-
-void Cmd_window(char *ArgString)
-{
-	 int	pos = 0;
-	char	*window_id = GetValue(ArgString, &pos);
-	 int	window_num = atoi(window_id);
-	
-	if( window_num > 0 )
-	{
-		tWindow	*win;
-		window_num --;	// Move to base 0
-		// Get `window_num`th window
-		for( win = gpWindows; win && window_num--; win = win->Next );
-		if( win ) {
-			gpCurrentWindow = win;
-			Redraw_Screen();
-		}
-		// Otherwise, silently ignore
-	}
-	else
-	{
-		window_num = 1;
-		for( tWindow *win = gpWindows; win; win = win->Next, window_num ++ )
-		{
-			if( win->Name[0] ) {
-				Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "",
-					"%i: %s/%s", window_num, win->Server->Name, win->Name);
-			}
-			else {
-				Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "",
-					"%i: (status)", window_num);
-			}
-		}
-	}
-}
-
-const struct {
-	const char *Name;
-	void	(*Fcn)(char *ArgString);
-} caCommands[] = {
-	{"join", Cmd_join},
-	{"quit", Cmd_quit},
-	{"window", Cmd_window},
-	{"win",    Cmd_window},
-	{"w",      Cmd_window},
-};
-const int ciNumCommands = sizeof(caCommands)/sizeof(caCommands[0]);
-
-/**
- * \brief Handle a line from the prompt
- */
-int ParseUserCommand(char *String)
-{
-	if( String[0] == '/' )
-	{
-		char	*command;
-		 int	pos = 0;
-		
-		command = GetValue(String, &pos)+1;
-
-		// TODO: Prefix matches
-		 int	cmdIdx = -1;
-		for( int i = 0; i < ciNumCommands; i ++ )
-		{
-			if( strcmp(command, caCommands[i].Name) == 0 ) {
-				cmdIdx = i;
-				break;
-			}
-		}
-		if( cmdIdx != -1 ) {
-			caCommands[cmdIdx].Fcn(String+pos);
-		}
-		else
-		{
-			Message_AppendF(NULL, MSG_TYPE_SERVER, "client", "", "Unknown command %s", command);
-		}
-	}
-	else
-	{
-		// Message
-		// - Only send if server is valid and window name is non-empty
-		if( gpCurrentWindow->Server && gpCurrentWindow->Name[0] )
-		{
-			Message_Append(gpCurrentWindow->Server, MSG_TYPE_STANDARD,
-				gsNickname, gpCurrentWindow->Name, String);
-			writef(gpCurrentWindow->Server->FD,
-				"PRIVMSG %s :%s\n", gpCurrentWindow->Name,
-				String
-				);
-		}
-	}
-	
-	return 0;
-}
-
-/**
- * \brief Connect to a server
- */
-tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber)
-{
-	tServer	*ret;
-	
-	ret = calloc(1, sizeof(tServer) + strlen(Name) + 1);
-	
-	strcpy(ret->Name, Name);
-	
-	// Connect to the remove server
-	ret->FD = OpenTCP( AddressString, PortNumber );
-	if( ret->FD == -1 ) {
-		fprintf(stderr, "%s: Unable to create socket\n", Name);
-		return NULL;
-	}
-	
-	// Append to open list
-	ret->Next = gpServers;
-	gpServers = ret;
-	
-	// Read some initial data
-	Message_Append(NULL, MSG_TYPE_SERVER, Name, "", "Connection opened");
-	ProcessIncoming(ret);
-	
-	// Identify
-	writef(ret->FD, "USER %s %s %s : %s\n", gsUsername, gsHostname, AddressString, gsRealName);
-	writef(ret->FD, "NICK %s\n", gsNickname);
-	Message_Append(NULL, MSG_TYPE_SERVER, Name, "", "Identified");
-	//printf("%s: Identified\n", Name);
-	
-	return ret;
-}
-
-tMessage *Message_AppendF(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message, ...)
-{
-	va_list	args;
-	 int	len;
-	va_start(args, Message);
-	len = vsnprintf(NULL, 0, Message, args);
-	va_end(args);
-	
-	char	buf[len+1];
-	va_start(args, Message);
-	vsnprintf(buf, len+1, Message, args);
-	va_end(args);
-	
-	return Message_Append(Server, Type, Source, Dest, buf);
-}
-
-tMessage *Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message)
-{
-	tWindow	*win = NULL;
-	 int	msgLen = strlen(Message);
-	
-	// Server==NULL indicates an internal message
-	if( Server == NULL || Source[0] == '\0' )
-	{
-		win = &gWindow_Status;
-	}
-	// Determine if it's a channel or PM
-	else if( Dest[0] == '#' || Dest[0] == '&' )	// TODO: Better determining here
-	{
-		for(win = gpWindows; win; win = win->Next)
-		{
-			if( win->Server == Server && strcmp(win->Name, Dest) == 0 )
-			{
-				break;
-			}
-		}
-		if( !win ) {
-			//win = Window_Create(Server, Dest);
-			win = &gWindow_Status;	// Stick it in the status window, just in case
-		}
-	}
-	#if 0
-	else if( strcmp(Dest, Server->Nick) != 0 )
-	{
-		// Umm... message for someone who isn't us?
-		win = &gWindow_Status;	// Stick it in the status window, just in case
-	}
-	#endif
-	// Server message?
-	else if( strchr(Source, '.') )	// TODO: And again, less hack please
-	{
-		#if 1
-		for(win = gpWindows; win; win = win->Next)
-		{
-			if( win->Server == Server && strcmp(win->Name, Source) == 0 )
-			{
-				break;
-			}
-		}
-		#endif
-		if( !win ) {
-			win = &gWindow_Status;
-		}
-		
-		// Set source to the server name (instead of the hostname)
-		Source = Server->Name;
-	}
-	// Private message
-	else
-	{
-		for(win = gpWindows; win; win = win->Next)
-		{
-			if( win->Server == Server && strcmp(win->Name, Source) == 0 )
-			{
-				break;
-			}
-		}
-		if( !win ) {
-			win = Window_Create(Server, Dest);
-		}
-	}
-
-	// Create message cache	
-	_SysDebug("Win (%s) msg: <%s> %s", win->Name, Source, Message);
-	tMessage	*ret;
-	ret = malloc( sizeof(tMessage) + msgLen + 1 + strlen(Source) + 1 );
-	ret->Source = ret->Data + msgLen + 1;
-	strcpy(ret->Source, Source);
-	strcpy(ret->Data, Message);
-	ret->Type = Type;
-	ret->Server = Server;
-	
-	// Append to window message list
-	ret->Next = win->Messages;
-	win->Messages = ret;
-	
-	// Print now if current window
-	if( win == gpCurrentWindow )
-	{
-		printf("\33[s");
-		printf("\33[T");	// Scroll down 1 (free space below)
-		SetCursorPos(giTerminal_Height-2, 1);
-		 int	prefixlen = strlen(Source) + 3;
-		 int	avail = giTerminal_Width - prefixlen;
-		 int	msglen = strlen(Message);
-		printf("[%s] %.*s", Source, avail, Message);
-		while( msglen > avail ) {
-			msglen -= avail;
-			Message += avail;
-			printf("\33[T");
-			SetCursorPos(giTerminal_Height-2, prefixlen+1);
-			printf("%.*s", avail, Message);
-		}
-		printf("\x1b[u");
-	}
-	
-	return ret;
-}
-
-tWindow *Window_Create(tServer *Server, const char *Name)
-{
-	tWindow	*ret, *prev = NULL;
-	 int	num = 1;
-	
-	// Get the end of the list
-	// TODO: Cache this instead
-	for( ret = gpCurrentWindow; ret; prev = ret, ret = ret->Next )
-		num ++;
-	
-	ret = malloc(sizeof(tWindow) + strlen(Name) + 1);
-	ret->Messages = NULL;
-	ret->Server = Server;
-	ret->ActivityLevel = 1;
-	strcpy(ret->Name, Name);
-	
-	if( prev ) {
-		ret->Next = prev->Next;
-		prev->Next = ret;
-	}
-	else {	// Shouldn't happen really
-		ret->Next = gpWindows;
-		gpWindows = ret;
-	}
-	
-//	printf("Win %i %s:%s created\n", num, Server->Name, Name);
-	
-	return ret;
-}
-
 void Redraw_Screen(void)
 {
-	 int	y = 0;
-	tMessage	*msg;
-
 	printf("\x1B[2J");	// Clear screen
-	printf("\x1B[0;0H");	// Reset cursor
-
-	msg = gpCurrentWindow->Messages;
-	
-	// TODO: Title bar?
-
-	// Note: This renders from the bottom up
-	for( y = giTerminal_Height - 1; y -- && msg; msg = msg->Next)
-	{
-		 int	msglen = strlen(msg->Data);
-		 int	prefix_len = 3 + strlen(msg->Source);
-		 int	line_avail = giTerminal_Width - prefix_len;
-		 int	i = 0, done = 0;
-		
-		y -= msglen / line_avail;	// Extra lines (y-- above handles the 1 line case)
-		SetCursorPos(y, 1);
-		printf("[%s] ", msg->Source);
-		
-		while(done < msglen) {
-			done += printf("%.*s", line_avail, msg->Data+done);
-			i ++;
-			SetCursorPos(y+i, prefix_len+1);
-		}
-	}
-
-	// Bottom line is rendered by the prompt
-}
-
-void Cmd_PRIVMSG(tServer *Server, const char *Dest, const char *Src, const char *Message)
-{
-	Message_Append(Server, MSG_TYPE_STANDARD, Dest, Src, Message);
-	//printf("<%s:%s:%s> %s\n", Server->Name, Dest, Src, Message);
-}
+	printf("\x1B[H");	// Reset cursor
 
-void ParseServerLine_Numeric(tServer *Server, const char *ident, int Num, char *Line)
-{
-	 int	pos = 0;
-	const char *message;
-	const char *user = GetValue(Line, &pos);
-	
-	if( Line[pos] == ':' ) {
-		message = Line + pos + 1;
-	}
-	else {
-		message = GetValue(Line, &pos);
-	}
-	
-	switch(Num)
-	{
-	case 332:	// Topic
-		user = message;	// Channel
-		message = Line + pos + 1;	// Topic
-		Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Topic: %s", message);
-		break;
-	case 333:	// Topic set by
-		user = message;	// Channel
-		message = GetValue(Line, &pos);	// User
-		GetValue(Line, &pos);	// Timestamp
-		Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Set by %s", message);
-		break;
-	case 353:	// /NAMES list
-		// <user> = <channel> :list
-		// '=' was eaten in and set to message
-		user = GetValue(Line, &pos);	// Actually channel
-		message = Line + pos + 1;	// List
-		Message_AppendF(Server, MSG_TYPE_SERVER, user, user, "Names: %s", message);
-		break;
-	case 366:	// end of /NAMES list
-		// <user> <channel> :msg
-		user = message;
-		message = Line + pos + 1;
-		Message_Append(Server, MSG_TYPE_SERVER, user, user, message);
-		break;
-	case 372:	// MOTD Data
-	case 375:	// MOTD Start
-	case 376:	// MOTD End
-		
-	default:
-		//printf("[%s] %i %s\n", Server->Name, num, message);
-		Message_Append(Server, MSG_TYPE_SERVER, ident, user, message);
-		break;
-	}
-}
-
-void ParseServerLine_String(tServer *Server, const char *ident, const char *cmd, char *Line)
-{
-	 int	pos = 0;
-	_SysDebug("ident=%s,cmd=%s,Line=%s", ident, cmd, Line);
-	if( strcmp(cmd, "NOTICE") == 0 )
-	{
-		const char *class = GetValue(Line, &pos);
-		_SysDebug("NOTICE class='%s'", class);
-		
-		const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
-		
-		//printf("[%s] NOTICE %s: %s\n", Server->Name, ident, message);
-		char *ident_bang = strchr(ident, '!');
-		if( ident_bang ) {
-			*ident_bang = '\0';
-		}
-		Message_Append(Server, MSG_TYPE_NOTICE, ident, "", message);
-	}
-	else if( strcmp(cmd, "PRIVMSG") == 0 )
-	{
-		const char *dest = GetValue(Line, &pos);
-		const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
-
-		// TODO: Catch when the privmsg is addressed to the user
-
-//		Cmd_PRIVMSG(Server, dest, ident, message);
-		char *ident_bang = strchr(ident, '!');
-		if( ident_bang ) {
-			*ident_bang = '\0';
-		}
-		Message_Append(Server, MSG_TYPE_STANDARD, ident, dest, message);
-	}
-	else if( strcmp(cmd, "JOIN" ) == 0 )
-	{
-		const char	*channel = Line + pos + 1;
-		
-		Message_AppendF(Server, MSG_TYPE_JOIN, "", channel, "%s has joined", ident);
-		//Window_Create(Server, channel);
-	}
-	else if( strcmp(cmd, "PART" ) == 0 )
-	{
-		const char	*channel = Line + pos + 1;
-		
-		Message_AppendF(Server, MSG_TYPE_PART, "", channel, "%s has left", ident);
-		//Window_Create(Server, channel);
-	}
-	else
-	{
-		Message_AppendF(Server, MSG_TYPE_SERVER, "", "", "Unknown message %s (%s)", cmd, Line);
-	}
-}
-
-/**
- */
-void ParseServerLine(tServer *Server, char *Line)
-{
-	 int	pos = 0;
-
-	_SysDebug("[%s] %s", Server->Name, Line);	
-	
-	// Message?
-	if( *Line == ':' )
-	{
-		pos ++;
-		const char *ident = GetValue(Line, &pos);	// Ident (user or server)
-		const char *cmd = GetValue(Line, &pos);
-		
-		// Numeric command
-		if( isdigit(cmd[0]) && isdigit(cmd[1]) && isdigit(cmd[2]) )
-		{
-			 int	num;
-			num  = (cmd[0] - '0') * 100;
-			num += (cmd[1] - '0') * 10;
-			num += (cmd[2] - '0') * 1;
-
-			ParseServerLine_Numeric(Server, ident, num, Line+pos);
-		}
-		else
-		{
-			ParseServerLine_String(Server, ident, cmd, Line+pos);
-		}
-	}
-	else {
-		const char *cmd = GetValue(Line, &pos);
-		
-		if( strcmp(cmd, "PING") == 0 ) {
-			writef(Server->FD, "PONG %s\n", Line+pos);
-		}
-		else {
-			// Command to client
-			Message_AppendF(NULL, MSG_TYPE_UNK, "", "", "Client Command: %s", Line);
-		}
-	}
-}
-
-/**
- * \brief Process incoming lines from the server
- */
-int ProcessIncoming(tServer *Server)
-{	
-	char	*ptr, *newline;
-	 int	len;
-	
-	// While there is data in the buffer, read it into user memory and 
-	// process it line by line
-	// ioctl#8 on a TCP client gets the number of bytes in the recieve buffer
-	// - Used to avoid blocking
-	#if NON_BLOCK_READ
-	while( (len = _SysIOCtl(Server->FD, 8, NULL)) > 0 )
-	{
-	#endif
-		// Read data
-		len = _SysRead(Server->FD, &Server->InBuf[Server->ReadPos], BUFSIZ - Server->ReadPos);
-		if( len == -1 ) {
-			return -1;
-		}
-		Server->InBuf[Server->ReadPos + len] = '\0';
-		
-		// Break into lines
-		ptr = Server->InBuf;
-		while( (newline = strchr(ptr, '\n')) )
-		{
-			*newline = '\0';
-			if( newline[-1] == '\r' )	newline[-1] = '\0';
-			ParseServerLine(Server, ptr);
-			ptr = newline + 1;
-		}
-		
-		// Handle incomplete lines
-		if( ptr - Server->InBuf < len + Server->ReadPos ) {
-			// Update the read position
-			// InBuf ReadPos    ptr          ReadPos+len
-			// | old | new used | new unused |
-			Server->ReadPos = len + Server->ReadPos - (ptr - Server->InBuf);
-			// Copy stuff back (moving "new unused" to the start of the buffer)
-			memcpy(Server->InBuf, ptr, Server->ReadPos);
-		}
-		else {
-			Server->ReadPos = 0;
-		}
-	#if NON_BLOCK_READ
-	}
-	#endif
-	
-	return 0;
+	Windows_RepaintCurrent();
 }
 
 /**
@@ -863,12 +225,3 @@ char *GetValue(char *Src, int *Ofs)
 	return ret;
 }
 
-void SetCursorPos(int Row, int Col)
-{
-	printf("\x1B[%i;%iH", Row, Col);
-}
-
-static inline int isdigit(int ch)
-{
-	return '0' <= ch && ch < '9';
-}
diff --git a/Usermode/Applications/irc_src/message.h b/Usermode/Applications/irc_src/message.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b86f124873a7e01a92f2bfb731401c695353c28
--- /dev/null
+++ b/Usermode/Applications/irc_src/message.h
@@ -0,0 +1,37 @@
+/*
+ */
+#ifndef _MESSAGE_H_
+#define _MESSAGE_H_
+
+enum eMessageTypes
+{
+	MSG_TYPE_NULL,
+	MSG_TYPE_SERVER,	// Server message
+	
+	MSG_TYPE_NOTICE,	// NOTICE command
+	MSG_TYPE_JOIN,	// JOIN command
+	MSG_TYPE_PART,	// PART command
+	MSG_TYPE_QUIT,	// QUIT command
+	
+	MSG_TYPE_STANDARD,	// Standard line
+	MSG_TYPE_ACTION,	// /me
+	
+	MSG_TYPE_UNK
+};
+
+enum eMessageClass
+{
+	MSG_CLASS_BARE,	// source is unused, just gets timestamped
+	MSG_CLASS_CLIENT,	// source optional, prefixed by '-!-'
+	MSG_CLASS_WALL, 	// [SOURCE] MSG 	-- Server-provided message
+	MSG_CLASS_MESSAGE,	// <SOURCE> MSG
+	MSG_CLASS_ACTION,	// * SOURCE MSG
+};
+
+typedef struct sMessage	tMessage;
+
+//extern tMessage	*Message_AppendF(tServer *Server, int Type, const char *Src, const char *Dst, const char *Fmt, ...) __attribute__((format(__printf__,5,6)));
+//extern tMessage	*Message_Append(tServer *Server, int Type, const char *Source, const char *Dest, const char *Message);
+
+#endif
+
diff --git a/Usermode/Applications/irc_src/pseudo_curses.c b/Usermode/Applications/irc_src/pseudo_curses.c
new file mode 100644
index 0000000000000000000000000000000000000000..23ee1371b2c0880d1953ceee807158183f8a5cfd
--- /dev/null
+++ b/Usermode/Applications/irc_src/pseudo_curses.c
@@ -0,0 +1,75 @@
+/*
+ */
+#include "pseudo_curses.h"
+#include <stdbool.h>
+#include <acess/sys.h>
+#include <acess/devices/pty.h>
+#include <stdio.h>
+
+// === PROTOTYPES ===
+bool	ACurses_GetDims_Acess(void);
+bool	ACurses_GetDims_SerialTermHack(void);
+
+// === GLOBALS ===
+ int	giTerminal_Width = 80;
+ int	giTerminal_Height = 25;
+
+// === CODE ===
+void ACurses_Init(void)
+{
+	if( ACurses_GetDims_Acess() ) {
+	}
+	else if( ACurses_GetDims_SerialTermHack() ) {
+	}
+	else {
+		_SysDebug("note: assuming 80x25, can't get terminal dimensions");
+		giTerminal_Width = 80;
+		giTerminal_Height = 25;
+	}
+}
+
+bool ACurses_GetDims_Acess(void)
+{
+	if( _SysIOCtl(1, DRV_IOCTL_TYPE, NULL) != DRV_TYPE_TERMINAL )
+		return false;
+	
+	struct ptydims	dims;
+	if( _SysIOCtl(1, PTY_IOCTL_GETDIMS, &dims) == -1 )
+		return false;
+	giTerminal_Width = dims.W;
+	giTerminal_Height = dims.H;
+	return true;
+}
+
+bool ACurses_GetDims_SerialTermHack(void)
+{
+	_SysDebug("ACurses_GetDims_SerialTermHack: Trying");
+	// Set cursor to 1000,1000 , request cursor position, reset cursor to 0,0
+	static const char req[] = "\033[1000;1000H\033[6n\033[H";
+	fflush(stdin);
+	_SysWrite(1, req, sizeof(req));
+	int64_t timeout = 1000;
+	fd_set fds;
+	FD_ZERO(&fds);
+	FD_SET(0, &fds);
+	_SysSelect(1, &fds, NULL, NULL, &timeout, 0);
+	if( !FD_ISSET(0, &fds) )
+		return false;
+	
+	if( fgetc(stdin) != '\x1b' )
+		return false;
+	if( fgetc(stdin) != '[' )
+		return false;
+	if( fscanf(stdin, "%i;%i", &giTerminal_Width, &giTerminal_Height) != 2 )
+		return false;
+	if( fgetc(stdin) != 'R' )
+		return false;
+	
+	return true;
+}
+
+void SetCursorPos(int Row, int Col)
+{
+	printf("\x1B[%i;%iH", Row, Col);
+}
+
diff --git a/Usermode/Applications/irc_src/pseudo_curses.h b/Usermode/Applications/irc_src/pseudo_curses.h
new file mode 100644
index 0000000000000000000000000000000000000000..0548e923eca71649b064201398fe308a021a4a95
--- /dev/null
+++ b/Usermode/Applications/irc_src/pseudo_curses.h
@@ -0,0 +1,13 @@
+/*
+ */
+#ifndef _ACURSES_H_
+#define _ACURSES_H_
+
+extern int	giTerminal_Width;
+extern int	giTerminal_Height;
+
+extern void	ACurses_Init(void);
+extern void	SetCursorPos(int Row, int Col);
+
+#endif
+
diff --git a/Usermode/Applications/irc_src/server.c b/Usermode/Applications/irc_src/server.c
new file mode 100644
index 0000000000000000000000000000000000000000..1b85df4fe8781dfaf9e27e9313d538bc6b5023be
--- /dev/null
+++ b/Usermode/Applications/irc_src/server.c
@@ -0,0 +1,407 @@
+/*
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "server.h"
+#include "window.h"
+#include <acess/sys.h>
+#include <ctype.h>	// isdigit
+#include <stdio.h>	// vsnprintf
+
+// === PROTOTYPES ===
+tServer	*Server_Connect(const char *Name, const char *AddressString, short PortNumber);
+ int	Server_HandleIncoming(tServer *Server);
+void	ParseServerLine(tServer *Server, char *Line);
+
+// === GLOBALS ===
+const char	*gsUsername = "user";
+const char	*gsHostname = "acess";
+const char	*gsRealName = "An Acess User";
+const char	*gsNickname = "acess";
+const char	*gsVersionResponse = "Acess2 IRC Client / Running on some VM probably";
+tServer	*gpServers;
+
+// === CODE ===
+void Servers_FillSelect(int *nfds, fd_set *rfds, fd_set *efds)
+{
+	for( tServer *srv = gpServers; srv; srv = srv->Next )
+	{
+		FD_SET(srv->FD, rfds);
+		FD_SET(srv->FD, efds);
+		if( srv->FD >= *nfds )
+			*nfds = srv->FD+1;
+	}
+}
+
+void Servers_HandleSelect(int nfds, const fd_set *rfds, const fd_set *efds)
+{
+	for( tServer *srv = gpServers; srv; srv = srv->Next )
+	{
+		if(FD_ISSET(srv->FD, rfds))
+		{
+			 int	rv = Server_HandleIncoming(srv);
+			if(rv)
+			{
+				// Oops, error
+				_SysDebug("ProcessIncoming failed on FD%i (Server %p %s)",
+					srv->FD, srv, srv->Name);
+				Exit("Processing error");
+			}
+		}
+		
+		if(FD_ISSET(srv->FD, efds))
+		{
+			_SysDebug("Error on FD%i (Server %p %s)",
+				srv->FD, srv, srv->Name);
+			Exit("Socket error");
+		}
+	}
+}
+
+void Servers_CloseAll(const char *QuitMessage)
+{
+	while( gpServers )
+	{
+		tServer *srv = gpServers;
+		gpServers = srv->Next;
+	
+		Server_SendCommand(srv, "QUIT :%s", QuitMessage);
+		_SysClose(srv->FD);
+		free(srv);
+	}
+}
+
+/**
+ * \brief Connect to a server
+ */
+tServer *Server_Connect(const char *Name, const char *AddressString, short PortNumber)
+{
+	tServer	*ret;
+	
+	ret = calloc(1, sizeof(tServer) + strlen(Name) + 1);
+	
+	strcpy(ret->Name, Name);
+	
+	// Connect to the remove server
+	ret->FD = OpenTCP( AddressString, PortNumber );
+	if( ret->FD == -1 ) {
+		free(ret);
+		Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Unable to create socket");
+		return NULL;
+	}
+	
+	ret->Nick = strdup(gsNickname);
+	
+	// Append to open list
+	ret->Next = gpServers;
+	gpServers = ret;
+	
+	// Read some initial data
+	Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Unable to create socket");
+	Server_HandleIncoming(ret);
+	
+	// Identify
+	Server_SendCommand(ret, "USER %s %s %s : %s", gsUsername, gsHostname, AddressString, gsRealName);
+	Server_SendCommand(ret, "NICK %s", ret->Nick);
+	Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Name, "Identified");
+	
+	return ret;
+}
+
+const char *Server_GetNick(const tServer *Server)
+{
+	return Server ? Server->Nick : "NULL";
+}
+const char *Server_GetName(const tServer *Server)
+{
+	return Server ? Server->Name : "NULL";
+}
+
+void Server_SendCommand(tServer *Server, const char *Format, ...)
+{
+	va_list	args;
+	 int	len;
+	
+	va_start(args, Format);
+	len = vsnprintf(NULL, 0, Format, args);
+	va_end(args);
+	
+	char	buf[len+1];
+	va_start(args, Format);
+	vsnprintf(buf, len+1, Format, args);
+	va_end(args);
+	
+	_SysWrite(Server->FD, buf, len);
+	_SysWrite(Server->FD, "\n", 1);
+}
+
+void Cmd_PRIVMSG(tServer *Server, const char *Dest, const char *Src, const char *Message)
+{
+	tWindow *win;
+	if( strcmp(Dest, Server->Nick) == 0 ) {
+		win = Windows_GetByNameOrCreate(Server, Src);
+	}
+	else {
+		win = Windows_GetByNameOrCreate(Server, Dest);
+	}
+	
+	// Detect CTCP
+	if( Message[0] == '\1' && Message[strlen(Message)-1] == '\1' )
+	{
+		Message += 1;
+		// message is a CTCP command
+		if( strcmp(Message, "VERSION\1") == 0 )
+		{
+			// Put a message in the status window, and reply
+			Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Server->Name, "CTCP VERSION request from %s", Src);
+			// - Always reply via NOTICE
+			Server_SendCommand(Server, "NOTICE %s :\1VERSION %s\1", Src, gsVersionResponse);
+		}
+		else if( strncmp(Message, "ACTION ", 7) == 0 )
+		{
+			Message += 7;
+			// Put a message in the status window, and reply
+			Window_AppendMessage(win, MSG_CLASS_ACTION, Src, "%.*s", (int)(strlen(Message)-1), Message);
+		}
+		else
+		{
+			Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Server->Name, "Unknown CTCP '%s' from %s",
+				Message, Src);
+		}
+	}
+	else
+	{
+		Window_AppendMessage(win, MSG_CLASS_MESSAGE, Src, "%s", Message);
+	}
+}
+
+/**
+ * \brief Process incoming lines from the server
+ */
+int Server_HandleIncoming(tServer *Server)
+{	
+	char	*ptr, *newline;
+	 int	len;
+	
+	// While there is data in the buffer, read it into user memory and 
+	// process it line by line
+	// ioctl#8 on a TCP client gets the number of bytes in the recieve buffer
+	// - Used to avoid blocking
+	#if NON_BLOCK_READ
+	while( (len = _SysIOCtl(Server->FD, 8, NULL)) > 0 )
+	{
+	#endif
+		// Read data
+		len = _SysRead(Server->FD, &Server->InBuf[Server->ReadPos], sizeof(Server->InBuf) - Server->ReadPos);
+		if( len == -1 ) {
+			return -1;
+		}
+		Server->InBuf[Server->ReadPos + len] = '\0';
+		
+		// Break into lines
+		ptr = Server->InBuf;
+		while( (newline = strchr(ptr, '\n')) )
+		{
+			*newline = '\0';
+			if( newline[-1] == '\r' )	newline[-1] = '\0';
+			ParseServerLine(Server, ptr);
+			ptr = newline + 1;
+		}
+		
+		// Handle incomplete lines
+		if( ptr - Server->InBuf < len + Server->ReadPos ) {
+			// Update the read position
+			// InBuf ReadPos    ptr          ReadPos+len
+			// | old | new used | new unused |
+			Server->ReadPos = len + Server->ReadPos - (ptr - Server->InBuf);
+			// Copy stuff back (moving "new unused" to the start of the buffer)
+			memcpy(Server->InBuf, ptr, Server->ReadPos);
+		}
+		else {
+			Server->ReadPos = 0;
+		}
+	#if NON_BLOCK_READ
+	}
+	#endif
+	
+	return 0;
+}
+
+void ParseServerLine_Numeric(tServer *Server, const char *ident, int Num, char *Line)
+{
+	 int	pos = 0;
+	const char *message;
+	const char *user = GetValue(Line, &pos);
+	const char	*timestamp;
+	
+	if( Line[pos] == ':' ) {
+		message = Line + pos + 1;
+	}
+	else {
+		message = GetValue(Line, &pos);
+	}
+	
+	switch(Num)
+	{
+	case 332:	// Topic
+		user = message;	// Channel
+		message = Line + pos + 1;	// Topic
+		Window_AppendMsg_Topic( Windows_GetByNameOrCreate(Server, user), message );
+		break;
+	case 333:	// Topic set by
+		user = message;	// Channel
+		message = GetValue(Line, &pos);	// User
+		timestamp = GetValue(Line, &pos);	// Timestamp
+		Window_AppendMsg_TopicTime( Windows_GetByNameOrCreate(Server, user), message, timestamp );
+		break;
+	case 353:	// /NAMES list
+		// TODO: Parse the /names list and store it locally, dump to window when end is seen
+		// <user> = <channel> :list
+		// '=' was eaten in and set to message
+		user = GetValue(Line, &pos);	// Actually channel
+		message = Line + pos + 1;	// List
+		Window_AppendMessage( Windows_GetByNameOrCreate(Server, user), MSG_CLASS_CLIENT, "NAMES", message );
+		break;
+	case 366:	// end of /NAMES list
+		// <user> <channel> :msg
+		// - Ignored
+		break;
+
+	case   1:	// welcome
+	case   2:	// host name and version (text)
+	case   3:	// host uptime
+	case   4:	// host name, version, and signature
+	case   5:	// parameters
+	
+	case 250:	// Highest connection count
+	case 251:	// user count (network)
+	case 252:	// Operator count
+	case 253:	// Unidentified connections
+	case 254:	// Channel count
+	case 255:	// Server's stats
+	case 265:	// Local users -- min max :Text representation
+	case 266:	// Global users (same as above)
+	
+	case 372:	// MOTD Data
+	case 375:	// MOTD Start
+	case 376:	// MOTD End
+		Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_WALL, Server->Name, "%s", message);
+		break;
+		
+	default:
+		//printf("[%s] %i %s\n", Server->Name, num, message);
+		Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_WALL, Server->Name, "Unknown %i %s", Num, message);
+		break;
+	}
+}
+
+void ParseServerLine_String(tServer *Server, const char *ident, const char *cmd, char *Line)
+{
+	 int	pos = 0;
+	_SysDebug("ident=%s,cmd=%s,Line=%s", ident, cmd, Line);
+	if( strcmp(cmd, "NOTICE") == 0 )
+	{
+		const char *class = GetValue(Line, &pos);
+		const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
+		_SysDebug("NOTICE class='%s'", class);
+		
+		char *ident_bang = strchr(ident, '!');
+		if( ident_bang ) {
+			*ident_bang = '\0';
+		}
+		// TODO: Colour codes
+		Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_WALL, Server->Name, "%s %s", ident, message);
+	}
+	else if( strcmp(cmd, "PRIVMSG") == 0 )
+	{
+		const char *dest = GetValue(Line, &pos);
+		const char *message = (Line[pos] == ':') ? Line + pos + 1 : GetValue(Line, &pos);
+
+		char *ident_bang = strchr(ident, '!');
+		if( ident_bang ) {
+			*ident_bang = '\0';
+		}
+		Cmd_PRIVMSG(Server, dest, ident, message);
+	}
+	else if( strcmp(cmd, "JOIN" ) == 0 )
+	{
+		const char	*channel = Line + pos + 1;
+		
+		Window_AppendMsg_Join( Windows_GetByNameOrCreate(Server, channel), ident );
+	}
+	else if( strcmp(cmd, "PART" ) == 0 )
+	{
+		const char	*channel = Line + pos + 1;
+		
+		Window_AppendMsg_Part( Windows_GetByNameOrCreate(Server, channel), ident, "" );
+	}
+	else if( strcmp(cmd, "MODE" ) == 0 )
+	{
+		// ident MODE channel flags nick[ nick...]
+		const char	*channel = GetValue(Line, &pos);
+		const char	*flags = GetValue(Line, &pos);
+		const char	*args = Line + pos;
+		
+		Window_AppendMsg_Mode( Windows_GetByNameOrCreate(Server, channel), ident, flags, args );
+	}
+	else if( strcmp(cmd, "KICK" ) == 0 )
+	{
+		// ident KICK channel nick :reason
+		const char	*channel = GetValue(Line, &pos);
+		const char	*nick = GetValue(Line, &pos);
+		const char	*message = Line + pos + 1;
+		
+		Window_AppendMsg_Kick( Windows_GetByNameOrCreate(Server, channel), ident, nick, message );
+		if( strcmp(nick, Server->Nick) == 0 ) {
+			// Oh, that was me :(
+			// - what do?
+		}
+	}
+	else
+	{
+		Window_AppendMessage( WINDOW_STATUS, MSG_CLASS_BARE, Server->Name, "Unknown command '%s' %s", cmd, Line);
+	}
+}
+
+/**
+ */
+void ParseServerLine(tServer *Server, char *Line)
+{
+	 int	pos = 0;
+
+	_SysDebug("[%s] %s", Server->Name, Line);	
+	
+	// Message?
+	if( *Line == ':' )
+	{
+		pos ++;
+		const char *ident = GetValue(Line, &pos);	// Ident (user or server)
+		const char *cmd = GetValue(Line, &pos);
+		
+		// Numeric command
+		if( isdigit(cmd[0]) && isdigit(cmd[1]) && isdigit(cmd[2]) )
+		{
+			 int	num;
+			num  = (cmd[0] - '0') * 100;
+			num += (cmd[1] - '0') * 10;
+			num += (cmd[2] - '0') * 1;
+
+			ParseServerLine_Numeric(Server, ident, num, Line+pos);
+		}
+		else
+		{
+			ParseServerLine_String(Server, ident, cmd, Line+pos);
+		}
+	}
+	else {
+		const char *cmd = GetValue(Line, &pos);
+		
+		if( strcmp(cmd, "PING") == 0 ) {
+			Server_SendCommand(Server, "PONG %s", Line+pos);
+		}
+		else {
+			// Command to client
+			Window_AppendMessage(WINDOW_STATUS, MSG_CLASS_CLIENT, Server->Name, "UNK Client Command: %s", Line);
+		}
+	}
+}
diff --git a/Usermode/Applications/irc_src/server.h b/Usermode/Applications/irc_src/server.h
new file mode 100644
index 0000000000000000000000000000000000000000..edc9bd0a7ee0084f5ed34524a0ca825f460a47df
--- /dev/null
+++ b/Usermode/Applications/irc_src/server.h
@@ -0,0 +1,30 @@
+/*
+ */
+#ifndef _SERVER_H_
+#define _SERVER_H_
+
+#include "common.h"
+
+typedef struct sServer {
+	struct sServer	*Next;
+	 int	FD;
+	char	InBuf[1024+1];
+	 int	ReadPos;
+	char	*Nick;
+	char	Name[];
+} tServer;
+
+extern void	Servers_FillSelect(int *nfds, fd_set *rfds, fd_set *efds);
+extern void	Servers_HandleSelect(int nfds, const fd_set *rfds, const fd_set *efds);
+extern void	Servers_CloseAll(const char *QuitMessage);
+
+extern tServer	*Server_Connect(const char *Name, const char *AddressString, short PortNumber);
+extern  int	Server_HandleIncoming(tServer *Server);
+
+extern const char	*Server_GetNick(const tServer *Server);
+extern const char	*Server_GetName(const tServer *Server);
+
+extern void	Server_SendCommand(tServer *Server, const char *Format, ...) __attribute__((format(__printf__,2,3)));
+
+#endif
+
diff --git a/Usermode/Applications/irc_src/window.c b/Usermode/Applications/irc_src/window.c
new file mode 100644
index 0000000000000000000000000000000000000000..ba12880c1f3f33efd9fb15d4e9985adea070b06f
--- /dev/null
+++ b/Usermode/Applications/irc_src/window.c
@@ -0,0 +1,358 @@
+/*
+ */
+#include "window.h"
+#include <stddef.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>	// TODO: replace with calls into ACurses_*
+#include <stdlib.h>
+#include <assert.h>
+#include "server.h"
+#include <ctype.h>
+
+struct sMessage
+{
+	struct sMessage	*Next;
+	time_t	Timestamp;
+	enum eMessageClass	Class;
+	char	*Source;	// Pointer to the end of `Data`
+	size_t	PrefixLen;
+	char	Data[];
+};
+
+struct sWindow
+{
+	struct sWindow	*Next;
+	tMessage	*Messages;
+	tServer	*Server;	//!< Canonical server (can be NULL)
+	 int	ActivityLevel;
+	char	Name[];	// Channel name / remote user
+};
+
+// === PROTOTYPES ===
+void	Windows_RepaintCurrent(void);
+size_t	WordBreak(const char *Line, size_t avail);
+size_t	Windows_int_PaintMessagePrefix(const tMessage *Message, bool EnablePrint);
+size_t	Windows_int_PaintMessageLine(const tMessage *Message, size_t Offset, bool EnablePrint);
+ int	Windows_int_GetMessageLines(const tMessage *Message);
+ int	Windows_int_PaintMessage(tMessage *Message);
+
+// === GLOBALS ===
+tWindow	gWindow_Status = {
+	NULL, NULL, NULL,	// No next, empty list, no server
+	0, {"(status)"}	// No activity, empty name (rendered as status)
+};
+tWindow	*gpWindows = &gWindow_Status;
+tWindow	*gpCurrentWindow = &gWindow_Status;
+
+// === CODE ===
+void Windows_RepaintCurrent(void)
+{
+	// TODO: Title bar?
+	SetCursorPos(1, 1);
+	printf("\x1b[37;44m\x1b[2K%s\x1b[0m\n", gpCurrentWindow->Name);
+
+	 int	avail_rows = giTerminal_Height - 3;
+
+	// Note: This renders from the bottom up
+	tMessage *msg = gpCurrentWindow->Messages;
+	for( int y = avail_rows; msg && y > 0; )
+	{
+		int lines = Windows_int_GetMessageLines(msg);
+		y -= lines;
+		size_t	ofs = 0;
+		size_t	len;
+		 int	i = 0;
+		do {
+			SetCursorPos(2 + y+i, 1);
+			len = Windows_int_PaintMessageLine(msg, ofs, (y+i >= 0));
+			ofs += len;
+			i ++;
+		} while( len > 0 );
+		msg = msg->Next;
+	}
+
+	// Status line is our department
+	SetCursorPos(giTerminal_Height-1, 1);
+	printf("\x1b[37;44m\x1b[2K[%s] [%s/%s]\x1b[0m", Server_GetNick(gpCurrentWindow->Server),
+		Server_GetName(gpCurrentWindow->Server), gpCurrentWindow->Name);
+	fflush(stdout);
+	// Bottom line is rendered by the prompt
+}
+
+size_t WordBreak(const char *Line, size_t avail)
+{
+	// If sufficient space, don't need to break on a word
+	if( strlen(Line) < avail )
+		return strlen(Line);
+	
+	// Search backwards from end of space for a non-alpha-numeric character
+	size_t	ret = avail-1;
+	while( ret > 0 && isalnum(Line[ret]) )
+		ret --;
+	
+	// if one wasn't found in a sane area, just split
+	if( ret < avail-20 || ret == 0 )
+		return avail;
+	
+	return ret;
+}
+
+size_t Windows_int_PaintMessagePrefix(const tMessage *Message, bool EnablePrint)
+{
+	size_t	len = 0;
+	
+	unsigned long	seconds_today = Message->Timestamp/1000 % (24 * 3600);
+	if(EnablePrint)
+		printf("%02i:%02i:%02i ", seconds_today/3600, (seconds_today/60)%60, seconds_today%60);
+	else
+		len += snprintf(NULL, 0, "%02i:%02i:%02i ", seconds_today/3600, (seconds_today/60)%60, seconds_today%60);
+	
+	const char *format;
+	switch(Message->Class)
+	{
+	case MSG_CLASS_BARE:
+		format = "";
+		break;
+	case MSG_CLASS_CLIENT:
+		if(Message->Source)
+			format = "[%s] -!- ";
+		else 
+			format = "-!- ";
+		break;
+	case MSG_CLASS_WALL:
+		format = "[%s] ";
+		break;
+	case MSG_CLASS_MESSAGE:
+		format = "<%s> ";
+		break;
+	case MSG_CLASS_ACTION:
+		format = "* %s ";
+		break;
+	}
+	
+	if( EnablePrint )
+		len += printf(format, Message->Source);
+	else
+		len += snprintf(NULL, 0, format, Message->Source);
+
+	return len;
+}
+
+size_t Windows_int_PaintMessageLine(const tMessage *Message, size_t Offset, bool EnablePrint)
+{
+	_SysDebug("Windows_int_PaintMessageLine: Message=%p,Offset=%i,EnablePrint=%b",
+		Message, (int)Offset, EnablePrint);
+	if( Offset > strlen(Message->Data) ) {
+		_SysDebug("Windows_int_PaintMessageLine: No message left");
+		return 0;
+	}
+	_SysDebug("Windows_int_PaintMessageLine: Message->Data=\"%.*s\"+\"%s\"",
+		(int)Offset, Message->Data, Message->Data+Offset);
+	
+	size_t	avail = giTerminal_Width - Message->PrefixLen;
+	const char *msg_data = Message->Data + Offset;
+	int used = WordBreak(msg_data, avail);
+	
+	if( EnablePrint )
+	{
+		if( Offset == 0 )
+			Windows_int_PaintMessagePrefix(Message, true);
+		else {
+			for(int i = 0; i < Message->PrefixLen; i ++)
+				printf(" ");
+			//printf("\x1b[%iC", Message->PrefixLen);
+		}
+		printf("%.*s", used, msg_data);
+	}
+	
+	_SysDebug("used(%i) >= strlen(msg_data)(%i)", used, strlen(msg_data));
+	if( used >= strlen(msg_data) )
+		return 0;
+	
+	return Offset + used;
+}
+
+int Windows_int_GetMessageLines(const tMessage *Message)
+{
+	assert(Message->PrefixLen);
+	const size_t	avail = giTerminal_Height - Message->PrefixLen;
+	const size_t	msglen = strlen(Message->Data);
+	size_t	offset = 0;
+	 int	nLines = 0;
+	do {
+		offset += WordBreak(Message->Data+offset, avail);
+		nLines ++;
+	} while(offset < msglen);
+	return nLines;
+}
+
+void Windows_SwitchTo(tWindow *Window)
+{
+	gpCurrentWindow = Window;
+	Redraw_Screen();
+}
+
+void Windows_SetStatusServer(tServer *Server)
+{
+	gWindow_Status.Server = Server;
+}
+
+tWindow *Windows_GetByIndex(int Index)
+{
+	tWindow	*win;
+	for( win = gpWindows; win && Index--; win = win->Next )
+		;
+	return win;
+}
+
+tWindow *Windows_GetByNameEx(tServer *Server, const char *Name, bool CreateAllowed)
+{
+	tWindow	*ret, *prev = NULL;
+	 int	num = 1;
+	
+	// Get the end of the list and check for duplicates
+	// TODO: Cache this instead
+	for( ret = &gWindow_Status; ret; prev = ret, ret = ret->Next )
+	{
+		if( ret->Server == Server && strcmp(ret->Name, Name) == 0 )
+		{
+			return ret;
+		}
+		num ++;
+	}
+	if( !CreateAllowed ) {
+		return NULL;
+	}
+	
+	ret = malloc(sizeof(tWindow) + strlen(Name) + 1);
+	ret->Messages = NULL;
+	ret->Server = Server;
+	ret->ActivityLevel = 1;
+	strcpy(ret->Name, Name);
+	
+	if( prev ) {
+		ret->Next = prev->Next;
+		prev->Next = ret;
+	}
+	else {	// Shouldn't happen really
+		ret->Next = gpWindows;
+		gpWindows = ret;
+	}
+	
+//	printf("Win %i %s:%s created\n", num, Server->Name, Name);
+	
+	return ret;
+}
+
+tWindow *Windows_GetByName(tServer *Server, const char *Name)
+{
+	return Windows_GetByNameEx(Server, Name, false);
+}
+
+tWindow *Window_Create(tServer *Server, const char *Name)
+{
+	return Windows_GetByNameEx(Server, Name, true);
+}
+
+
+tWindow *Window_int_ParseSpecial(const tWindow *Window)
+{
+	if( Window == NULL )
+		return gpCurrentWindow;
+	if( Window == WINDOW_STATUS )
+		return &gWindow_Status;
+	return (tWindow*)Window;
+}
+
+const char *Window_GetName(const tWindow *Window) {
+	return Window_int_ParseSpecial(Window)->Name;
+}
+tServer	*Window_GetServer(const tWindow *Window) {
+	return Window_int_ParseSpecial(Window)->Server;
+}
+bool Window_IsChat(const tWindow *Window) {
+	return Window_int_ParseSpecial(Window) != &gWindow_Status;
+}
+
+// -----------------------------
+//  Messages
+// -----------------------------
+void Window_AppendMessage(tWindow *Window, enum eMessageClass Class, const char *Source, const char *Message, ...)
+{
+	Window = Window_int_ParseSpecial(Window);
+
+	va_list	args;
+	
+	va_start(args, Message);
+	size_t len = vsnprintf(NULL, 0, Message, args);
+	va_end(args);
+	
+	tMessage *msg = malloc( sizeof(tMessage) + len+1 + (Source?strlen(Source)+1:0) );
+	assert(msg);
+	
+	msg->Class = Class;
+	msg->Source = (Source ? msg->Data + len+1 : NULL);
+	msg->Timestamp = _SysTimestamp();
+	
+	va_start(args, Message);
+	vsnprintf(msg->Data, len+1, Message, args);
+	va_end(args);
+	
+	if( Source ) {
+		strcpy(msg->Source, Source);
+	}
+	
+	msg->PrefixLen = Windows_int_PaintMessagePrefix(msg, false);
+	
+	msg->Next = Window->Messages;
+	Window->Messages = msg;
+	
+	if( Window == gpCurrentWindow )
+	{
+		// Scroll if needed, and redraw?
+		// - Lazy option of draw at bottom of screen
+		printf("\33[s");	// Save cursor
+		size_t	offset = 0, len;
+		do {
+			printf("\33[S");	// Scroll down 1 (free space below)
+			SetCursorPos(giTerminal_Height-2, 1);
+			len = Windows_int_PaintMessageLine(msg, offset, true);
+			offset += len;
+		} while( len > 0 );
+		printf("\33[u");	// Restore cursor
+		fflush(stdout);
+	}
+}
+
+void Window_AppendMsg_Join(tWindow *Window, const char *Usermask)
+{
+	Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined %s", Usermask, Window->Name);
+}
+void Window_AppendMsg_Quit(tWindow *Window, const char *Usermask, const char *Reason)
+{
+	Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined quit (%s)", Usermask, Reason);
+}
+void Window_AppendMsg_Part(tWindow *Window, const char *Usermask, const char *Reason)
+{
+	Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined left %s (%s)", Usermask, Window->Name, Reason);
+}
+void Window_AppendMsg_Topic(tWindow *Window, const char *Topic)
+{
+	Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "Topic of %s is %s", Window->Name, Topic);
+}
+void Window_AppendMsg_TopicTime(tWindow *Window, const char *User, const char *Timestamp)
+{
+	Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "Topic set by %s at %s", User, Timestamp);
+}
+void Window_AppendMsg_Kick(tWindow *Window, const char *Operator, const char *Nick, const char *Reason)
+{
+	Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s was kicked from %s by %s [%s]",
+		Nick, Window->Name, Operator, Reason);
+}
+void Window_AppendMsg_Mode(tWindow *Window, const char *Operator, const char *Flags, const char *Args)
+{
+	Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "mode/%s [%s %s] by %s",
+		Window->Name, Flags, Args, Operator);
+}
+
diff --git a/Usermode/Applications/irc_src/window.h b/Usermode/Applications/irc_src/window.h
new file mode 100644
index 0000000000000000000000000000000000000000..620e1a5da2a5fd374ae2b00568e3612de4b4d4f6
--- /dev/null
+++ b/Usermode/Applications/irc_src/window.h
@@ -0,0 +1,38 @@
+/*
+ */
+#ifndef _WINDOW_H_
+#define _WINDOW_H_
+
+#include "common.h"
+#include "message.h"
+#include <stdbool.h>
+
+typedef struct sWindow	tWindow;
+extern void	Windows_RepaintCurrent(void);
+
+extern void	Windows_SetStatusServer(tServer *Server);
+extern tWindow	*Window_Create(tServer *Server, const char *Name);
+extern tWindow	*Windows_GetByIndex(int Index);
+extern tWindow	*Windows_GetByName(tServer *Server, const char *Name);
+static inline tWindow	*Windows_GetByNameOrCreate(tServer *Server, const char *Name) {
+	return Window_Create(Server, Name);
+}
+extern void	Windows_SwitchTo(tWindow *Window);
+
+extern void	Window_AppendMessage(tWindow *Window, enum eMessageClass Class, const char *Source, const char *Message, ...)
+	__attribute__((format(__printf__,4,5)));
+extern void	Window_AppendMsg_Join(tWindow *Window, const char *Usermask);
+extern void	Window_AppendMsg_Quit(tWindow *Window, const char *Usermask, const char *Reason);
+extern void	Window_AppendMsg_Part(tWindow *Window, const char *Usermask, const char *Reason);
+extern void	Window_AppendMsg_Kick(tWindow *Window, const char *Operator, const char *Nick, const char *Reason);
+extern void	Window_AppendMsg_Mode(tWindow *Window, const char *Operator, const char *Flags, const char *Args);
+extern void	Window_AppendMsg_Topic(tWindow *Window, const char *Topic);
+extern void	Window_AppendMsg_TopicTime(tWindow *Window, const char *User, const char *Timestmap);
+
+extern const char	*Window_GetName(const tWindow *Window);
+extern tServer	*Window_GetServer(const tWindow *Window);
+extern bool	Window_IsChat(const tWindow *Window);
+
+#define WINDOW_STATUS	((void*)-1)
+
+#endif
diff --git a/Usermode/Applications/ping_src/Makefile b/Usermode/Applications/ping_src/Makefile
index 64c4649e8014da82086f4828395317c2ff69c552..3893fa0fcd146d9cd2df54c0414737df4f703fa5 100644
--- a/Usermode/Applications/ping_src/Makefile
+++ b/Usermode/Applications/ping_src/Makefile
@@ -2,7 +2,7 @@
 
 -include ../Makefile.cfg
 
-LDFLAGS += -lnet
+LIBS += -lnet
 
 OBJ = main.o
 BIN = ping
diff --git a/Usermode/Applications/telnet_src/Makefile b/Usermode/Applications/telnet_src/Makefile
index bbcac85fba5217e85a2c9b414ed503d3ef796a18..7d61245e099d342ff5ee7073e2cf9ebc21eb5aff 100644
--- a/Usermode/Applications/telnet_src/Makefile
+++ b/Usermode/Applications/telnet_src/Makefile
@@ -2,7 +2,7 @@
 
 -include ../Makefile.cfg
 
-LDFLAGS += -lnet -lreadline
+LIBS += -lnet -lreadline
 
 OBJ = main.o
 BIN = telnet
diff --git a/Usermode/Applications/telnetd_src/Makefile b/Usermode/Applications/telnetd_src/Makefile
index a85a07121c7165ac3e955f1af01497fcba9c51ab..d7d6ee6eb0ad29a7d535919706c140eaa87ea354 100644
--- a/Usermode/Applications/telnetd_src/Makefile
+++ b/Usermode/Applications/telnetd_src/Makefile
@@ -2,7 +2,7 @@
 
 -include ../Makefile.cfg
 
-LDFLAGS += -lnet
+LIBS += -lnet
 
 OBJ = main.o
 BIN = telnetd
diff --git a/Usermode/Applications/wget_src/Makefile b/Usermode/Applications/wget_src/Makefile
index e6f5a012b29faf2d60a58fabddca1b5213116ba7..db28b27fa239466cd73afc8817d0694497493532 100644
--- a/Usermode/Applications/wget_src/Makefile
+++ b/Usermode/Applications/wget_src/Makefile
@@ -3,7 +3,8 @@
 -include ../Makefile.cfg
 
 CFLAGS += -std=gnu99
-LDFLAGS += -lnet -lpsocket -luri
+LIBS += -lnet -lpsocket -luri
+
 OBJ = main.o
 BIN = wget
 
diff --git a/Usermode/Libraries/Makefile.cfg b/Usermode/Libraries/Makefile.cfg
index 5920059e71f70f3bd300fac550487da0fe86726e..b10117e493f67393d8063944a974a70d3e4b008c 100644
--- a/Usermode/Libraries/Makefile.cfg
+++ b/Usermode/Libraries/Makefile.cfg
@@ -4,11 +4,14 @@
 _libsdir := $(dir $(lastword $(MAKEFILE_LIST)))
 -include $(_libsdir)../Makefile.cfg
 
+USE_CXX_LINK :=
+PRELINK :=
 MAKEDEP  = $(CC) -M
 
 ifeq ($(ARCHDIR),native)
  ASFLAGS += -D ARCHDIR=$(ARCHDIR) -D __ASSEMBLER__=1
- LDFLAGS := -lacess-native
+ LDFLAGS := 
+ LIBS := -lacess-native
  #CPPFLAGS := -D SoMain="__attribute__ ((constructor(101))) libacessnative_init"
  ifeq ($(PLATFORM),windows)
  else
@@ -26,13 +29,16 @@ else ifneq ($(HOST_ARCH),)
   CFLAGS += -fPIC
   CXXFLAGS += -fPIC
  endif
+ LIBS := -lld-acess
 else
- CPPFLAGS := -ffreestanding
  CFLAGS   := -fno-stack-protector -fPIC
- CXXFLAGS   := -fno-stack-protector -fPIC
- LDFLAGS  := -I/Acess/Libs/ld-acess.so -lld-acess `$(CC) -print-libgcc-file-name`
+ CXXFLAGS := -fno-stack-protector -fPIC
+ LDFLAGS  := 
+ LIBS := -lld-acess
 endif
-LDFLAGS += -g -nostdlib -shared -eSoMain -x --no-undefined -L$(OUTPUTDIR)Libs/
+LDFLAGS += -g -shared -eSoStart -L$(OUTPUTDIR)Libs/ --no-undefined
+CXXFLAGS += -std=gnu++11
+#CPPFLAGS += -D 'SoMain(...)=SoMain(__VA_ARGS__) __attribute__ ((visibility ("hidden"))); int SoMain(__VA_ARGS__)'
 
 -include $(_libsdir)../common_settings.mk
 
diff --git a/Usermode/Libraries/Makefile.tpl b/Usermode/Libraries/Makefile.tpl
index a99ff144e3099134fd0ea5a9f0f745d4d5787d49..3b8d2f55aca31fab14e0edfd2d57e3820ed6daa0 100644
--- a/Usermode/Libraries/Makefile.tpl
+++ b/Usermode/Libraries/Makefile.tpl
@@ -10,21 +10,28 @@ ifeq ($(ARCH),native)
  LDFLAGS := $(LDFLAGS:-lc=-lc_acess)
 endif
 
-_LD_CMD := $(lastword $(subst -, ,$(firstword $(LD))))
-ifneq ($(_LD_CMD),ld)
-  LDFLAGS := $(subst -soname ,-Wl$(comma)-soname$(comma),$(LDFLAGS))
-  LDFLAGS := $(subst -Map ,-Wl$(comma)-Map$(comma),$(LDFLAGS))
-  LDFLAGS := $(LDFLAGS:-x=-Wl,-x)
-  LDFLAGS := $(LDFLAGS:--%=-Wl,--%)
-endif
-
 _BIN := $(addprefix $(OUTPUTDIR)Libs/,$(BIN))
 _XBIN := $(addprefix $(OUTPUTDIR)Libs/,$(EXTRABIN))
 _OBJPREFIX := obj-$(ARCH)/
+LDFLAGS += -Map $(_OBJPREFIX)Map.txt
+
+_LD_CMD := $(lastword $(subst -, ,$(firstword $(LD))))
+LDFLAGS := $(subst -soname ,-Wl$(comma)-soname$(comma),$(LDFLAGS))
+LDFLAGS := $(subst -Map ,-Wl$(comma)-Map$(comma),$(LDFLAGS))
+LDFLAGS := $(LDFLAGS:-x=-Wl,-x)
+LDFLAGS := $(LDFLAGS:--%=-Wl,--%)
 
 _LIBS := $(filter -l%,$(LDFLAGS))
 _LIBS := $(patsubst -l%,$(OUTPUTDIR)Libs/lib%.so,$(_LIBS))
 
+ifeq ($(ARCHDIR),native)
+ LIBS := $(patsubst -lc,-lc_acess,$(LIBS))
+ LIBS := $(patsubst -lc++,-lc++_acess,$(LIBS))
+ ifneq ($(BIN),libc_acess.so)
+  LIBS += -lc_acess
+ endif
+endif
+
 OBJ := $(addprefix $(_OBJPREFIX),$(OBJ))
 
 UTESTS := $(patsubst TEST_%.c,%,$(wildcard TEST_*.c))
@@ -36,14 +43,15 @@ else
 V := @
 endif
 
-.PHONY: all clean install postbuild
+.PHONY: all clean install postbuild utest-build utest-run generate_exp
 
 all: _libs $(_BIN) $(_XBIN)
 
 .PHONY: _libs
 
+.PRECIOUS: .no
 
-HEADERS := $(patsubst include_exp/%,../../include/%,$(shell find include_exp/ -name \*.h))
+HEADERS := $(patsubst include_exp/%,../../include/%,$(shell find include_exp/ -name \*.h 2>/dev/null))
 _libs: $(HEADERS)
 
 ../../include/%: include_exp/%
@@ -54,16 +62,13 @@ _libs: $(HEADERS)
 
 utest: utest-build utest-run
 
-generate_exp: $(UTESTS:%=EXP_%.txt)
-	@echo > /dev/null
-
 utest-build: $(UTESTS:%=TEST_%)
 
 utest-run: $(UTESTS:%=runtest-%)
-	@echo > /dev/null
 
-$(UTESTS:%=runtest-%): runtest-%: TEST_% EXP_%.txt
-	./TEST_$* | diff EXP_$*.txt -
+$(UTESTS:%=runtest-%): runtest-%: TEST_%
+	@echo --- [TEST] $*
+	@./TEST_$*
 
 clean:
 	$(RM) $(_BIN) $(_XBIN) $(OBJ) $(_BIN).dsm $(DEPFILES) $(EXTRACLEAN)
@@ -81,23 +86,33 @@ endif
 #	for f in $(INCFILES); do ln -s $f $(ACESSDIR)/include/$f; done
 #endif
 
-$(_BIN): $(OBJ)
+LINK_OBJS = $(PRELINK) $(OBJ)
+$(_BIN): $(CRTI) $(LINK_OBJS) $(CRTN) $(CRT0S)
 	@mkdir -p $(dir $(_BIN))
 	@echo [LD] -o $(BIN) $(OBJ)
-	$V$(LD) $(LDFLAGS) -o $(_BIN) $(OBJ) $(shell $(CC) -print-libgcc-file-name)
-	$V$(DISASM) -D -S $(_BIN) > $(_OBJPREFIX)$(BIN).dsm
+ifneq ($(USE_CXX_LINK),)
+	$V$(CXX) $(LDFLAGS) -o $(_BIN) $(LINK_OBJS) $(LIBS)
+else
+	$V$(CC) $(LDFLAGS) -o $(_BIN) $(LINK_OBJS) $(LIBS)
+endif
+	$V$(DISASM) -C $(_BIN) > $(_OBJPREFIX)$(BIN).dsm
 
-$(_OBJPREFIX)%.o: %.c
+$(_OBJPREFIX)%.o: %.c Makefile
 	@echo [CC] -o $@
 	@mkdir -p $(dir $@)
 	$V$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< -MMD -MP -MT $@ -MF $@.dep
 
-$(_OBJPREFIX)%.o: %.cc
+$(_OBJPREFIX)%.o: %.cc Makefile
 	@echo [CXX] -o $@
 	@mkdir -p $(dir $@)
 	$V$(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ -c $< -MMD -MP -MT $@ -MF $@.dep
 
-$(_OBJPREFIX)%.ao: %.$(ASSUFFIX)
+$(_OBJPREFIX)%.o: %.cpp Makefile
+	@echo [CXX] -o $@
+	@mkdir -p $(dir $@)
+	$V$(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ -c $< -MMD -MP -MT $@ -MF $@.dep
+
+$(_OBJPREFIX)%.ao: %.$(ASSUFFIX) Makefile
 	@echo [AS] -o $@
 	@mkdir -p $(dir $@)
 	$V$(AS) $(ASFLAGS) -o $@ $<
@@ -115,10 +130,14 @@ $(OUTPUTDIR)Libs/%:
 
 obj-native/%.no: %.c
 	@mkdir -p $(dir $@)
-	$(NCC) -c $< -o $@ -Wall -std=gnu99 -MD -MP -MF $@.dep
+	@echo [CC Native] -o $@
+	@$(NCC) -g -c $< -o $@ -Wall -std=gnu99 -MD -MP -MF $@.dep '-D_SysDebug(f,v...)=fprintf(stderr,"DEBUG "f"\n",##v)' -include stdio.h
 
 TEST_%: obj-native/TEST_%.no obj-native/%.no
-	$(NCC) -o $@ $^
+	@echo [CC Native] -o $@
+	@$(NCC) -g -o $@ $^
+
+.PRECIOUS: $(UTESTS:%=obj-native/%.no) $(UTESTS:%=obj-native/TEST_%.no)
 
 -include $(UTESTS:%=obj-native/TEST_%.no.dep)
 -include $(UTESTS:%=obj-native/%.no.dep)
diff --git a/Usermode/Libraries/acess.ld_src/.gitignore b/Usermode/Libraries/acess.ld_src/.gitignore
deleted file mode 100644
index 3932ffb93f0d771d24aaa4039559f032e94801fb..0000000000000000000000000000000000000000
--- a/Usermode/Libraries/acess.ld_src/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-acess_native.ld.h
diff --git a/Usermode/Libraries/acess.ld_src/Makefile b/Usermode/Libraries/acess.ld_src/Makefile
deleted file mode 100644
index 25a5c56538954b3f59139cca857ebf4186fbaac6..0000000000000000000000000000000000000000
--- a/Usermode/Libraries/acess.ld_src/Makefile
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Acess2
-# - Common usermode linker script
-#
-
-
--include ../Makefile.cfg
-
-BIN = $(OUTPUTDIR)Libs/acess.ld
-
-.PHONY: all clean install utest generate_exp
-
-all: $(BIN)
-
-clean:
-	$(RM) $(BIN)
-
-install: $(BIN)
-
-# How does one unit test a linker script?
-utest generate_exp:
-	@echo > /dev/null
-
-$(BIN):	acess_$(ARCHDIR).ld.h
-	@mkdir -p $(dir $(BIN))
-	cpp -nostdinc -U i386 -P -C $< -o $@ -D__LIBDIR=$(OUTPUTDIR)Libs
-
-acess_$(ARCHDIR).ld.h:
-	$(LD) --verbose | awk '{ if( substr($$0,0,5) == "====="){ bPrint = !bPrint; } else { if(bPrint){ print $$0;} } }' | sed 's/SEARCH_DIR\(.*\)/SEARCH_DIR(__LIBDIR)/' > $@
diff --git a/Usermode/Libraries/acess.ld_src/acess_armv6.ld.h b/Usermode/Libraries/acess.ld_src/acess_armv6.ld.h
deleted file mode 100644
index 0ea7f9029a563ffd212c20e14b553a3996436326..0000000000000000000000000000000000000000
--- a/Usermode/Libraries/acess.ld_src/acess_armv6.ld.h
+++ /dev/null
@@ -1,235 +0,0 @@
-/* Script for -z combreloc: combine and sort reloc sections */
-OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",
-	      "elf32-littlearm")
-OUTPUT_ARCH(arm)
-ENTRY(_start)
-SEARCH_DIR(__LIBDIR)
-SECTIONS
-{
-  /* Read-only sections, merged into text segment: */
-  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x8000)); . = SEGMENT_START("text-segment", 0x8000);
-  .interp         : { *(.interp) }
-  .note.gnu.build-id : { *(.note.gnu.build-id) }
-  .hash           : { *(.hash) }
-  .gnu.hash       : { *(.gnu.hash) }
-  .dynsym         : { *(.dynsym) }
-  .dynstr         : { *(.dynstr) }
-  .gnu.version    : { *(.gnu.version) }
-  .gnu.version_d  : { *(.gnu.version_d) }
-  .gnu.version_r  : { *(.gnu.version_r) }
-  .rel.dyn        :
-    {
-      *(.rel.init)
-      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
-      *(.rel.fini)
-      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
-      *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
-      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
-      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
-      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
-      *(.rel.ctors)
-      *(.rel.dtors)
-      *(.rel.got)
-      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
-      PROVIDE_HIDDEN (__rel_iplt_start = .);
-      *(.rel.iplt)
-      PROVIDE_HIDDEN (__rel_iplt_end = .);
-      PROVIDE_HIDDEN (__rela_iplt_start = .);
-      PROVIDE_HIDDEN (__rela_iplt_end = .);
-    }
-  .rela.dyn       :
-    {
-      *(.rela.init)
-      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
-      *(.rela.fini)
-      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
-      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
-      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
-      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
-      *(.rela.ctors)
-      *(.rela.dtors)
-      *(.rela.got)
-      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
-      PROVIDE_HIDDEN (__rel_iplt_start = .);
-      PROVIDE_HIDDEN (__rel_iplt_end = .);
-      PROVIDE_HIDDEN (__rela_iplt_start = .);
-      *(.rela.iplt)
-      PROVIDE_HIDDEN (__rela_iplt_end = .);
-    }
-  .rel.plt        :
-    {
-      *(.rel.plt)
-    }
-  .rela.plt       :
-    {
-      *(.rela.plt)
-    }
-  .init           :
-  {
-    KEEP (*(.init))
-  } =0
-  .plt            : { *(.plt) }
-  .iplt           : { *(.iplt) }
-  .text           :
-  {
-    *(.text.unlikely .text.*_unlikely)
-    *(.text.exit .text.exit.*)
-    *(.text.startup .text.startup.*)
-    *(.text.hot .text.hot.*)
-    *(.text .stub .text.* .gnu.linkonce.t.*)
-    /* .gnu.warning sections are handled specially by elf32.em.  */
-    *(.gnu.warning)
-    *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)
-  } =0
-  .fini           :
-  {
-    KEEP (*(.fini))
-  } =0
-  PROVIDE (__etext = .);
-  PROVIDE (_etext = .);
-  PROVIDE (etext = .);
-  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
-  .rodata1        : { *(.rodata1) }
-  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) }
-   PROVIDE_HIDDEN(__exidx_start = .);
-  .ARM.exidx   : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }
-   PROVIDE_HIDDEN(__exidx_end = .);
-  .eh_frame_hdr : { *(.eh_frame_hdr) }
-  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
-  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
-  /* Adjust the address for the data segment.  We want to adjust up to
-     the same address within the page on the next page up.  */
-  . = ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1));
-  /* Exception handling  */
-  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
-  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
-  /* Thread Local Storage sections  */
-  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
-  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
-  .preinit_array     :
-  {
-    PROVIDE_HIDDEN (__preinit_array_start = .);
-    KEEP (*(.preinit_array))
-    PROVIDE_HIDDEN (__preinit_array_end = .);
-  }
-  .init_array     :
-  {
-    PROVIDE_HIDDEN (__init_array_start = .);
-    KEEP (*(SORT(.init_array.*)))
-    KEEP (*(.init_array))
-    PROVIDE_HIDDEN (__init_array_end = .);
-  }
-  .fini_array     :
-  {
-    PROVIDE_HIDDEN (__fini_array_start = .);
-    KEEP (*(SORT(.fini_array.*)))
-    KEEP (*(.fini_array))
-    PROVIDE_HIDDEN (__fini_array_end = .);
-  }
-  .ctors          :
-  {
-    /* gcc uses crtbegin.o to find the start of
-       the constructors, so we make sure it is
-       first.  Because this is a wildcard, it
-       doesn't matter if the user does not
-       actually link against crtbegin.o; the
-       linker won't look for a file to match a
-       wildcard.  The wildcard also means that it
-       doesn't matter which directory crtbegin.o
-       is in.  */
-    KEEP (*crtbegin.o(.ctors))
-    KEEP (*crtbegin?.o(.ctors))
-    /* We don't want to include the .ctor section from
-       the crtend.o file until after the sorted ctors.
-       The .ctor section from the crtend file contains the
-       end of ctors marker and it must be last */
-    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
-    KEEP (*(SORT(.ctors.*)))
-    KEEP (*(.ctors))
-  }
-  .dtors          :
-  {
-    KEEP (*crtbegin.o(.dtors))
-    KEEP (*crtbegin?.o(.dtors))
-    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
-    KEEP (*(SORT(.dtors.*)))
-    KEEP (*(.dtors))
-  }
-  .jcr            : { KEEP (*(.jcr)) }
-  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
-  .dynamic        : { *(.dynamic) }
-  .got            : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
-  .data           :
-  {
-    __data_start = . ;
-    *(.data .data.* .gnu.linkonce.d.*)
-    SORT(CONSTRUCTORS)
-  }
-  .data1          : { *(.data1) }
-  _edata = .; PROVIDE (edata = .);
-  __bss_start = .;
-  __bss_start__ = .;
-  .bss            :
-  {
-   *(.dynbss)
-   *(.bss .bss.* .gnu.linkonce.b.*)
-   *(COMMON)
-   /* Align here to ensure that the .bss section occupies space up to
-      _end.  Align after .bss to ensure correct alignment even if the
-      .bss section disappears because there are no input sections.
-      FIXME: Why do we need it? When there is no .bss section, we don't
-      pad the .data section.  */
-   . = ALIGN(. != 0 ? 32 / 8 : 1);
-  }
-  _bss_end__ = . ; __bss_end__ = . ;
-  . = ALIGN(32 / 8);
-  . = ALIGN(32 / 8);
-  __end__ = . ;
-  _end = .; PROVIDE (end = .);
-  /* Stabs debugging sections.  */
-  .stab          0 : { *(.stab) }
-  .stabstr       0 : { *(.stabstr) }
-  .stab.excl     0 : { *(.stab.excl) }
-  .stab.exclstr  0 : { *(.stab.exclstr) }
-  .stab.index    0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment       0 : { *(.comment) }
-  /* DWARF debug sections.
-     Symbols in the DWARF debugging sections are relative to the beginning
-     of the section so we begin them at 0.  */
-  /* DWARF 1 */
-  .debug          0 : { *(.debug) }
-  .line           0 : { *(.line) }
-  /* GNU DWARF 1 extensions */
-  .debug_srcinfo  0 : { *(.debug_srcinfo .zdebug_srcinfo) }
-  .debug_sfnames  0 : { *(.debug_sfnames .zdebug_sfnames) }
-  /* DWARF 1.1 and DWARF 2 */
-  .debug_aranges  0 : { *(.debug_aranges .zdebug_aranges) }
-  .debug_pubnames 0 : { *(.debug_pubnames .zdebug_pubnames) }
-  /* DWARF 2 */
-  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.* .zdebug_info) }
-  .debug_abbrev   0 : { *(.debug_abbrev .zdebug_abbrev) }
-  .debug_line     0 : { *(.debug_line .zdebug_line) }
-  .debug_frame    0 : { *(.debug_frame .zdebug_frame) }
-  .debug_str      0 : { *(.debug_str .zdebug_str) }
-  .debug_loc      0 : { *(.debug_loc .zdebug_loc) }
-  .debug_macinfo  0 : { *(.debug_macinfo .zdebug_macinfo) }
-  /* SGI/MIPS DWARF 2 extensions */
-  .debug_weaknames 0 : { *(.debug_weaknames .zdebug_weaknames) }
-  .debug_funcnames 0 : { *(.debug_funcnames .zdebug_funcnames) }
-  .debug_typenames 0 : { *(.debug_typenames .zdebug_typenames) }
-  .debug_varnames  0 : { *(.debug_varnames .zdebug_varnames) }
-  /* DWARF 3 */
-  .debug_pubtypes 0 : { *(.debug_pubtypes .zdebug_pubtypes) }
-  .debug_ranges   0 : { *(.debug_ranges .zdebug_ranges) }
-    .stack         0x80000 :
-  {
-    _stack = .;
-    *(.stack)
-  }
-  .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) }
-  .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
-  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
-}
-
-
diff --git a/Usermode/Libraries/acess.ld_src/acess_armv7.ld.h b/Usermode/Libraries/acess.ld_src/acess_armv7.ld.h
deleted file mode 100644
index 24c4af34ad39afaf9a445f48d99428f98410c470..0000000000000000000000000000000000000000
--- a/Usermode/Libraries/acess.ld_src/acess_armv7.ld.h
+++ /dev/null
@@ -1,236 +0,0 @@
-/* Script for -z combreloc: combine and sort reloc sections */
-OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",
-	      "elf32-littlearm")
-OUTPUT_ARCH(arm)
-ENTRY(start)
-SEARCH_DIR(__LIBDIR)
-INPUT(crt0.o)
-SECTIONS
-{
-  /* Read-only sections, merged into text segment: */
-  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x8000)); . = SEGMENT_START("text-segment", 0x8000) + SIZEOF_HEADERS;
-  .interp         : { *(.interp) }
-  .note.gnu.build-id : { *(.note.gnu.build-id) }
-  .hash           : { *(.hash) }
-  .gnu.hash       : { *(.gnu.hash) }
-  .dynsym         : { *(.dynsym) }
-  .dynstr         : { *(.dynstr) }
-  .gnu.version    : { *(.gnu.version) }
-  .gnu.version_d  : { *(.gnu.version_d) }
-  .gnu.version_r  : { *(.gnu.version_r) }
-  .rel.dyn        :
-    {
-      *(.rel.init)
-      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
-      *(.rel.fini)
-      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
-      *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
-      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
-      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
-      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
-      *(.rel.ctors)
-      *(.rel.dtors)
-      *(.rel.got)
-      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
-      PROVIDE_HIDDEN (__rel_iplt_start = .);
-      *(.rel.iplt)
-      PROVIDE_HIDDEN (__rel_iplt_end = .);
-      PROVIDE_HIDDEN (__rela_iplt_start = .);
-      PROVIDE_HIDDEN (__rela_iplt_end = .);
-    }
-  .rela.dyn       :
-    {
-      *(.rela.init)
-      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
-      *(.rela.fini)
-      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
-      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
-      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
-      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
-      *(.rela.ctors)
-      *(.rela.dtors)
-      *(.rela.got)
-      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
-      PROVIDE_HIDDEN (__rel_iplt_start = .);
-      PROVIDE_HIDDEN (__rel_iplt_end = .);
-      PROVIDE_HIDDEN (__rela_iplt_start = .);
-      *(.rela.iplt)
-      PROVIDE_HIDDEN (__rela_iplt_end = .);
-    }
-  .rel.plt        :
-    {
-      *(.rel.plt)
-    }
-  .rela.plt       :
-    {
-      *(.rela.plt)
-    }
-  .init           :
-  {
-    KEEP (*(.init))
-  } =0
-  .plt            : { *(.plt) }
-  .iplt           : { *(.iplt) }
-  .text           :
-  {
-    *(.text.unlikely .text.*_unlikely)
-    *(.text.exit .text.exit.*)
-    *(.text.startup .text.startup.*)
-    *(.text.hot .text.hot.*)
-    *(.text .stub .text.* .gnu.linkonce.t.*)
-    /* .gnu.warning sections are handled specially by elf32.em.  */
-    *(.gnu.warning)
-    *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)
-  } =0
-  .fini           :
-  {
-    KEEP (*(.fini))
-  } =0
-  PROVIDE (__etext = .);
-  PROVIDE (_etext = .);
-  PROVIDE (etext = .);
-  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
-  .rodata1        : { *(.rodata1) }
-  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) }
-   PROVIDE_HIDDEN (__exidx_start = .);
-  .ARM.exidx   : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }
-   PROVIDE_HIDDEN (__exidx_end = .);
-  .eh_frame_hdr : { *(.eh_frame_hdr) }
-  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
-  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
-  /* Adjust the address for the data segment.  We want to adjust up to
-     the same address within the page on the next page up.  */
-  . = ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1));
-  /* Exception handling  */
-  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
-  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
-  /* Thread Local Storage sections  */
-  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
-  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
-  .preinit_array     :
-  {
-    PROVIDE_HIDDEN (__preinit_array_start = .);
-    KEEP (*(.preinit_array))
-    PROVIDE_HIDDEN (__preinit_array_end = .);
-  }
-  .init_array     :
-  {
-    PROVIDE_HIDDEN (__init_array_start = .);
-    KEEP (*(SORT(.init_array.*)))
-    KEEP (*(.init_array))
-    PROVIDE_HIDDEN (__init_array_end = .);
-  }
-  .fini_array     :
-  {
-    PROVIDE_HIDDEN (__fini_array_start = .);
-    KEEP (*(SORT(.fini_array.*)))
-    KEEP (*(.fini_array))
-    PROVIDE_HIDDEN (__fini_array_end = .);
-  }
-  .ctors          :
-  {
-    /* gcc uses crtbegin.o to find the start of
-       the constructors, so we make sure it is
-       first.  Because this is a wildcard, it
-       doesn't matter if the user does not
-       actually link against crtbegin.o; the
-       linker won't look for a file to match a
-       wildcard.  The wildcard also means that it
-       doesn't matter which directory crtbegin.o
-       is in.  */
-    KEEP (*crtbegin.o(.ctors))
-    KEEP (*crtbegin?.o(.ctors))
-    /* We don't want to include the .ctor section from
-       the crtend.o file until after the sorted ctors.
-       The .ctor section from the crtend file contains the
-       end of ctors marker and it must be last */
-    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
-    KEEP (*(SORT(.ctors.*)))
-    KEEP (*(.ctors))
-  }
-  .dtors          :
-  {
-    KEEP (*crtbegin.o(.dtors))
-    KEEP (*crtbegin?.o(.dtors))
-    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
-    KEEP (*(SORT(.dtors.*)))
-    KEEP (*(.dtors))
-  }
-  .jcr            : { KEEP (*(.jcr)) }
-  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
-  .dynamic        : { *(.dynamic) }
-  .got            : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
-  .data           :
-  {
-    __data_start = . ;
-    *(.data .data.* .gnu.linkonce.d.*)
-    SORT(CONSTRUCTORS)
-  }
-  .data1          : { *(.data1) }
-  _edata = .; PROVIDE (edata = .);
-  __bss_start = .;
-  __bss_start__ = .;
-  .bss            :
-  {
-   *(.dynbss)
-   *(.bss .bss.* .gnu.linkonce.b.*)
-   *(COMMON)
-   /* Align here to ensure that the .bss section occupies space up to
-      _end.  Align after .bss to ensure correct alignment even if the
-      .bss section disappears because there are no input sections.
-      FIXME: Why do we need it? When there is no .bss section, we don't
-      pad the .data section.  */
-   . = ALIGN(. != 0 ? 32 / 8 : 1);
-  }
-  _bss_end__ = . ; __bss_end__ = . ;
-  . = ALIGN(32 / 8);
-  . = ALIGN(32 / 8);
-  __end__ = . ;
-  _end = .; PROVIDE (end = .);
-  /* Stabs debugging sections.  */
-  .stab          0 : { *(.stab) }
-  .stabstr       0 : { *(.stabstr) }
-  .stab.excl     0 : { *(.stab.excl) }
-  .stab.exclstr  0 : { *(.stab.exclstr) }
-  .stab.index    0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment       0 : { *(.comment) }
-  /* DWARF debug sections.
-     Symbols in the DWARF debugging sections are relative to the beginning
-     of the section so we begin them at 0.  */
-  /* DWARF 1 */
-  .debug          0 : { *(.debug) }
-  .line           0 : { *(.line) }
-  /* GNU DWARF 1 extensions */
-  .debug_srcinfo  0 : { *(.debug_srcinfo .zdebug_srcinfo) }
-  .debug_sfnames  0 : { *(.debug_sfnames .zdebug_sfnames) }
-  /* DWARF 1.1 and DWARF 2 */
-  .debug_aranges  0 : { *(.debug_aranges .zdebug_aranges) }
-  .debug_pubnames 0 : { *(.debug_pubnames .zdebug_pubnames) }
-  /* DWARF 2 */
-  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.* .zdebug_info) }
-  .debug_abbrev   0 : { *(.debug_abbrev .zdebug_abbrev) }
-  .debug_line     0 : { *(.debug_line .zdebug_line) }
-  .debug_frame    0 : { *(.debug_frame .zdebug_frame) }
-  .debug_str      0 : { *(.debug_str .zdebug_str) }
-  .debug_loc      0 : { *(.debug_loc .zdebug_loc) }
-  .debug_macinfo  0 : { *(.debug_macinfo .zdebug_macinfo) }
-  /* SGI/MIPS DWARF 2 extensions */
-  .debug_weaknames 0 : { *(.debug_weaknames .zdebug_weaknames) }
-  .debug_funcnames 0 : { *(.debug_funcnames .zdebug_funcnames) }
-  .debug_typenames 0 : { *(.debug_typenames .zdebug_typenames) }
-  .debug_varnames  0 : { *(.debug_varnames .zdebug_varnames) }
-  /* DWARF 3 */
-  .debug_pubtypes 0 : { *(.debug_pubtypes .zdebug_pubtypes) }
-  .debug_ranges   0 : { *(.debug_ranges .zdebug_ranges) }
-    .stack         0x80000 :
-  {
-    _stack = .;
-    *(.stack)
-  }
-  .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) }
-  .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
-  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
-}
-
-
diff --git a/Usermode/Libraries/acess.ld_src/acess_x86.ld.h b/Usermode/Libraries/acess.ld_src/acess_x86.ld.h
deleted file mode 100644
index e81c0a56fb71579aed01db1cab31a368296d8773..0000000000000000000000000000000000000000
--- a/Usermode/Libraries/acess.ld_src/acess_x86.ld.h
+++ /dev/null
@@ -1,199 +0,0 @@
-OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
-OUTPUT_ARCH(i386)
-ENTRY(start)
-SEARCH_DIR(__LIBDIR)
-INPUT(crt0.o)
-SECTIONS
-{
-  /* Read-only sections, merged into text segment: */
-  PROVIDE (__executable_start = 0x08048000); . = 0x08048000 + SIZEOF_HEADERS;
-  .interp         : { *(.interp) }
-  .note.gnu.build-id : { *(.note.gnu.build-id) }
-  .hash           : { *(.hash) }
-  .gnu.hash       : { *(.gnu.hash) }
-  .dynsym         : { *(.dynsym) }
-  .dynstr         : { *(.dynstr) }
-  .gnu.version    : { *(.gnu.version) }
-  .gnu.version_d  : { *(.gnu.version_d) }
-  .gnu.version_r  : { *(.gnu.version_r) }
-  .rel.dyn        :
-    {
-      *(.rel.init)
-      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
-      *(.rel.fini)
-      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
-      *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
-      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
-      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
-      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
-      *(.rel.ctors)
-      *(.rel.dtors)
-      *(.rel.got)
-      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
-    }
-  .rela.dyn       :
-    {
-      *(.rela.init)
-      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
-      *(.rela.fini)
-      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
-      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
-      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
-      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
-      *(.rela.ctors)
-      *(.rela.dtors)
-      *(.rela.got)
-      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
-    }
-  .rel.plt        : { *(.rel.plt) }
-  .rela.plt       : { *(.rela.plt) }
-  .init           :
-  {
-    KEEP (*(.init))
-  } =0x90909090
-  .plt            : { *(.plt) }
-  .text           :
-  {
-    *(.text .stub .text.* .gnu.linkonce.t.*)
-    /* .gnu.warning sections are handled specially by elf32.em.  */
-    *(.gnu.warning)
-  } =0x90909090
-  .fini           :
-  {
-    KEEP (*(.fini))
-  } =0x90909090
-  PROVIDE (__etext = .);
-  PROVIDE (_etext = .);
-  PROVIDE (etext = .);
-  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
-  .rodata1        : { *(.rodata1) }
-  .eh_frame_hdr : { *(.eh_frame_hdr) }
-  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
-  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
-  /* Adjust the address for the data segment.  We want to adjust up to
-     the same address within the page on the next page up.  */
-  . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
-  /* Exception handling  */
-  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
-  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
-  /* Thread Local Storage sections  */
-  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
-  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
-  .preinit_array     :
-  {
-    PROVIDE_HIDDEN (__preinit_array_start = .);
-    KEEP (*(.preinit_array))
-    PROVIDE_HIDDEN (__preinit_array_end = .);
-  }
-  .init_array     :
-  {
-     PROVIDE_HIDDEN (__init_array_start = .);
-     KEEP (*(SORT(.init_array.*)))
-     KEEP (*(.init_array))
-     PROVIDE_HIDDEN (__init_array_end = .);
-  }
-  .fini_array     :
-  {
-    PROVIDE_HIDDEN (__fini_array_start = .);
-    KEEP (*(.fini_array))
-    KEEP (*(SORT(.fini_array.*)))
-    PROVIDE_HIDDEN (__fini_array_end = .);
-  }
-  .ctors          :
-  {
-    /* gcc uses crtbegin.o to find the start of
-       the constructors, so we make sure it is
-       first.  Because this is a wildcard, it
-       doesn't matter if the user does not
-       actually link against crtbegin.o; the
-       linker won't look for a file to match a
-       wildcard.  The wildcard also means that it
-       doesn't matter which directory crtbegin.o
-       is in.  */
-    KEEP (*crtbegin.o(.ctors))
-    KEEP (*crtbegin?.o(.ctors))
-    /* We don't want to include the .ctor section from
-       the crtend.o file until after the sorted ctors.
-       The .ctor section from the crtend file contains the
-       end of ctors marker and it must be last */
-    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
-    KEEP (*(SORT(.ctors.*)))
-    KEEP (*(.ctors))
-  }
-  .dtors          :
-  {
-    KEEP (*crtbegin.o(.dtors))
-    KEEP (*crtbegin?.o(.dtors))
-    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
-    KEEP (*(SORT(.dtors.*)))
-    KEEP (*(.dtors))
-  }
-  .jcr            : { KEEP (*(.jcr)) }
-  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
-  .dynamic        : { *(.dynamic) }
-  .got            : { *(.got) }
-  . = DATA_SEGMENT_RELRO_END (12, .);
-  .got.plt        : { *(.got.plt) }
-  .data           :
-  {
-    *(.data .data.* .gnu.linkonce.d.*)
-    SORT(CONSTRUCTORS)
-  }
-  .data1          : { *(.data1) }
-  _edata = .; PROVIDE (edata = .);
-  __bss_start = .;
-  .bss            :
-  {
-   *(.dynbss)
-   *(.bss .bss.* .gnu.linkonce.b.*)
-   *(COMMON)
-   /* Align here to ensure that the .bss section occupies space up to
-      _end.  Align after .bss to ensure correct alignment even if the
-      .bss section disappears because there are no input sections.
-      FIXME: Why do we need it? When there is no .bss section, we don't
-      pad the .data section.  */
-   . = ALIGN(. != 0 ? 32 / 8 : 1);
-  }
-  . = ALIGN(32 / 8);
-  . = ALIGN(32 / 8);
-  _end = .; PROVIDE (end = .);
-  . = DATA_SEGMENT_END (.);
-  /* Stabs debugging sections.  */
-  .stab          0 : { *(.stab) }
-  .stabstr       0 : { *(.stabstr) }
-  .stab.excl     0 : { *(.stab.excl) }
-  .stab.exclstr  0 : { *(.stab.exclstr) }
-  .stab.index    0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment       0 : { *(.comment) }
-  /* DWARF debug sections.
-     Symbols in the DWARF debugging sections are relative to the beginning
-     of the section so we begin them at 0.  */
-  /* DWARF 1 */
-  .debug          0 : { *(.debug) }
-  .line           0 : { *(.line) }
-  /* GNU DWARF 1 extensions */
-  .debug_srcinfo  0 : { *(.debug_srcinfo) }
-  .debug_sfnames  0 : { *(.debug_sfnames) }
-  /* DWARF 1.1 and DWARF 2 */
-  .debug_aranges  0 : { *(.debug_aranges) }
-  .debug_pubnames 0 : { *(.debug_pubnames) }
-  /* DWARF 2 */
-  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
-  .debug_abbrev   0 : { *(.debug_abbrev) }
-  .debug_line     0 : { *(.debug_line) }
-  .debug_frame    0 : { *(.debug_frame) }
-  .debug_str      0 : { *(.debug_str) }
-  .debug_loc      0 : { *(.debug_loc) }
-  .debug_macinfo  0 : { *(.debug_macinfo) }
-  /* SGI/MIPS DWARF 2 extensions */
-  .debug_weaknames 0 : { *(.debug_weaknames) }
-  .debug_funcnames 0 : { *(.debug_funcnames) }
-  .debug_typenames 0 : { *(.debug_typenames) }
-  .debug_varnames  0 : { *(.debug_varnames) }
-  /* DWARF 3 */
-  .debug_pubtypes 0 : { *(.debug_pubtypes) }
-  .debug_ranges   0 : { *(.debug_ranges) }
-  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
-  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) }
-}
diff --git a/Usermode/Libraries/acess.ld_src/acess_x86_64.ld.h b/Usermode/Libraries/acess.ld_src/acess_x86_64.ld.h
deleted file mode 100644
index 469e4afd2df4235a6cf0078e7015ba1cc647f5e2..0000000000000000000000000000000000000000
--- a/Usermode/Libraries/acess.ld_src/acess_x86_64.ld.h
+++ /dev/null
@@ -1,199 +0,0 @@
-OUTPUT_FORMAT("elf64-x86-64")
-/* OUTPUT_ARCH(x86_64) */
-ENTRY(start)
-SEARCH_DIR(__LIBDIR)
-INPUT(crt0.o)
-SECTIONS
-{
-  /* Read-only sections, merged into text segment: */
-  PROVIDE (__executable_start = 0x00400000); . = 0x00400000 + SIZEOF_HEADERS;
-  .interp         : { *(.interp) }
-  .note.gnu.build-id : { *(.note.gnu.build-id) }
-  .hash           : { *(.hash) }
-  .gnu.hash       : { *(.gnu.hash) }
-  .dynsym         : { *(.dynsym) }
-  .dynstr         : { *(.dynstr) }
-  .gnu.version    : { *(.gnu.version) }
-  .gnu.version_d  : { *(.gnu.version_d) }
-  .gnu.version_r  : { *(.gnu.version_r) }
-  .rel.dyn        :
-    {
-      *(.rel.init)
-      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
-      *(.rel.fini)
-      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
-      *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
-      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
-      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
-      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
-      *(.rel.ctors)
-      *(.rel.dtors)
-      *(.rel.got)
-      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
-    }
-  .rela.dyn       :
-    {
-      *(.rela.init)
-      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
-      *(.rela.fini)
-      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
-      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
-      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
-      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
-      *(.rela.ctors)
-      *(.rela.dtors)
-      *(.rela.got)
-      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
-    }
-  .rel.plt        : { *(.rel.plt) }
-  .rela.plt       : { *(.rela.plt) }
-  .init           :
-  {
-    KEEP (*(.init))
-  } =0x90909090
-  .plt            : { *(.plt) }
-  .text           :
-  {
-    *(.text .stub .text.* .gnu.linkonce.t.*)
-    /* .gnu.warning sections are handled specially by elf32.em.  */
-    *(.gnu.warning)
-  } =0x90909090
-  .fini           :
-  {
-    KEEP (*(.fini))
-  } =0x90909090
-  PROVIDE (__etext = .);
-  PROVIDE (_etext = .);
-  PROVIDE (etext = .);
-  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
-  .rodata1        : { *(.rodata1) }
-  .eh_frame_hdr : { *(.eh_frame_hdr) }
-  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
-  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
-  /* Adjust the address for the data segment.  We want to adjust up to
-     the same address within the page on the next page up.  */
-  . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
-  /* Exception handling  */
-  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
-  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
-  /* Thread Local Storage sections  */
-  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
-  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
-  .preinit_array     :
-  {
-    PROVIDE_HIDDEN (__preinit_array_start = .);
-    KEEP (*(.preinit_array))
-    PROVIDE_HIDDEN (__preinit_array_end = .);
-  }
-  .init_array     :
-  {
-     PROVIDE_HIDDEN (__init_array_start = .);
-     KEEP (*(SORT(.init_array.*)))
-     KEEP (*(.init_array))
-     PROVIDE_HIDDEN (__init_array_end = .);
-  }
-  .fini_array     :
-  {
-    PROVIDE_HIDDEN (__fini_array_start = .);
-    KEEP (*(.fini_array))
-    KEEP (*(SORT(.fini_array.*)))
-    PROVIDE_HIDDEN (__fini_array_end = .);
-  }
-  .ctors          :
-  {
-    /* gcc uses crtbegin.o to find the start of
-       the constructors, so we make sure it is
-       first.  Because this is a wildcard, it
-       doesn't matter if the user does not
-       actually link against crtbegin.o; the
-       linker won't look for a file to match a
-       wildcard.  The wildcard also means that it
-       doesn't matter which directory crtbegin.o
-       is in.  */
-    KEEP (*crtbegin.o(.ctors))
-    KEEP (*crtbegin?.o(.ctors))
-    /* We don't want to include the .ctor section from
-       the crtend.o file until after the sorted ctors.
-       The .ctor section from the crtend file contains the
-       end of ctors marker and it must be last */
-    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
-    KEEP (*(SORT(.ctors.*)))
-    KEEP (*(.ctors))
-  }
-  .dtors          :
-  {
-    KEEP (*crtbegin.o(.dtors))
-    KEEP (*crtbegin?.o(.dtors))
-    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
-    KEEP (*(SORT(.dtors.*)))
-    KEEP (*(.dtors))
-  }
-  .jcr            : { KEEP (*(.jcr)) }
-  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
-  .dynamic        : { *(.dynamic) }
-  .got            : { *(.got) }
-  . = DATA_SEGMENT_RELRO_END (12, .);
-  .got.plt        : { *(.got.plt) }
-  .data           :
-  {
-    *(.data .data.* .gnu.linkonce.d.*)
-    SORT(CONSTRUCTORS)
-  }
-  .data1          : { *(.data1) }
-  _edata = .; PROVIDE (edata = .);
-  __bss_start = .;
-  .bss            :
-  {
-   *(.dynbss)
-   *(.bss .bss.* .gnu.linkonce.b.*)
-   *(COMMON)
-   /* Align here to ensure that the .bss section occupies space up to
-      _end.  Align after .bss to ensure correct alignment even if the
-      .bss section disappears because there are no input sections.
-      FIXME: Why do we need it? When there is no .bss section, we don't
-      pad the .data section.  */
-   . = ALIGN(. != 0 ? 32 / 8 : 1);
-  }
-  . = ALIGN(32 / 8);
-  . = ALIGN(32 / 8);
-  _end = .; PROVIDE (end = .);
-  . = DATA_SEGMENT_END (.);
-  /* Stabs debugging sections.  */
-  .stab          0 : { *(.stab) }
-  .stabstr       0 : { *(.stabstr) }
-  .stab.excl     0 : { *(.stab.excl) }
-  .stab.exclstr  0 : { *(.stab.exclstr) }
-  .stab.index    0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment       0 : { *(.comment) }
-  /* DWARF debug sections.
-     Symbols in the DWARF debugging sections are relative to the beginning
-     of the section so we begin them at 0.  */
-  /* DWARF 1 */
-  .debug          0 : { *(.debug) }
-  .line           0 : { *(.line) }
-  /* GNU DWARF 1 extensions */
-  .debug_srcinfo  0 : { *(.debug_srcinfo) }
-  .debug_sfnames  0 : { *(.debug_sfnames) }
-  /* DWARF 1.1 and DWARF 2 */
-  .debug_aranges  0 : { *(.debug_aranges) }
-  .debug_pubnames 0 : { *(.debug_pubnames) }
-  /* DWARF 2 */
-  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
-  .debug_abbrev   0 : { *(.debug_abbrev) }
-  .debug_line     0 : { *(.debug_line) }
-  .debug_frame    0 : { *(.debug_frame) }
-  .debug_str      0 : { *(.debug_str) }
-  .debug_loc      0 : { *(.debug_loc) }
-  .debug_macinfo  0 : { *(.debug_macinfo) }
-  /* SGI/MIPS DWARF 2 extensions */
-  .debug_weaknames 0 : { *(.debug_weaknames) }
-  .debug_funcnames 0 : { *(.debug_funcnames) }
-  .debug_typenames 0 : { *(.debug_typenames) }
-  .debug_varnames  0 : { *(.debug_varnames) }
-  /* DWARF 3 */
-  .debug_pubtypes 0 : { *(.debug_pubtypes) }
-  .debug_ranges   0 : { *(.debug_ranges) }
-  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
-  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) }
-}
diff --git a/Usermode/Libraries/acess.ld_src/rules.mk b/Usermode/Libraries/acess.ld_src/rules.mk
deleted file mode 100644
index f2f3de02016663fd0100e454b4850f2df7091b8a..0000000000000000000000000000000000000000
--- a/Usermode/Libraries/acess.ld_src/rules.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-include $(BASE)header.mk
-
-# Variables
-BIN = $(OUTPUTDIR)Libs/acess.ld
-
-# Rules
-.PHONY: all-$(DIR)
-
-all-$(DIR): $(BIN)
-clean-$(DIR):
-	$(RM) $(BIN)
-
-$(BIN):	$(DIR)/acess_$(ARCHDIR).ld.h
-	@mkdir -p $(dir $(BIN))
-	cpp -nostdinc -U i386 -P -C $< -o $@ -D__LIBDIR=$(OUTPUTDIR)Libs
-
-$(DIR)/acess_$(ARCHDIR).ld.h:
-	$(LD) --verbose | awk '{ if( substr($$0,0,5) == "====="){ bPrint = !bPrint; } else { if(bPrint){ print $$0;} } }' | sed 's/SEARCH_DIR\(.*\)/SEARCH_DIR(__LIBDIR)/' > $@
-
-include $(BASE)footer.mk
diff --git a/Usermode/Libraries/crt0.o_src/Makefile b/Usermode/Libraries/crt0.o_src/Makefile
index 7ddc5f413966bc7f3ab61231729a46e3842b7993..29bdb0eee225035457c6ace03cf58f4c8e049d7f 100644
--- a/Usermode/Libraries/crt0.o_src/Makefile
+++ b/Usermode/Libraries/crt0.o_src/Makefile
@@ -4,9 +4,11 @@
 
 -include ../Makefile.cfg
 
-BIN = $(OUTPUTDIR)Libs/crt0.o $(OUTPUTDIR)Libs/crtbegin.o $(OUTPUTDIR)Libs/crtend.o
+BIN = $(OUTPUTDIR)Libs/crt0.o $(OUTPUTDIR)Libs/crt0S.o $(OUTPUTDIR)Libs/crti.o $(OUTPUTDIR)Libs/crtn.o
 
-.PHONY: all clean install utest generate_exp
+CFLAGS := -std=c99
+
+.PHONY: all clean install utest utest-build generate_exp
 
 all: $(BIN)
 
@@ -16,22 +18,16 @@ clean:
 	$(RM) $(BIN)
 
 # Disabled unit tests
-utest generate_exp:
+utest generate_exp utest-build utest-run:
 	@echo > /dev/null
 
-$(OUTPUTDIR)Libs/%.o: %.c
+$(OUTPUTDIR)Libs/%S.o: %S.c Makefile
 	@mkdir -p $(dir $@)
-	$(CC) -c $< -o $@
-
-#$(OUTPUTDIR)Libs/crt0.o: obj-$(ARCH)/crt0_asm.o obj-$(ARCH)/crt0_c.o
-#	@mkdir -p $(dir $@)
-#	$(LD) -r -o $@ $?
-
-#obj-$(ARCH)/crt0_asm.o: crt0.$(ARCHDIR).$(ASSUFFIX)
-#	@mkdir -p $(dir $@)
-#	$(AS) $(ASFLAGS) $< -o $@
-
-#obj-$(ARCH)/crt0_c.o: crt0.c
-#	@mkdir -p $(dir $@)
-#	$(CC) -c $< -o $@
+	$(CC) $(CFLAGS) -c $< -o $@ -fPIC
+$(OUTPUTDIR)Libs/%.o: %.c Makefile
+	@mkdir -p $(dir $@)
+	$(CC) $(CFLAGS) -c $< -o $@
+$(OUTPUTDIR)Libs/%.o: $(ARCHDIR)-%.S
+	@mkdir -p $(dir $@)
+	$(CC) $(CFLAGS) -c $< -o $@
 
diff --git a/Usermode/Libraries/crt0.o_src/armv7-crti.S b/Usermode/Libraries/crt0.o_src/armv7-crti.S
new file mode 100644
index 0000000000000000000000000000000000000000..310cd6abad9f81bc48a20c8d93491825c20f33f2
--- /dev/null
+++ b/Usermode/Libraries/crt0.o_src/armv7-crti.S
@@ -0,0 +1,31 @@
+.section .init
+.global _init
+.type _init, function
+_init:
+#ifdef __thumb__
+	.thumb
+	
+	push	{r3, r4, r5, r6, r7, lr}
+#else
+	.arm
+	mov	ip, sp
+	stmdb	sp!, {r3, r4, r5, r6, r7, r8, r9, sl, fp, ip, lr, pc}
+	sub	fp, ip, #4
+#endif
+	/* gcc will nicely put the contents of crtbegin.o's .init section here. */
+
+.section .fini
+.global _fini
+.type _fini, function
+_fini:
+#ifdef __thumb__
+	.thumb
+	
+	push	{r3, r4, r5, r6, r7, lr}
+#else
+	.arm
+	mov	ip, sp
+	stmdb	sp!, {r3, r4, r5, r6, r7, r8, r9, sl, fp, ip, lr, pc}
+	sub	fp, ip, #4
+#endif
+	/* gcc will nicely put the contents of crtbegin.o's .fini section here. */
diff --git a/Usermode/Libraries/crt0.o_src/armv7-crtn.S b/Usermode/Libraries/crt0.o_src/armv7-crtn.S
new file mode 100644
index 0000000000000000000000000000000000000000..1c3e1113e82be2b18a637484f8bda441e1a9e67f
--- /dev/null
+++ b/Usermode/Libraries/crt0.o_src/armv7-crtn.S
@@ -0,0 +1,41 @@
+.section .init
+	/* gcc will nicely put the contents of crtend.o's .init section here. */
+#ifdef __thumb__
+	.thumb
+	
+	pop	{r3, r4, r5, r6, r7}
+	pop	{r3}
+	mov	lr, r3
+#else
+	.arm
+	
+	sub	sp, fp, #40
+	ldmfd	sp, {r4, r5, r6, r7, r8, r9, sl, fp, sp, lr}
+#endif
+	
+#if defined __THUMB_INTERWORK__ || defined __thumb__
+	bx	lr
+#else
+	mov	pc, lr
+#endif
+
+.section .fini
+	/* gcc will nicely put the contents of crtend.o's .fini section here. */
+#ifdef __thumb__
+	.thumb
+	
+	pop	{r3, r4, r5, r6, r7}
+	pop	{r3}
+	mov	lr, r3
+#else
+	.arm
+	
+	sub	sp, fp, #40
+	ldmfd	sp, {r4, r5, r6, r7, r8, r9, sl, fp, sp, lr}
+#endif
+	
+#if defined __THUMB_INTERWORK__ || defined __thumb__
+	bx	lr
+#else
+	mov	pc, lr
+#endif
diff --git a/Usermode/Libraries/crt0.o_src/crt0.c b/Usermode/Libraries/crt0.o_src/crt0.c
index 8363aea9ae889bcdf02e098e47123f77fc06f14d..3cdbe119df16cb2fe37bd203fc2a95ec876f442e 100644
--- a/Usermode/Libraries/crt0.o_src/crt0.c
+++ b/Usermode/Libraries/crt0.o_src/crt0.c
@@ -9,7 +9,8 @@ typedef	void	(*constructor_t)(void);
 constructor_t	_crtbegin_ctors[0] __attribute__((section(".ctors")));
 
 exithandler_t	_crt0_exit_handler;
-//extern constructor_t	_crtbegin_ctors[];
+extern void	_init(void);
+extern void	_fini(void);
 extern void	_exit(int status) __attribute__((noreturn));
 extern int	main(int argc, char *argv[], char **envp);
 
@@ -17,16 +18,16 @@ void _start(int argc, char *argv[], char **envp) __attribute__ ((alias("start"))
 
 void start(int argc, char *argv[], char **envp)
 {
-	 int	i;
-	 int	rv;
-	
-	for( i = 0; _crtbegin_ctors[i]; i ++ )
+	// TODO: isn't this handled by _init?
+	for( int i = 0; _crtbegin_ctors[i]; i ++ )
 		_crtbegin_ctors[i]();
+	
+	_init();
 
-	rv = main(argc, argv, envp);
+	int rv = main(argc, argv, envp);
 	
 	if( _crt0_exit_handler )
 		_crt0_exit_handler();
-
+	_fini();
 	_exit(rv);
 }
diff --git a/Usermode/Libraries/crt0.o_src/crt0S.c b/Usermode/Libraries/crt0.o_src/crt0S.c
new file mode 100644
index 0000000000000000000000000000000000000000..1f35ce4519f9d374c4e4ab5e83d004fef9f63f9d
--- /dev/null
+++ b/Usermode/Libraries/crt0.o_src/crt0S.c
@@ -0,0 +1,23 @@
+/*
+ * Acess2
+ * - CRT0 Shared library version
+ */
+
+typedef	void	(*exithandler_t)(void);
+typedef	void	(*constructor_t)(void);
+
+extern void	_SysDebug(const char *, ...);
+extern void	_init(void);
+extern void	_fini(void);
+extern int	SoMain(void *Base, int argc, char *argv[], char **envp) __attribute__((weak));
+
+int SoStart(void *Base, int argc, char *argv[], char **envp)
+{
+	//_SysDebug("SoStart(%p,%i,%p)", Base, argc, argv);
+	_init();
+
+	if( SoMain )
+		return SoMain(Base, argc, argv, envp);
+	else
+		return 0;
+}
diff --git a/Usermode/Libraries/crt0.o_src/native-crti.S b/Usermode/Libraries/crt0.o_src/native-crti.S
new file mode 100644
index 0000000000000000000000000000000000000000..01c8127f0dbf3aeaf528cc437e496789a03ef104
--- /dev/null
+++ b/Usermode/Libraries/crt0.o_src/native-crti.S
@@ -0,0 +1,11 @@
+.section .init
+.global _init
+.type _init, @function
+_init:
+	/* gcc will nicely put the contents of crtbegin.o's .init section here. */
+
+.section .fini
+.global _fini
+.type _fini, @function
+_fini:
+	/* gcc will nicely put the contents of crtbegin.o's .fini section here. */
diff --git a/Usermode/Libraries/crt0.o_src/native-crtn.S b/Usermode/Libraries/crt0.o_src/native-crtn.S
new file mode 100644
index 0000000000000000000000000000000000000000..f884c09d9a69e06a0d43cc0515370b0bdb4be520
--- /dev/null
+++ b/Usermode/Libraries/crt0.o_src/native-crtn.S
@@ -0,0 +1,7 @@
+.section .init
+	/* gcc will nicely put the contents of crtend.o's .init section here. */
+	ret
+
+.section .fini
+	ret
+	/* gcc will nicely put the contents of crtend.o's .fini section here. */
diff --git a/Usermode/Libraries/crt0.o_src/x86-crti.S b/Usermode/Libraries/crt0.o_src/x86-crti.S
new file mode 100644
index 0000000000000000000000000000000000000000..cebea3e9007fd8d0a8ade3044c3906a373fec50a
--- /dev/null
+++ b/Usermode/Libraries/crt0.o_src/x86-crti.S
@@ -0,0 +1,15 @@
+.section .init
+.global _init
+.type _init, @function
+_init:
+	push %ebp
+	movl %esp, %ebp
+	/* gcc will nicely put the contents of crtbegin.o's .init section here. */
+
+.section .fini
+.global _fini
+.type _fini, @function
+_fini:
+	push %ebp
+	movl %esp, %ebp
+	/* gcc will nicely put the contents of crtbegin.o's .fini section here. */
diff --git a/Usermode/Libraries/crt0.o_src/x86-crtn.S b/Usermode/Libraries/crt0.o_src/x86-crtn.S
new file mode 100644
index 0000000000000000000000000000000000000000..33957bfe1686086de0d8978421eed3c8245fef9e
--- /dev/null
+++ b/Usermode/Libraries/crt0.o_src/x86-crtn.S
@@ -0,0 +1,9 @@
+.section .init
+	/* gcc will nicely put the contents of crtend.o's .init section here. */
+	popl %ebp
+	ret
+
+.section .fini
+	/* gcc will nicely put the contents of crtend.o's .fini section here. */
+	popl %ebp
+	ret
diff --git a/Usermode/Libraries/crt0.o_src/x86_64-crti.S b/Usermode/Libraries/crt0.o_src/x86_64-crti.S
new file mode 100644
index 0000000000000000000000000000000000000000..fb7dad336830a1d34ea825053de605ae62ea07b8
--- /dev/null
+++ b/Usermode/Libraries/crt0.o_src/x86_64-crti.S
@@ -0,0 +1,15 @@
+.section .init
+.global _init
+.type _init, @function
+_init:
+	push %rbp
+	mov %rsp, %rbp
+	/* gcc will nicely put the contents of crtbegin.o's .init section here. */
+
+.section .fini
+.global _fini
+.type _fini, @function
+_fini:
+	push %rbp
+	mov %rsp, %rbp
+	/* gcc will nicely put the contents of crtbegin.o's .fini section here. */
diff --git a/Usermode/Libraries/crt0.o_src/x86_64-crtn.S b/Usermode/Libraries/crt0.o_src/x86_64-crtn.S
new file mode 100644
index 0000000000000000000000000000000000000000..ec78f0b3cb6dea8f033a9656c255ada797e8f762
--- /dev/null
+++ b/Usermode/Libraries/crt0.o_src/x86_64-crtn.S
@@ -0,0 +1,9 @@
+.section .init
+	/* gcc will nicely put the contents of crtend.o's .init section here. */
+	pop %rbp
+	ret
+
+.section .fini
+	/* gcc will nicely put the contents of crtend.o's .fini section here. */
+	pop %rbp
+	ret
diff --git a/Usermode/Libraries/ld-acess.so_src/Makefile b/Usermode/Libraries/ld-acess.so_src/Makefile
index 4dc881c09ca2ed4ce5c8e03bac892cb27d49fb91..d6691e401c731185a9ba6803caa54582e7021689 100644
--- a/Usermode/Libraries/ld-acess.so_src/Makefile
+++ b/Usermode/Libraries/ld-acess.so_src/Makefile
@@ -11,10 +11,12 @@ EXTRABIN := libld-acess.so
 EXTRACLEAN = $(_OBJPREFIX)_stublib.o
 INCFILES := sys/sys.h
 
-CFLAGS   = -g -Wall -fno-builtin -fno-stack-protector -fPIC -std=c99
+#CPPFLAGS += -D DISABLE_ELF64
+CFLAGS   = -g -Wall -fno-builtin -fno-stack-protector -fPIC -std=c99 -ffreestanding
 # -fno-leading-underscore
 CFLAGS  += $(CPPFLAGS)
-LDFLAGS  = -g -T arch/$(ARCHDIR).ld -Map map.txt --export-dynamic
+LDFLAGS  = -ffreestanding -nostdlib -g -Wl,-T,arch/$(ARCHDIR).ld -Map map.txt --export-dynamic -x
+LIBS := $(LIBGCC_PATH)
 
 ifeq ($(ARCH),native)
 XBIN := $(addprefix $(OUTPUTDIR)Libs/,$(EXTRABIN)) 
@@ -31,15 +33,15 @@ include ../Makefile.tpl
 # create libld-acess.so
 $(_XBIN): $(_OBJPREFIX)_stublib.o
 	@echo [LD] -o -shared libld-acess.so
-	$(LD) -shared -o $@ $<
+	$V$(LD) -shared -o $@ $<
 #	@$(LD) $(LDFLAGS) -o $@ $(OBJ)
 
 
 # Override .ao to look in the object prefix for the source
-$(_OBJPREFIX)arch/$(ARCHDIR).ao_: $(_OBJPREFIX)arch/$(ARCHDIR).$(ASSUFFIX)
+$(_OBJPREFIX)arch/$(ARCHDIR).ao_: $(_OBJPREFIX)arch/$(ARCHDIR).$(ASSUFFIX) Makefile
 	@echo [AS] -o $@
 	@mkdir -p $(dir $@)
-	@$(AS) $(ASFLAGS) -o $@ $<
+	$V$(AS) $(ASFLAGS) -o $@ $<
 
 #.PRECIOUS: $(OBJ:%.ao=%.asm)
 
@@ -47,7 +49,7 @@ $(_OBJPREFIX)arch/$(ARCHDIR).ao_: $(_OBJPREFIX)arch/$(ARCHDIR).$(ASSUFFIX)
 $(_OBJPREFIX)arch/$(ARCHDIR).$(ASSUFFIX): arch/$(ARCHDIR).$(ASSUFFIX).h arch/syscalls.s.h
 	@echo [CPP] -o $@
 	@mkdir -p $(dir $@)
-	@$(CPP) $(CPPFLAGS) -P -D__ASSEMBLER__ $< -o $@
+	$V$(CPP) $(CPPFLAGS) -P -D__ASSEMBLER__ $< -o $@
 
 $(_OBJPREFIX)arch/$(ARCHDIR).$(ASSUFFIX): $(ACESSDIR)/KernelLand/Kernel/include/syscalls.h
 
diff --git a/Usermode/Libraries/ld-acess.so_src/arch/armv7.S.h b/Usermode/Libraries/ld-acess.so_src/arch/armv7.S.h
index 351c84147c43f2867c49b632548e5335b0557a13..7e604f1d85bd192374d8332ad56673b38e184e9b 100644
--- a/Usermode/Libraries/ld-acess.so_src/arch/armv7.S.h
+++ b/Usermode/Libraries/ld-acess.so_src/arch/armv7.S.h
@@ -16,7 +16,7 @@ _start:
 	
 	b _exit
 
-@ Stupid GCC
+// Stupid GCC
 .globl __ucmpdi2
 __ucmpdi2:
 	cmp r0, r2
@@ -32,8 +32,8 @@ __ucmpdi2:
 	mov r0, #1
 	mov pc, lr
 
-@ Well, can't blame it
-@ - Clear the instruction cache
+//@ Well, can't blame it
+// - Clear the instruction cache
 .globl __clear_cache
 __clear_cache:
 	svc #0x1001
diff --git a/Usermode/Libraries/ld-acess.so_src/arch/syscalls.s.h b/Usermode/Libraries/ld-acess.so_src/arch/syscalls.s.h
index 7f0acb83528533f153b728acd4ad180222ffaf4a..1b9c3e82123e1fd05ef163e5bedf75b587b47a58 100644
--- a/Usermode/Libraries/ld-acess.so_src/arch/syscalls.s.h
+++ b/Usermode/Libraries/ld-acess.so_src/arch/syscalls.s.h
@@ -37,7 +37,11 @@ SYSCALL1(_SysSetFaultHandler, SYS_SETFAULTHANDLER)
 
 SYSCALL1(_SysLoadModule, SYS_LOADMOD)
 
-SYSCALL6(_SysDebug, 0x100)
+SYSCALL6(_ZN4_sys5debugEPKcz, SYS_DEBUGF)
+SYSCALL6(_SysDebug, SYS_DEBUGF)
+//SYSCALL3(_SysDebugS, SYS_DEBUGS)
+SYSCALL3(_SysDebugHex, SYS_DEBUGHEX)
+
 SYSCALL1(_SysGetPhys, SYS_GETPHYS)	// uint64_t _SysGetPhys(uint addr)
 SYSCALL1(_SysAllocate, SYS_ALLOCATE)	// uint64_t _SysAllocate(uint addr)
 SYSCALL3(_SysSetMemFlags, SYS_SETFLAGS)	// uint32_t SysSetMemFlags(uint addr, uint flags, uint mask)
@@ -49,7 +53,10 @@ SYSCALL2(_SysCopyFD, SYS_COPYFD)	// int, int
 SYSCALL3(_SysFDFlags, SYS_FDCTL)	// int, int, int
 SYSCALL1(_SysClose, SYS_CLOSE)	// int
 SYSCALL3(_SysRead, SYS_READ)	// int, uint, void*
+SYSCALL5(_SysReadAt, SYS_READAT)	// int, uint, uint64, void*
 SYSCALL3(_SysWrite, SYS_WRITE)	// int, uint, void*
+SYSCALL5(_SysWriteAt, SYS_WRITEAT)	// int, uint, uint64, void*
+SYSCALL3(_SysTruncate, SYS_TRUNCATE)	// int, uint64
 SYSCALL4(_SysSeek, SYS_SEEK)	// int, uint64_t, int
 SYSCALL1(_SysTell, SYS_TELL)	// int
 SYSCALL3(_SysFInfo, SYS_FINFO)	// int, void*, int
@@ -63,3 +70,9 @@ SYSCALL6(_SysSelect, SYS_SELECT)	// int, fd_set*, fd_set*, fd_set*, tTime*, uint
 SYSCALL1(_SysMkDir, SYS_MKDIR)	// const char*
 SYSCALL1(_SysUnlink, SYS_UNLINK)	// const char*
 
+SYSCALL6(_SysMMap, SYS_MMAP)
+SYSCALL2(_SysMUnMap, SYS_MUNMAP)
+
+SYSCALL1(_SysMarshalFD, SYS_MARSHALFD)
+SYSCALL2(_SysUnMarshalFD, SYS_UNMARSHALFD)
+
diff --git a/Usermode/Libraries/ld-acess.so_src/arch/x86_64.asm.h b/Usermode/Libraries/ld-acess.so_src/arch/x86_64.asm.h
index 350585a0136f4e13493a9d8f44a2b15d37488052..b80e092ba2d2ec2df8d698668d22f91658be1fb5 100644
--- a/Usermode/Libraries/ld-acess.so_src/arch/x86_64.asm.h
+++ b/Usermode/Libraries/ld-acess.so_src/arch/x86_64.asm.h
@@ -56,9 +56,11 @@ _errno:	dw	0	; Placed in .text, to allow use of relative addressing
 [global %1:func]
 %1:
 	push rbx
+	push rbp
 	mov eax, %2
 	SYSCALL_OP
 	mov [DWORD rel _errno], ebx
+	pop rbp
 	pop rbx
 	ret
 %endmacro
diff --git a/Usermode/Libraries/ld-acess.so_src/common.h b/Usermode/Libraries/ld-acess.so_src/common.h
index ac07f9c7365f2f0bd5d829c1461f132d6aed14dc..e0620c264da9224f642b9afaa818d9375091f304 100644
--- a/Usermode/Libraries/ld-acess.so_src/common.h
+++ b/Usermode/Libraries/ld-acess.so_src/common.h
@@ -41,7 +41,7 @@ extern void	*DoRelocate(void *Base, char **envp, const char *Filename);
 // === Library/Symbol Manipulation ==
 extern void	*LoadLibrary(const char *Filename, const char *SearchDir, char **envp);
 extern void	AddLoaded(const char *File, void *base);
-extern int	GetSymbol(const char *Name, void **Value, size_t *size);
+extern int	GetSymbol(const char *Name, void **Value, size_t *size, void *IgnoreBase);
 extern int	GetSymbolFromBase(void *base, const char *name, void **ret, size_t *size);
 
 // === Library Functions ===
diff --git a/Usermode/Libraries/ld-acess.so_src/elf.c b/Usermode/Libraries/ld-acess.so_src/elf.c
index 71e9a92baf9d0e125df9363d575338cac6c8f217..fa2e3f44da6ad793ccc9d914cb29469a2c85935d 100644
--- a/Usermode/Libraries/ld-acess.so_src/elf.c
+++ b/Usermode/Libraries/ld-acess.so_src/elf.c
@@ -4,8 +4,10 @@
  *
  * elf.c
  * - ELF32/ELF64 relocation
+ *
+ * TODO: Have GetSymbol() return a symbol "strength" on success. Allows STB_WEAK to be overriden by STB_GLOBAL
  */
-#ifndef DEBUG	// This code is #include'd from the kernel, so DEBUG may already be defined
+#ifndef KERNEL_VERSION
 # define DEBUG	0
 #endif
 
@@ -15,6 +17,7 @@
 
 #include "common.h"
 #include <stdint.h>
+#include <stdbool.h>
 #ifndef assert
 # include <assert.h>
 #endif
@@ -22,11 +25,16 @@
 #include "elf64.h"
 
 #if DEBUG
-# define	DEBUGS(v...)	SysDebug("ld-acess - " v)
+# define DEBUG_OUT(...)	SysDebug(__VA_ARGS__)
 #else
-# define	DEBUGS(...)	
+# define DEBUG_OUT(...)	do{}while(0)	//((void)(__VA_ARGS__))
 #endif
 
+#define WARNING(f,...)	SysDebug("WARN: "f ,## __VA_ARGS__)	// Malformed file
+#define NOTICE(f,...)	SysDebug("NOTICE: "f ,## __VA_ARGS__)	// Missing relocation
+//#define TRACE(f,...)	DEBUG_OUT("TRACE:%s:%i "f, __func__, __LINE__ ,## __VA_ARGS__)	// Debugging trace
+#define TRACE(f,...)	DEBUG_OUT("TRACE:%s "f, __func__,## __VA_ARGS__)	// Debugging trace
+
 #ifndef DISABLE_ELF64
 # define SUPPORT_ELF64
 #endif
@@ -34,21 +42,20 @@
 // === CONSTANTS ===
 #if DEBUG
 //static const char	*csaDT_NAMES[] = {"DT_NULL", "DT_NEEDED", "DT_PLTRELSZ", "DT_PLTGOT", "DT_HASH", "DT_STRTAB", "DT_SYMTAB", "DT_RELA", "DT_RELASZ", "DT_RELAENT", "DT_STRSZ", "DT_SYMENT", "DT_INIT", "DT_FINI", "DT_SONAME", "DT_RPATH", "DT_SYMBOLIC", "DT_REL", "DT_RELSZ", "DT_RELENT", "DT_PLTREL", "DT_DEBUG", "DT_TEXTREL", "DT_JMPREL"};
-static const char	*csaR_NAMES[] = {"R_386_NONE", "R_386_32", "R_386_PC32", "R_386_GOT32", "R_386_PLT32", "R_386_COPY", "R_386_GLOB_DAT", "R_386_JMP_SLOT", "R_386_RELATIVE", "R_386_GOTOFF", "R_386_GOTPC", "R_386_LAST"};
+//static const char	*csaR_NAMES[] = {"R_386_NONE", "R_386_32", "R_386_PC32", "R_386_GOT32", "R_386_PLT32", "R_386_COPY", "R_386_GLOB_DAT", "R_386_JMP_SLOT", "R_386_RELATIVE", "R_386_GOTOFF", "R_386_GOTPC", "R_386_LAST"};
+#endif
+
+#ifdef SUPPORT_ELF64
 #endif
 
 // === PROTOTYPES ===
 void	*ElfRelocate(void *Base, char **envp, const char *Filename);
  int	ElfGetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
-void	*Elf32Relocate(void *Base, char **envp, const char *Filename);
- int	Elf32GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
- int	elf_doRelocate_386(uint32_t r_info, uint32_t *ptr, Elf32_Addr addend, int type, int bRela, const char *Sym, intptr_t iBaseDiff);
- int	elf_doRelocate_arm(uint32_t r_info, uint32_t *ptr, Elf32_Addr addend, int type, int bRela, const char *Sym, intptr_t iBaseDiff);
- int	elf_doRelocate_unk(uint32_t , uint32_t *, Elf32_Addr , int , int , const char *, intptr_t);
+void	*Elf32_Relocate(void *Base, char **envp, const char *Filename);
+ int	Elf32_GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
 #ifdef SUPPORT_ELF64
-int	_Elf64DoReloc_X86_64(void *Base, const char *strtab, Elf64_Sym *symtab, Elf64_Xword r_info, void *ptr, Elf64_Sxword addend);
-void	*Elf64Relocate(void *Base, char **envp, const char *Filename);
- int	Elf64GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
+void	*Elf64_Relocate(void *Base, char **envp, const char *Filename);
+ int	Elf64_GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size);
 #endif
 uint32_t	ElfHashString(const char *name);
 
@@ -64,10 +71,10 @@ void *ElfRelocate(void *Base, char **envp, const char *Filename)
 	switch(hdr->e_ident[4])
 	{
 	case ELFCLASS32:
-		return Elf32Relocate(Base, envp, Filename);
+		return Elf32_Relocate(Base, envp, Filename);
 #ifdef SUPPORT_ELF64
 	case ELFCLASS64:
-		return Elf64Relocate(Base, envp, Filename);
+		return Elf64_Relocate(Base, envp, Filename);
 #endif
 	default:
 		SysDebug("ld-acess - ElfRelocate: Unknown file class %i", hdr->e_ident[4]);
@@ -85,10 +92,10 @@ int ElfGetSymbol(void *Base, const char *Name, void **ret, size_t *Size)
 	switch(hdr->e_ident[4])
 	{
 	case ELFCLASS32:
-		return Elf32GetSymbol(Base, Name, ret, Size);
+		return Elf32_GetSymbol(Base, Name, ret, Size);
 #ifdef SUPPORT_ELF64
 	case ELFCLASS64:
-		return Elf64GetSymbol(Base, Name, ret, Size);
+		return Elf64_GetSymbol(Base, Name, ret, Size);
 #endif
 	default:
 		SysDebug("ld-acess - ElfRelocate: Unknown file class %i", hdr->e_ident[4]);
@@ -96,482 +103,219 @@ int ElfGetSymbol(void *Base, const char *Name, void **ret, size_t *Size)
 	}
 }
 
-int elf_doRelocate_386(uint32_t r_info, uint32_t *ptr, Elf32_Addr addend, int type,
-		int bRela, const char *Sym, intptr_t iBaseDiff)
+// --------------------------------------------------------------------
+// Elf32 support
+// --------------------------------------------------------------------
+#define ELFTYPE	Elf32
+#include "elf_impl.c"
+#undef ELFTYPE
+
+Elf32_RelocFcn	elf_doRelocate_386;
+Elf32_RelocFcn	elf_doRelocate_arm;
+
+int elf_doRelocate_386(const Elf32_RelocInfo *Info, Elf32_Word r_info, Elf32_Word* ptr, Elf32_Addr addend, bool bRela)
 {
-	void	*symval;
-	switch( type )
+	const Elf32_Sym	*sym = &Info->symtab[ ELF32_R_SYM(r_info) ];
+	void	*symval = (void*)(intptr_t)sym->st_value;
+	size_t	size = sym->st_size;
+	TRACE("%i '%s'", ELF32_R_TYPE(r_info), Info->strtab + sym->st_name);
+	switch( ELF32_R_TYPE(r_info) )
 	{
 	// Standard 32 Bit Relocation (S+A)
 	case R_386_32:
-		if( !GetSymbol(Sym, &symval, NULL) )
-			return 1;
-		DEBUGS(" elf_doRelocate: R_386_32 *0x%x += %p('%s')",
-				ptr, symval, Sym);
+		TRACE("R_386_32 *0x%x = %p + 0x%x", ptr, symval, addend);
 		*ptr = (intptr_t)symval + addend;
 		break;
 		
 	// 32 Bit Relocation wrt. Offset (S+A-P)
 	case R_386_PC32:
-		DEBUGS(" elf_doRelocate: '%s'", Sym);
-		if( !GetSymbol(Sym, &symval, NULL) )	return 1;
-		DEBUGS(" elf_doRelocate: R_386_PC32 *0x%x = 0x%x + 0x%p - 0x%x",
-			ptr, *ptr, symval, (intptr_t)ptr );
+		TRACE("R_386_PC32 *0x%x = 0x%x + 0x%p - 0x%x", ptr, *ptr, symval, (intptr_t)ptr );
 		*ptr = (intptr_t)symval + addend - (intptr_t)ptr;
 		//*ptr = val + addend - ((Uint)ptr - iBaseDiff);
 		break;
 
 	// Absolute Value of a symbol (S)
 	case R_386_GLOB_DAT:
+		TRACE("R_386_GLOB_DAT *0x%x = %p", ptr, symval);	if(0)
 	case R_386_JMP_SLOT:
-		DEBUGS(" elf_doRelocate: '%s'", Sym);
-		if( !GetSymbol(Sym, &symval, NULL) )	return 1;
-		DEBUGS(" elf_doRelocate: %s *0x%x = %p", csaR_NAMES[type], ptr, symval);
+		TRACE("R_386_JMP_SLOT *0x%x = %p", ptr, symval);
 		*ptr = (intptr_t)symval;
 		break;
 
 	// Base Address (B+A)
 	case R_386_RELATIVE:
-		DEBUGS(" elf_doRelocate: R_386_RELATIVE *0x%x = 0x%x + 0x%x", ptr, iBaseDiff, addend);
-		*ptr = iBaseDiff + addend;
+		TRACE("R_386_RELATIVE *0x%x = 0x%x + 0x%x", ptr, Info->iBaseDiff, addend);
+		*ptr = Info->iBaseDiff + addend;
 		break;
 
 	case R_386_COPY: {
-		size_t	size;
-		if( !GetSymbol(Sym, &symval, &size) )	return 1;
-		DEBUGS(" elf_doRelocate_386: R_386_COPY (%p, %p, %i)", ptr, symval, size);
-		memcpy(ptr, symval, size);
+		void *old_symval = symval;
+		GetSymbol(Info->strtab + sym->st_name, &symval, &size, Info->Base);
+		if( symval == old_symval )
+		{
+			if( ELF32_ST_BIND(sym->st_info) != STB_WEAK )
+			{
+				WARNING("sym={val:%p,size:0x%x,info:0x%x,other:0x%x,shndx:%i}",
+					sym->st_value, sym->st_size, sym->st_info, sym->st_other, sym->st_shndx);
+				WARNING("Can't find required external symbol '%s' for R_386_COPY", Info->strtab + sym->st_name);
+				return 1;
+			}
+			// Don't bother doing the memcpy
+			TRACE("R_386_COPY (%p, %p, %i)", ptr, symval, size);
+		}
+		else
+		{
+			TRACE("R_386_COPY (%p, %p, %i)", ptr, symval, size);
+			memcpy(ptr, symval, size);
+		}
 		break; }
 
 	default:
-		SysDebug("elf_doRelocate_386: Unknown relocation %i", type);
+		WARNING("Unknown relocation %i", ELF32_ST_TYPE(r_info));
 		return 2;
 	}
 	return 0;
 }
 
-int elf_doRelocate_arm(uint32_t r_info, uint32_t *ptr, Elf32_Addr addend, int type, int bRela, const char *Sym, intptr_t iBaseDiff)
+int elf_doRelocate_arm(const Elf32_RelocInfo *Info, Elf32_Word r_info, Elf32_Word* ptr, Elf32_Addr addend, bool bRela)
 {
-	uint32_t	val;
-	switch(type)
+	const Elf32_Sym	*sym = &Info->symtab[ ELF32_R_SYM(r_info) ];
+	void	*symval = (void*)(intptr_t)sym->st_value;
+	size_t	size = sym->st_size;
+	TRACE("%i '%s'", ELF32_R_TYPE(r_info), Info->strtab + sym->st_name);
+	uintptr_t	val = (uintptr_t)symval;
+	switch( ELF32_R_TYPE(r_info) )
 	{
 	// (S + A) | T
 	case R_ARM_ABS32:
-		DEBUGS(" elf_doRelocate_arm: R_ARM_ABS32 %p (%s + %x)", ptr, Sym, addend);
-		if( !GetSymbol(Sym, (void**)&val, NULL) )	return 1;
+		TRACE("R_ARM_ABS32 %p (%p + %x)", ptr, symval, addend);
 		*ptr = val + addend;
 		break;
 	case R_ARM_GLOB_DAT:
-		DEBUGS(" elf_doRelocate_arm: R_ARM_GLOB_DAT %p (%s + %x)", ptr, Sym, addend);
-		if( !GetSymbol(Sym, (void**)&val, NULL) )	return 1;
+		TRACE("R_ARM_GLOB_DAT %p (%p + %x)", ptr, symval, addend);
 		*ptr = val + addend;
 		break;
 	case R_ARM_JUMP_SLOT:
 		if(!bRela)	addend = 0;
-		DEBUGS(" elf_doRelocate_arm: R_ARM_JUMP_SLOT %p (%s + %x)", ptr, Sym, addend);
-		if( !GetSymbol(Sym, (void**)&val, NULL) )	return 1;
+		TRACE("R_ARM_JUMP_SLOT %p (%p + %x)", ptr, symval, addend);
 		*ptr = val + addend;
 		break;
 	// Copy
-	case R_ARM_COPY: {
-		size_t	size;
-		void	*src;
-		if( !GetSymbol(Sym, &src, &size) )	return 1;
-		DEBUGS(" elf_doRelocate_arm: R_ARM_COPY (%p, %p, %i)", ptr, src, size);
-		memcpy(ptr, src, size);
-		break; }
+	case R_ARM_COPY:
+		TRACE("R_ARM_COPY (%p, %p, %i)", ptr, symval, size);
+		memcpy(ptr, symval, size);
+		break;
 	// Delta between link and runtime locations + A
 	case R_ARM_RELATIVE:
-		DEBUGS(" elf_doRelocate_arm: R_ARM_RELATIVE %p (0x%x + 0x%x)", ptr, iBaseDiff, addend);
-		if(Sym[0] != '\0') {
+		TRACE("R_ARM_RELATIVE %p (0x%x + 0x%x)", ptr, Info->iBaseDiff, addend);
+		if(ELF32_R_SYM(r_info) != 0) {
 			// TODO: Get delta for a symbol
-			SysDebug("elf_doRelocate_arm: TODO - Implment R_ARM_RELATIVE for symbols");
+			WARNING("TODO - Implment R_ARM_RELATIVE for symbols");
 			return 2;
 		}
 		else {
-			*ptr = iBaseDiff + addend;
+			*ptr = Info->iBaseDiff + addend;
 		}
 		break;
 	default:
-		SysDebug("elf_doRelocate_arm: Unknown Relocation, %i", type);
+		WARNING("Unknown Relocation, %i", ELF32_R_TYPE(r_info));
 		return 2;
 	}
 	return 0;
 }
 
-int elf_doRelocate_unk(uint32_t r_info, uint32_t *ptr, Elf32_Addr addend, int type, int bRela, const char *Sym, intptr_t iBaseDiff)
-{
-	return 1;
-}
-
-void *Elf32Relocate(void *Base, char **envp, const char *Filename)
+Elf32_RelocFcn*	Elf32_GetRelocFcn(unsigned Machine)
 {
-	Elf32_Ehdr	*hdr = Base;
-	Elf32_Phdr	*phtab;
-	char	*libPath;
-	intptr_t	iRealBase = -1;
-	intptr_t	iBaseDiff;
-	 int	iSegmentCount;
-	Elf32_Rel	*rel = NULL;
-	Elf32_Rela	*rela = NULL;
-	void	*plt = NULL;
-	 int	relSz=0, relEntSz=8;
-	 int	relaSz=0, relaEntSz=8;
-	 int	pltSz=0, pltType=0;
-	Elf32_Dyn	*dynamicTab = NULL;	// Dynamic Table Pointer
-	char	*dynstrtab = NULL;	// .dynamic String Table
-	Elf32_Sym	*dynsymtab;
-	int	(*do_relocate)(uint32_t t_info, uint32_t *ptr, Elf32_Addr addend, int Type, int bRela, const char *Sym, intptr_t iBaseDiff);
-	
-	DEBUGS("ElfRelocate: (Base=0x%x)", Base);
-	
-	// Check magic header
-	
-	
-	// Parse Program Header to get Dynamic Table
-	phtab = (void*)( (uintptr_t)Base + hdr->phoff );
-	iSegmentCount = hdr->phentcount;
-	for(int i = 0; i < iSegmentCount; i ++)
+	switch(Machine)
 	{
-		switch(phtab[i].Type)
-		{
-		case PT_LOAD:
-			// Determine linked base address
-			if( iRealBase > phtab[i].VAddr)
-				iRealBase = phtab[i].VAddr;
-			break;
-		case PT_DYNAMIC:
-			// Find Dynamic Section
-			if(!dynamicTab) {
-				dynamicTab = (void *) (intptr_t) phtab[i].VAddr;
-			}
-			else {
-				DEBUGS(" WARNING - elf_relocate: Multiple PT_DYNAMIC segments");
-			}
-			break;
-		}
-	}
-	
-	// Page Align real base
-	iRealBase &= ~0xFFF;
-	DEBUGS(" elf_relocate: True Base = 0x%x, Compiled Base = 0x%x", Base, iRealBase);
-	
-	// Adjust "Real" Base
-	iBaseDiff = (intptr_t)Base - iRealBase;
-
-//	hdr->entrypoint += iBaseDiff;	// Adjust Entrypoint
-	
-	// Check if a PT_DYNAMIC segement was found
-	if(!dynamicTab) {
-		SysDebug(" elf_relocate: No PT_DYNAMIC segment in image %p, returning", Base);
-		return (void *)(intptr_t)(hdr->entrypoint + iBaseDiff);
+	case EM_386:	return elf_doRelocate_386;
+	case EM_ARM:	return elf_doRelocate_arm;
+	default:	return NULL;
 	}
+}
 
-	// Allow writing to read-only segments, just in case they need to be relocated
-	// - Will be reversed at the end of the function
-	for( int i = 0; i < iSegmentCount; i ++ )
-	{
-		if(phtab[i].Type == PT_LOAD && !(phtab[i].Flags & PF_W) ) {
-			uintptr_t	addr = phtab[i].VAddr + iBaseDiff;
-			uintptr_t	end = addr + phtab[i].MemSize;
-			for( ; addr < end; addr += PAGE_SIZE )
-				_SysSetMemFlags(addr, 0, 1);	// Unset RO
-		}
-	}
 
-	// Adjust Dynamic Table
-	dynamicTab = (void *)( (intptr_t)dynamicTab + iBaseDiff );
-	
-	// === Get Symbol table and String Table ===
-	dynsymtab = NULL;
-	for( int j = 0; dynamicTab[j].d_tag != DT_NULL; j++)
-	{
-		switch(dynamicTab[j].d_tag)
-		{
-		// --- Symbol Table ---
-		case DT_SYMTAB:
-			DEBUGS(" elf_relocate: DYNAMIC Symbol Table 0x%x (0x%x)",
-				dynamicTab[j].d_val, dynamicTab[j].d_val + iBaseDiff);
-			dynsymtab = (void*)((intptr_t)dynamicTab[j].d_val + iBaseDiff);
-			//if(iBaseDiff != 0)	dynamicTab[j].d_val += iBaseDiff;
-			break;
-		// --- String Table ---
-		case DT_STRTAB:
-			DEBUGS(" elf_relocate: DYNAMIC String Table 0x%x (0x%x)",
-				dynamicTab[j].d_val, dynamicTab[j].d_val + iBaseDiff);
-			dynstrtab = (void*)((intptr_t)dynamicTab[j].d_val + iBaseDiff);
-			//if(iBaseDiff != 0)	dynamicTab[j].d_val += iBaseDiff;
-			break;
-		// --- Hash Table --
-		case DT_HASH:
-			//if(iBaseDiff != 0)	dynamicTab[j].d_val += iBaseDiff;
-//			iSymCount = ((Elf32_Word*)(intptr_t)dynamicTab[j].d_val)[1];
-			break;
-		}
-	}
+// --------------------------------------------------------------------
+// Elf64 support
+// --------------------------------------------------------------------
+#ifdef SUPPORT_ELF64
 
-	if(dynsymtab == NULL) {
-		SysDebug("ld-acess.so - WARNING: No Dynamic Symbol table in %p, returning", hdr);
-		return (void *)(intptr_t) (hdr->entrypoint + iBaseDiff);
-	}
+#define ELFTYPE	Elf64
+#include "elf_impl.c"
+#undef ELFTYPE
 
-	// === Add to loaded list (can be imported now) ===
-	AddLoaded( Filename, Base );
+Elf64_RelocFcn	elf_doRelocate_x86_64;
 
-	// === Parse Relocation Data ===
-	DEBUGS(" elf_relocate: dynamicTab = 0x%x", dynamicTab);
-	for( int j = 0; dynamicTab[j].d_tag != DT_NULL; j++)
-	{
-		switch(dynamicTab[j].d_tag)
-		{
-		// --- Shared Library Name ---
-		case DT_SONAME:
-			DEBUGS(" elf_relocate: .so Name '%s'", dynstrtab+dynamicTab[j].d_val);
-			break;
-		// --- Needed Library ---
-		case DT_NEEDED:
-			libPath = dynstrtab + dynamicTab[j].d_val;
-			DEBUGS(" dynstrtab = %p, d_val = 0x%x", dynstrtab, dynamicTab[j].d_val);
-			DEBUGS(" Required Library '%s'", libPath);
-			if(LoadLibrary(libPath, NULL, envp) == 0) {
-				#if DEBUG
-				DEBUGS(" elf_relocate: Unable to load '%s'", libPath);
-				#else
-				SysDebug("Unable to load required library '%s'", libPath);
-				#endif
-				return 0;
-			}
-			DEBUGS(" Lib loaded");
-			break;
-		// --- PLT/GOT ---
-//		case DT_PLTGOT:	pltgot = (void*)(iBaseDiff + dynamicTab[j].d_val);	break;
-		case DT_JMPREL:	plt = (void*)(iBaseDiff + dynamicTab[j].d_val);	break;
-		case DT_PLTREL:	pltType = dynamicTab[j].d_val;	break;
-		case DT_PLTRELSZ:	pltSz = dynamicTab[j].d_val;	break;
-		
-		// --- Relocation ---
-		case DT_REL:	rel = (void*)(iBaseDiff + dynamicTab[j].d_val);	break;
-		case DT_RELSZ:	relSz = dynamicTab[j].d_val;	break;
-		case DT_RELENT:	relEntSz = dynamicTab[j].d_val;	break;
-		case DT_RELA:	rela = (void*)(iBaseDiff + dynamicTab[j].d_val);	break;
-		case DT_RELASZ:	relaSz = dynamicTab[j].d_val;	break;
-		case DT_RELAENT:	relaEntSz = dynamicTab[j].d_val;	break;
-		
-		// --- Symbol Table ---
-		case DT_SYMTAB:
-		// --- Hash Table ---
-		case DT_HASH:
-		// --- String Table ---
-		case DT_STRTAB:
-			break;
-		
-		// --- Unknown ---
-		default:
-			if(dynamicTab[j].d_tag > DT_JMPREL)	continue;
-			//DEBUGS(" elf_relocate: %i-%i = %s,0x%x",
-			//	i,j, csaDT_NAMES[dynamicTab[j].d_tag],dynamicTab[j].d_val);
-			break;
-		}
-	}
-	
-	DEBUGS(" elf_relocate: Beginning Relocation");
-
-	 int	fail = 0;
-
-	switch(hdr->machine)
+int elf_doRelocate_x86_64(const Elf64_RelocInfo *Info, Elf64_Xword r_info, Elf64_Xword* ptr, Elf64_Addr addend, bool bRela)
+{
+	const Elf64_Sym	*sym = &Info->symtab[ ELF64_R_SYM(r_info) ];
+	void	*symval = (void*)(intptr_t)sym->st_value;
+	size_t	size = sym->st_size;
+	TRACE("%i '%s'", ELF64_R_TYPE(r_info), Info->strtab + sym->st_name);
+	switch( ELF64_R_TYPE(r_info) )
 	{
-	case EM_386:
-		do_relocate = elf_doRelocate_386;
+	case R_X86_64_NONE:
 		break;
-	case EM_ARM:
-		do_relocate = elf_doRelocate_arm;
+	case R_X86_64_64:
+		TRACE("R_X86_64_64 *0x%x = %p + 0x%x", ptr, symval, addend);
+		*ptr = (intptr_t)symval + addend;
 		break;
-	default:
-		SysDebug("Elf32Relocate: Unknown machine type %i", hdr->machine);
-		do_relocate = elf_doRelocate_unk;
-		fail = 1;
+	// Absolute Value of a symbol (S)
+	case R_X86_64_GLOB_DAT:
+		TRACE("R_X86_64_GLOB_DAT *0x%x = %p", ptr, symval);	if(0)
+	case R_X86_64_JUMP_SLOT:
+		TRACE("R_X86_64_JUMP_SLOT *0x%x = %p", ptr, symval);
+		*ptr = (intptr_t)symval;
 		break;
-	}
-	
-	DEBUGS("do_relocate = %p (%p or %p)", do_relocate, &elf_doRelocate_386, &elf_doRelocate_arm);
 
-	#define _doRelocate(r_info, ptr, bRela, addend)	\
-		do_relocate(r_info, ptr, addend, ELF32_R_TYPE(r_info), bRela, \
-			dynstrtab + dynsymtab[ELF32_R_SYM(r_info)].nameOfs, iBaseDiff);
+	// Base Address (B+A)
+	case R_X86_64_RELATIVE:
+		TRACE("R_X86_64_RELATIVE *0x%x = 0x%x + 0x%x", ptr, Info->iBaseDiff, addend);
+		*ptr = Info->iBaseDiff + addend;
+		break;
 
-	// Parse Relocation Entries
-	if(rel && relSz)
-	{
-		Elf32_Word	*ptr;
-		DEBUGS(" elf_relocate: rel=0x%x, relSz=0x%x, relEntSz=0x%x", rel, relSz, relEntSz);
-		int max = relSz / relEntSz;
-		for( int i = 0; i < max; i++ )
-		{
-			//DEBUGS("  Rel %i: 0x%x+0x%x", i, iBaseDiff, rel[i].r_offset);
-			ptr = (void*)(iBaseDiff + rel[i].r_offset);
-			fail |= _doRelocate(rel[i].r_info, ptr, 0, *ptr);
-		}
-	}
-	// Parse Relocation Entries
-	if(rela && relaSz)
-	{
-		Elf32_Word	*ptr;
-		DEBUGS(" elf_relocate: rela=0x%x, relaSz=0x%x, relaEntSz=0x%x", rela, relaSz, relaEntSz);
-		int count = relaSz / relaEntSz;
-		for( int i = 0; i < count; i++ )
-		{
-			ptr = (void*)(iBaseDiff + rela[i].r_offset);
-			fail |= _doRelocate(rel[i].r_info, ptr, 1, rela[i].r_addend);
-		}
-	}
-	
-	// === Process PLT (Procedure Linkage Table) ===
-	if(plt && pltSz)
-	{
-		Elf32_Word	*ptr;
-		DEBUGS(" elf_relocate: Relocate PLT, plt=0x%x", plt);
-		if(pltType == DT_REL)
+	case R_X86_64_COPY: {
+		void *old_symval = symval;
+		GetSymbol(Info->strtab + sym->st_name, &symval, &size, Info->Base);
+		if( symval == old_symval )
 		{
-			Elf32_Rel	*pltRel = plt;
-			int count = pltSz / sizeof(Elf32_Rel);
-			DEBUGS(" elf_relocate: PLT Reloc Type = Rel, %i entries", count);
-			for(int i = 0; i < count; i ++)
+			if( ELF64_ST_BIND(sym->st_info) != STB_WEAK )
 			{
-				ptr = (void*)(iBaseDiff + pltRel[i].r_offset);
-				fail |= _doRelocate(pltRel[i].r_info, ptr, 0, *ptr);
+				WARNING("sym={val:%p,size:0x%x,info:0x%x,other:0x%x,shndx:%i}",
+					sym->st_value, sym->st_size, sym->st_info, sym->st_other, sym->st_shndx);
+				WARNING("Can't find required external symbol '%s' for R_X86_64_COPY", Info->strtab + sym->st_name);
+				return 1;
 			}
+			// Don't bother doing the memcpy
+			TRACE("R_X86_64_COPY (%p, %p, %i)", ptr, symval, size);
 		}
 		else
 		{
-			Elf32_Rela	*pltRela = plt;
-			int count = pltSz / sizeof(Elf32_Rela);
-			DEBUGS(" elf_relocate: PLT Reloc Type = Rela, %i entries", count);
-			for(int i=0;i<count;i++)
-			{
-				ptr = (void*)(iRealBase + pltRela[i].r_offset);
-				fail |= _doRelocate(pltRela[i].r_info, ptr, 1, pltRela[i].r_addend);
-			}
-		}
-	}
-
-	// Re-set readonly
-	for( int i = 0; i < iSegmentCount; i ++ )
-	{
-		// If load and not writable
-		if(phtab[i].Type == PT_LOAD && !(phtab[i].Flags & PF_W) ) {
-			uintptr_t	addr = phtab[i].VAddr + iBaseDiff;
-			uintptr_t	end = addr + phtab[i].MemSize;
-			for( ; addr < end; addr += PAGE_SIZE )
-				_SysSetMemFlags(addr, 1, 1);	// Unset RO
+			TRACE("R_X86_64_COPY (%p, %p, %i)", ptr, symval, size);
+			memcpy(ptr, symval, size);
 		}
+		break; }
+	default:
+		WARNING("Unknown Relocation, %i", ELF64_R_TYPE(r_info));
+		return 2;
 	}
-
-	if( fail ) {
-		DEBUGS("ElfRelocate: Failure");
-		return NULL;
-	}	
-
-	#undef _doRelocate
-
-	DEBUGS("ElfRelocate: RETURN 0x%x to %p", hdr->entrypoint + iBaseDiff, __builtin_return_address(0));
-	return (void*)(intptr_t)( hdr->entrypoint + iBaseDiff );
+	return 0;
 }
 
-int Elf32GetSymbol(void *Base, const char *Name, void **ret, size_t *Size)
+Elf64_RelocFcn* Elf64_GetRelocFcn(unsigned Machine)
 {
-	Elf32_Ehdr	*hdr = Base;
-	Elf32_Sym	*symtab = NULL;
-	 int	nbuckets = 0;
-	Elf32_Word	*pBuckets = NULL;
-	Elf32_Word	*pChains;
-	uint32_t	iNameHash;
-	const char	*dynstrtab = NULL;
-	uintptr_t	iBaseDiff = -1;
-	Elf32_Phdr	*phtab;
-	Elf32_Dyn	*dynTab = NULL;
-
-	// Locate the tables
-	phtab = (void*)( (uintptr_t)Base + hdr->phoff );
-	for( int i = 0; i < hdr->phentcount; i ++ )
+	switch(Machine)
 	{
-		if(phtab[i].Type == PT_LOAD && iBaseDiff > phtab[i].VAddr)
-			iBaseDiff = phtab[i].VAddr;
-		if( phtab[i].Type == PT_DYNAMIC ) {
-			dynTab = (void*)(intptr_t)phtab[i].VAddr;
-		}
-	}
-	if( !dynTab ) {
-		SysDebug("ERROR - Unable to find DYNAMIC segment in %p", Base);
-		return 0;
+	case EM_X86_64:	return elf_doRelocate_x86_64;
+	default:	return NULL;
 	}
-	iBaseDiff = (intptr_t)Base - iBaseDiff;	// Make iBaseDiff actually the diff
-	dynTab = (void*)( (intptr_t)dynTab + iBaseDiff );
-	for( int i = 0; dynTab[i].d_tag != DT_NULL; i++)
-	{
-		switch(dynTab[i].d_tag)
-		{
-		// --- Symbol Table ---
-		case DT_SYMTAB:
-			symtab = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);	// Rebased in Relocate
-			break;
-		case DT_STRTAB:
-			dynstrtab = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);
-			break;
-		// --- Hash Table --
-		case DT_HASH:
-			pBuckets = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);
-			break;
-		}
-	}
-	
-	if( !symtab ) {
-		SysDebug("ERRO - No DT_SYMTAB in %p", Base);
-		return 0;
-	}
-	if( !pBuckets ) {
-		SysDebug("ERRO - No DT_HASH in %p", Base);
-		return 0;
-	}
-	if( !dynstrtab ) {
-		SysDebug("ERRO - No DT_STRTAB in %p", Base);
-		return 0;
-	}
-
-	// ... ok... maybe they haven't been relocated
-	if( (uintptr_t)symtab < (uintptr_t)Base )
-	{
-		symtab    = (void*)( (uintptr_t)symtab    + iBaseDiff );
-		pBuckets  = (void*)( (uintptr_t)pBuckets  + iBaseDiff );
-		dynstrtab = (void*)( (uintptr_t)dynstrtab + iBaseDiff );
-		SysDebug("Executable not yet relocated");
-	}
-
-	nbuckets = pBuckets[0];
-//	iSymCount = pBuckets[1];
-	pBuckets = &pBuckets[2];
-	pChains = &pBuckets[ nbuckets ];
-	assert(pChains);
+}
 
-	// Get hash
-	iNameHash = ElfHashString(Name);
-	iNameHash %= nbuckets;
+#endif	// SUPPORT_ELF64
 
-	// Walk Chain
-	int idx = pBuckets[ iNameHash ];
-	do {
-		Elf32_Sym *sym = &symtab[idx];
-		assert(sym);
-		if(sym->shndx != SHN_UNDEF && strcmp(dynstrtab + sym->nameOfs, Name) == 0) {
-			*ret = (void*)( (uintptr_t)sym->value + iBaseDiff );
-			if(Size)	*Size = sym->size;
-			return 1;
-		}
-	} while( (idx = pChains[idx]) != STN_UNDEF && idx != pBuckets[iNameHash] );
-	
-	return 0;
-}
 
 #ifdef SUPPORT_ELF64
+#if 0
 typedef int (*t_elf64_doreloc)(void *Base, const char *strtab, Elf64_Sym *symtab, Elf64_Xword r_info, void *ptr, Elf64_Sxword addend);
 
 int _Elf64DoReloc_X86_64(void *Base, const char *strtab, Elf64_Sym *symtab, Elf64_Xword r_info, void *ptr, Elf64_Sxword addend)
@@ -586,20 +330,20 @@ int _Elf64DoReloc_X86_64(void *Base, const char *strtab, Elf64_Sym *symtab, Elf6
 	case R_X86_64_NONE:
 		break;
 	case R_X86_64_64:
-		if( !GetSymbol(symname, &symval, NULL)  )	return 1;
+		if( !GetSymbol(symname, &symval, NULL, NULL)  )	return 1;
 		*(uint64_t*)ptr = (uintptr_t)symval + addend;
 		break;
 	case R_X86_64_COPY: {
 		size_t	size;
-		if( !GetSymbol(symname, &symval, &size)  )	return 1;
+		if( !GetSymbol(symname, &symval, &size, NULL)  )	return 1;
 		memcpy(ptr, symval, size);
 		} break;
 	case R_X86_64_GLOB_DAT:
-		if( !GetSymbol(symname, &symval, NULL)  )	return 1;
+		if( !GetSymbol(symname, &symval, NULL, NULL)  )	return 1;
 		*(uint64_t*)ptr = (uintptr_t)symval;
 		break;
 	case R_X86_64_JUMP_SLOT:
-		if( !GetSymbol(symname, &symval, NULL)  )	return 1;
+		if( !GetSymbol(symname, &symval, NULL, NULL)  )	return 1;
 		*(uint64_t*)ptr = (uintptr_t)symval;
 		break;
 	case R_X86_64_RELATIVE:
@@ -630,18 +374,18 @@ void *Elf64Relocate(void *Base, char **envp, const char *Filename)
 	void	*pltrel = NULL;
 	 int	plt_size = 0, plt_type = 0;
 
-	DEBUGS("Elf64Relocate: hdr = {");
-	DEBUGS("Elf64Relocate:  e_ident = '%.16s'", hdr->e_ident);
-	DEBUGS("Elf64Relocate:  e_type = 0x%x", hdr->e_type);
-	DEBUGS("Elf64Relocate:  e_machine = 0x%x", hdr->e_machine);
-	DEBUGS("Elf64Relocate:  e_version = 0x%x", hdr->e_version);
-	DEBUGS("Elf64Relocate:  e_entry = %p", hdr->e_entry);
-	DEBUGS("Elf64Relocate:  e_phoff = 0x%llx", hdr->e_phoff);
-	DEBUGS("Elf64Relocate:  e_shoff = 0x%llx", hdr->e_shoff);
-	DEBUGS("Elf64Relocate:  e_flags = 0x%x", hdr->e_flags);
-	DEBUGS("Elf64Relocate:  e_ehsize = 0x%x", hdr->e_ehsize);
-	DEBUGS("Elf64Relocate:  e_phentsize = 0x%x", hdr->e_phentsize);
-	DEBUGS("Elf64Relocate:  e_phnum = %i", hdr->e_phnum);
+	TRACE("hdr = {");
+	TRACE(" e_ident = '%.16s'", hdr->e_ident);
+	TRACE(" e_type = 0x%x", hdr->e_type);
+	TRACE(" e_machine = 0x%x", hdr->e_machine);
+	TRACE(" e_version = 0x%x", hdr->e_version);
+	TRACE(" e_entry = %p", hdr->e_entry);
+	TRACE(" e_phoff = 0x%llx", hdr->e_phoff);
+	TRACE(" e_shoff = 0x%llx", hdr->e_shoff);
+	TRACE(" e_flags = 0x%x", hdr->e_flags);
+	TRACE(" e_ehsize = 0x%x", hdr->e_ehsize);
+	TRACE(" e_phentsize = 0x%x", hdr->e_phentsize);
+	TRACE(" e_phnum = %i", hdr->e_phnum);
 
 	// Scan for the dynamic table (and find the compiled base)
 	phtab = (void*)((uintptr_t)Base + (uintptr_t)hdr->e_phoff);
@@ -655,7 +399,7 @@ void *Elf64Relocate(void *Base, char **envp, const char *Filename)
 
 	baseDiff = (uintptr_t)Base - compiledBase;
 
-	DEBUGS("baseDiff = %p", baseDiff);
+	TRACE("baseDiff = %p", baseDiff);
 
 	if(dyntab == NULL) {
 		SysDebug(" Elf64Relocate: No PT_DYNAMIC segment in image %p, returning", Base);
@@ -696,14 +440,14 @@ void *Elf64Relocate(void *Base, char **envp, const char *Filename)
 	// Second pass on dynamic table
 	for(i = 0; dyntab[i].d_tag != DT_NULL; i ++)
 	{
-		DEBUGS("dyntab[%i].d_tag = %i", i, dyntab[i].d_tag);
+		TRACE("dyntab[%i].d_tag = %i", i, dyntab[i].d_tag);
 		switch(dyntab[i].d_tag)
 		{
 		case DT_SONAME:	break;
 
 		case DT_NEEDED: {
 			char *libPath = strtab + dyntab[i].d_un.d_val;
-			DEBUGS("Elf64Relocate: libPath = '%s'", libPath);
+			TRACE("Elf64Relocate: libPath = '%s'", libPath);
 			if(LoadLibrary(libPath, NULL, envp) == 0) {
 				SysDebug("ld-acess - Elf64Relocate: Unable to load '%s'", libPath);
 				return NULL;
@@ -752,6 +496,8 @@ void *Elf64Relocate(void *Base, char **envp, const char *Filename)
 		}
 	}
 
+	// TODO: Relocate symbols
+	
 	// Relocation function
 	t_elf64_doreloc fpElf64DoReloc = &_Elf64DoReloc_X86_64;
 	#define _Elf64DoReloc(info, ptr, addend)	fpElf64DoReloc(Base, strtab, symtab, info, ptr, addend)
@@ -759,7 +505,7 @@ void *Elf64Relocate(void *Base, char **envp, const char *Filename)
 	int fail = 0;
 	if( rel )
 	{
-		DEBUGS("rel_count = %i", rel_count);
+		TRACE("rel_count = %i", rel_count);
 		for( i = 0; i < rel_count; i ++ )
 		{
 			uint64_t *ptr = (void *)(uintptr_t)( rel[i].r_offset + baseDiff );
@@ -769,7 +515,7 @@ void *Elf64Relocate(void *Base, char **envp, const char *Filename)
 
 	if( rela )
 	{
-		DEBUGS("rela_count = %i", rela_count);
+		TRACE("rela_count = %i", rela_count);
 		for( i = 0; i < rela_count; i ++ )
 		{
 			uint64_t *ptr = (void *)(uintptr_t)( rela[i].r_offset + baseDiff );
@@ -782,7 +528,7 @@ void *Elf64Relocate(void *Base, char **envp, const char *Filename)
 		if( plt_type == DT_REL ) {
 			Elf64_Rel	*plt = pltrel;
 			 int	count = plt_size / sizeof(Elf64_Rel);
-			DEBUGS("plt rel count = %i", count);
+			TRACE("plt rel count = %i", count);
 			for( i = 0; i < count; i ++ )
 			{
 				uint64_t *ptr = (void *)(uintptr_t)( plt[i].r_offset + baseDiff );
@@ -792,7 +538,7 @@ void *Elf64Relocate(void *Base, char **envp, const char *Filename)
 		else {
 			Elf64_Rela	*plt = pltrel;
 			 int	count = plt_size / sizeof(Elf64_Rela);
-			DEBUGS("plt rela count = %i", count);
+			TRACE("plt rela count = %i", count);
 			for( i = 0; i < count; i ++ )
 			{
 				uint64_t *ptr = (void *)(uintptr_t)( plt[i].r_offset + baseDiff );
@@ -802,13 +548,13 @@ void *Elf64Relocate(void *Base, char **envp, const char *Filename)
 	}
 
 	if( fail ) {
-		DEBUGS("Elf64Relocate: Failure");
+		TRACE("Failure");
 		return NULL;
 	}
 
 	{
 	void *ret = (void *)(uintptr_t)(hdr->e_entry + baseDiff);
-	DEBUGS("Elf64Relocate: Relocations done, return %p", ret);
+	TRACE("Relocations done, return %p", ret);
 	return ret;
 	}
 }
@@ -887,7 +633,7 @@ int Elf64GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size)
 	if(symtab[i].st_shndx != SHN_UNDEF && strcmp(dynstrtab + symtab[i].st_name, Name) == 0) {
 		*Ret = (void*)( (intptr_t)symtab[i].st_value + iBaseDiff );
 		if(Size)	*Size = symtab[i].st_size;
-		DEBUGS("%s = %p", Name, *Ret);
+		TRACE("%s = %p", Name, *Ret);
 		return 1;
 	}
 	
@@ -897,7 +643,7 @@ int Elf64GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size)
 		if(symtab[i].st_shndx != SHN_UNDEF && strcmp(dynstrtab + symtab[i].st_name, Name) == 0) {
 			*Ret = (void*)((intptr_t)symtab[i].st_value + iBaseDiff);
 			if(Size)	*Size = symtab[i].st_size;
-			DEBUGS("%s = %p", Name, *Ret);
+			TRACE("%s = %p", Name, *Ret);
 			return 1;
 		}
 	}
@@ -905,7 +651,7 @@ int Elf64GetSymbol(void *Base, const char *Name, void **Ret, size_t *Size)
 	return 0;
 }
 #endif
-
+#endif
 
 uint32_t ElfHashString(const char *name)
 {
diff --git a/Usermode/Libraries/ld-acess.so_src/elf32.h b/Usermode/Libraries/ld-acess.so_src/elf32.h
index 2e9c1c4a9e161771d770d824c595781946c7838a..40c801c92a1b269c8becc87dd3821a6a066c0a45 100644
--- a/Usermode/Libraries/ld-acess.so_src/elf32.h
+++ b/Usermode/Libraries/ld-acess.so_src/elf32.h
@@ -12,6 +12,7 @@ typedef uint16_t	Elf32_Half;
 typedef uint32_t	Elf32_Addr;
 typedef uint32_t	Elf32_Off;
 typedef uint32_t	Elf32_Word;
+typedef uint32_t	Elf32_Xword;	// < not strictly correct... but meh
 typedef int32_t 	Elf32_Sword;
 
 #define ELFCLASS32	1
@@ -26,19 +27,19 @@ typedef int32_t 	Elf32_Sword;
 */
 struct sElf32_Ehdr {
 	uint8_t	e_ident[16];	//!< Identifier Bytes
-	Elf32_Half	filetype;	//!< File Type
-	Elf32_Half	machine;	//!< Machine / Arch
-	Elf32_Word	version;	//!< Version (File?)
-	Elf32_Addr	entrypoint;	//!< Entry Point
-	Elf32_Off	phoff;	//!< Program Header Offset
-	Elf32_Word	shoff;	//!< Section Header Offset
-	Elf32_Word	flags;	//!< Flags
-	Elf32_Half	headersize;	//!< Header Size
-	Elf32_Half	phentsize;	//!< Program Header Entry Size
-	Elf32_Half	phentcount;	//!< Program Header Entry Count
-	Elf32_Half	shentsize;	//!< Section Header Entry Size
-	Elf32_Half	shentcount;	//!< Section Header Entry Count
-	Elf32_Half	shstrindex;	//!< Section Header String Table Index
+	Elf32_Half	e_filetype;	//!< File Type
+	Elf32_Half	e_machine;	//!< Machine / Arch
+	Elf32_Word	e_version;	//!< Version (File?)
+	Elf32_Addr	e_entry;	//!< Entry Point
+	Elf32_Off	e_phoff;	//!< Program Header Offset
+	Elf32_Word	e_shoff;	//!< Section Header Offset
+	Elf32_Word	e_flags;	//!< Flags
+	Elf32_Half	e_headersize;	//!< Header Size
+	Elf32_Half	e_phentsize;	//!< Program Header Entry Size
+	Elf32_Half	e_phnum;	//!< Program Header Entry Count
+	Elf32_Half	e_shentsize;	//!< Section Header Entry Size
+	Elf32_Half	e_shentcount;	//!< Section Header Entry Count
+	Elf32_Half	e_shstrindex;	//!< Section Header String Table Index
 };
 
 /**
@@ -112,15 +113,24 @@ struct sElf32_Shent {
 #endif
 
 struct elf_sym_s {
-	Elf32_Word	nameOfs;
-	Elf32_Addr	value;	//Address
-	Elf32_Word	size;
-	uint8_t	info;
-	uint8_t	other;
-	Elf32_Half	shndx;
+	Elf32_Word	st_name;
+	Elf32_Addr	st_value;	//Address
+	Elf32_Word	st_size;
+	uint8_t 	st_info;
+	uint8_t 	st_other;
+	Elf32_Half	st_shndx;
 };
 #define	STN_UNDEF	0	// Undefined Symbol
 
+#define ELF32_ST_BIND(i)	((i)>>4)
+#define ELF32_ST_TYPE(i)	((i)&0xF)
+
+enum {
+	STB_LOCAL,
+	STB_GLOBAL,
+	STB_WEAK,
+};
+
 enum {
 	PT_NULL,	//0
 	PT_LOAD,	//1
@@ -138,14 +148,14 @@ enum {
 #define PF_R	4
 
 struct sElf32_Phdr {
-	Elf32_Word	Type;
-	Elf32_Off	Offset;
-	Elf32_Addr	VAddr;
-	Elf32_Addr	PAddr;
-	Elf32_Word	FileSize;
-	Elf32_Word	MemSize;
-	Elf32_Word	Flags;
-	Elf32_Word	Align;
+	Elf32_Word	p_type;
+	Elf32_Off	p_offset;
+	Elf32_Addr	p_vaddr;
+	Elf32_Addr	p_paddr;
+	Elf32_Word	p_filesz;
+	Elf32_Word	p_memsz;
+	Elf32_Word	p_flags;
+	Elf32_Word	p_align;
 };
 
 struct elf32_rel_s {
diff --git a/Usermode/Libraries/ld-acess.so_src/elf64.h b/Usermode/Libraries/ld-acess.so_src/elf64.h
index fb39782ab0b116ee6fe718c616dc37c9fe8c823a..9d04e8af5fd6fb8b9dfae543822fa1928267f104 100644
--- a/Usermode/Libraries/ld-acess.so_src/elf64.h
+++ b/Usermode/Libraries/ld-acess.so_src/elf64.h
@@ -69,7 +69,7 @@ typedef struct
 	union {
 		Elf64_Xword	d_val;
 		Elf64_Addr	d_ptr;
-	} d_un;
+	};// d_un;
 } Elf64_Dyn;
 
 typedef struct
@@ -97,6 +97,8 @@ typedef struct
 
 #define ELF64_R_SYM(info)	((info) >> 32)
 #define ELF64_R_TYPE(info)	((info) & 0xFFFFFFFF)
+#define ELF64_ST_BIND(i)	((i)>>4)
+#define ELF64_ST_TYPE(i)	((i)&0xF)
 
 enum eElf64_RelocTypes_x86_64
 {
diff --git a/Usermode/Libraries/ld-acess.so_src/elf_impl.c b/Usermode/Libraries/ld-acess.so_src/elf_impl.c
new file mode 100644
index 0000000000000000000000000000000000000000..a89a26f695c06def4e58db210364b23a8ec8a5f8
--- /dev/null
+++ b/Usermode/Libraries/ld-acess.so_src/elf_impl.c
@@ -0,0 +1,506 @@
+
+#ifndef ELFTYPE
+# error "ELFTYPE must be defined to either ELf32 or Elf64 to use this file"
+#endif
+
+#define __PREF(t,s)	t##_##s
+#define _PREF(t,s)	__PREF(t,s)
+#define PREF(sym)	_PREF(ELFTYPE,sym)
+
+typedef struct
+{
+	void	*Base;
+	intptr_t	iBaseDiff;
+	const char	*strtab;
+	const PREF(Sym)	*symtab;
+} PREF(RelocInfo);
+
+typedef int PREF(RelocFcn)(const PREF(RelocInfo)* Info, PREF(Xword) t_info, PREF(Xword)* ptr, PREF(Addr) addend, bool bRela);
+
+// - Extern
+static PREF(RelocFcn)*	PREF(GetRelocFcn)(unsigned Machine);
+// - Local
+static PREF(RelocFcn) PREF(doRelocate_unk);
+static void PREF(int_GetBaseDyntab)(void* Base, const PREF(Phdr)* phtab, unsigned phentcount, intptr_t* iBaseDiff_p, PREF(Dyn)** dynamicTab_p);
+static int PREF(GetSymbolVars)(void *Base, const PREF(Sym)** symtab, const PREF(Word)** pBuckets, const char **dynstrtab, uintptr_t* piBaseDiff);
+static int PREF(GetSymbolInfo)(void *Base, const char *Name, void **Addr, size_t *Size, int* Section, int *Binding, int *Type);
+
+// - Relocate
+void *PREF(Relocate)(void *Base, char **envp, const char *Filename)
+{
+	TRACE("(Base=0x%x)", Base);
+	const PREF(Ehdr)	*hdr = Base;
+	
+	// Check magic header
+	// TODO: Validate header?
+	
+	
+	// Parse Program Header to get Dynamic Table
+	// - Determine the linked base of the executable
+	const PREF(Phdr)	*phtab = (void*)( (uintptr_t)Base + hdr->e_phoff );
+	
+	intptr_t	iBaseDiff = 0;
+	PREF(Dyn)*	dynamicTab = NULL;
+	
+	PREF(int_GetBaseDyntab)(Base, phtab, hdr->e_phnum, &iBaseDiff, &dynamicTab);
+	
+	// Check if a PT_DYNAMIC segement was found
+	if(!dynamicTab)
+	{
+		SysDebug(" elf_relocate: No PT_DYNAMIC segment in image %p, returning", Base);
+		return (void *)(intptr_t)(hdr->e_entry + iBaseDiff);
+	}
+
+	// Allow writing to read-only segments, just in case they need to be relocated
+	// - Will be reversed at the end of the function
+	for( unsigned i = 0; i < hdr->e_phnum; i ++ )
+	{
+		if(phtab[i].p_type == PT_LOAD && !(phtab[i].p_flags & PF_W) )
+		{
+			uintptr_t	addr = phtab[i].p_vaddr + iBaseDiff;
+			uintptr_t	end = addr + phtab[i].p_memsz;
+			for( ; addr < end; addr += PAGE_SIZE )
+				_SysSetMemFlags(addr, 0, 1);	// Unset RO
+		}
+	}
+	
+	// === Get Symbol table and String Table ===
+	char	*dynstrtab = NULL;	// .dynamic String Table
+	PREF(Sym)	*dynsymtab = NULL;
+	PREF(Word)	*hashtable = NULL;
+	unsigned	iSymCount = 0;
+	for( unsigned j = 0; dynamicTab[j].d_tag != DT_NULL; j++)
+	{
+		const PREF(Dyn)	*dt = &dynamicTab[j];
+		switch(dt->d_tag)
+		{
+		// --- Symbol Table ---
+		case DT_SYMTAB:
+			TRACE("DYNAMIC Symbol Table 0x%x (0x%x)", dt->d_val, dt->d_val + iBaseDiff);
+			dynsymtab = (void*)((intptr_t)dt->d_val + iBaseDiff);
+			break;
+		// --- String Table ---
+		case DT_STRTAB:
+			TRACE("DYNAMIC String Table 0x%x (0x%x)", dt->d_val, dt->d_val + iBaseDiff);
+			dynstrtab = (void*)((intptr_t)dt->d_val + iBaseDiff);
+			break;
+		// --- Hash Table --
+		case DT_HASH:
+			TRACE("DYNAMIC Hash table %p (%p)", dt->d_val, dt->d_val + iBaseDiff);
+			hashtable = (void*)((intptr_t)dt->d_val + iBaseDiff);
+			iSymCount = hashtable[1];
+			break;
+		}
+	}
+
+	if(dynsymtab == NULL) {
+		SysDebug("ld-acess.so - WARNING: No Dynamic Symbol table in %p, returning", hdr);
+		return (void *)(intptr_t) (hdr->e_entry + iBaseDiff);
+	}
+	
+	// Apply base offset to locally defined symbols
+	// - #0 is defined as ("" SHN_UNDEF), so skip it
+	for( unsigned i = 1; i < iSymCount; i ++ )
+	{
+		PREF(Sym)	*sym = &dynsymtab[i];
+		const char *name = dynstrtab + sym->st_name;
+		(void)name;
+		if( sym->st_shndx == SHN_UNDEF )
+		{
+			TRACE("Sym %i'%s' deferred (SHN_UNDEF)", i, name);
+		}
+		else if( sym->st_shndx == SHN_ABS )
+		{
+			// Leave as is
+			TRACE("Sym %i'%s' untouched", i, name);
+		}
+		else
+		{
+			void *newval;
+			size_t	newsize;
+			if( ELF32_ST_BIND(sym->st_info) != STB_WEAK )
+			{
+				TRACE("Sym %i'%s' = %p (local)", i, name, sym->st_value + iBaseDiff);
+				sym->st_value += iBaseDiff;
+			}
+			// If GetSymbol doesn't return a strong/global symbol value
+			else if( GetSymbol(name, &newval, &newsize, Base) != 1 )
+			{
+				TRACE("Sym %i'%s' = %p (Local weak)", i, name, sym->st_value + iBaseDiff);
+				sym->st_value += iBaseDiff;
+			}
+			else
+			{
+				TRACE("Sym %i'%s' = %p+0x%x (Extern weak)", i, name, newval, newsize);
+				sym->st_value = (uintptr_t)newval;
+				sym->st_size = newsize;
+			}
+		}
+	}
+
+	// === Add to loaded list (can be imported now) ===
+	AddLoaded( Filename, Base );
+
+	// === Parse Relocation Data ===
+	PREF(Rel)	*rel = NULL;
+	PREF(Rela)	*rela = NULL;
+	void	*plt = NULL;
+	 int	relSz=0, relEntSz=8;
+	 int	relaSz=0, relaEntSz=8;
+	 int	pltSz=0, pltType=0;
+	TRACE("dynamicTab = 0x%x", dynamicTab);
+	for( int j = 0; dynamicTab[j].d_tag != DT_NULL; j++)
+	{
+		const PREF(Dyn)	*dt = &dynamicTab[j];
+		switch(dt->d_tag)
+		{
+		// --- Shared Library Name ---
+		case DT_SONAME:
+			TRACE(".so Name '%s'", dynstrtab + dt->d_val);
+			break;
+		// --- Needed Library ---
+		case DT_NEEDED: {
+			//assert(dt->d_val < sizeof_dynstrtab);	// Disabled, no sizeof_dynstrtab
+			const char	*libPath = dynstrtab + dt->d_val;
+			TRACE(" Required Library '%s'", libPath);
+			if(LoadLibrary(libPath, NULL, envp) == 0) {
+				SysDebug("Unable to load required library '%s'", libPath);
+				return 0;
+			}
+			TRACE(" Lib loaded");
+			break; }
+		// --- PLT/GOT ---
+//		case DT_PLTGOT:	pltgot = (void*)(iBaseDiff + dt->d_val);	break;
+		case DT_JMPREL:	plt = (void*)(iBaseDiff + dt->d_val);	break;
+		case DT_PLTREL:	pltType = dt->d_val;	break;
+		case DT_PLTRELSZ:	pltSz = dt->d_val;	break;
+		
+		// --- Relocation ---
+		case DT_REL:	rel = (void*)(iBaseDiff + dt->d_val);	break;
+		case DT_RELSZ:	relSz = dt->d_val;	break;
+		case DT_RELENT:	relEntSz = dt->d_val;	break;
+		case DT_RELA:	rela = (void*)(iBaseDiff + dt->d_val);	break;
+		case DT_RELASZ:	relaSz = dt->d_val;	break;
+		case DT_RELAENT:	relaEntSz = dt->d_val;	break;
+		
+		// --- Symbol Table ---
+		case DT_SYMTAB:
+		// --- Hash Table ---
+		case DT_HASH:
+		// --- String Table ---
+		case DT_STRTAB:
+			break;
+		
+		// --- Unknown ---
+		default:
+			if(dt->d_tag > DT_JMPREL)	continue;
+			//DEBUGS(" elf_relocate: %i-%i = %s,0x%x",
+			//	i,j, csaDT_NAMES[dynamicTab[j].d_tag],dynamicTab[j].d_val);
+			break;
+		}
+	}
+	
+	// Resolve symbols (second pass)
+	// - #0 is defined as ("" SHN_UNDEF), so skip it
+	 int	fail = 0;
+	for( int i = 1; i < iSymCount; i ++ )
+	{
+		PREF(Sym)	*sym = &dynsymtab[i];
+		const char *name = dynstrtab + sym->st_name;
+		if( sym->st_shndx == SHN_UNDEF )
+		{
+			void *newval;
+			size_t	newsize;
+			if( !GetSymbol(name, &newval, &newsize, Base) ) {
+				if( ELF32_ST_BIND(sym->st_info) != STB_WEAK ) {
+					// Not a weak binding, set fail and move on
+					WARNING("%s: Can't find required symbol '%s' for '%s'", __func__, name, Filename);
+					fail = 1;
+					continue ;
+				}
+				// Leave the symbol value as-is
+			}
+			else {
+				TRACE("Sym %i'%s' bound to %p+0x%x", i, name, newval, newsize);
+				sym->st_value = (intptr_t)newval;
+				sym->st_size = newsize;
+			}
+		}
+		else if( sym->st_shndx == SHN_ABS )
+		{
+			// Leave as is
+		}
+		else
+		{
+			// Handled previously
+			// TODO: What about weak locally-defined symbols?
+			//assert( ELF32_ST_BIND(sym->st_info) != STB_WEAK );
+		}
+	}
+	if( fail ) {
+		WARNING("Relocation of '%s' failed", Filename);
+		return NULL;
+	}
+	
+	TRACE("Beginning Relocation on '%s'", Filename);
+
+
+	PREF(RelocFcn)*	do_relocate = PREF(GetRelocFcn)(hdr->e_machine);
+	if( do_relocate == 0 )
+	{
+		SysDebug("%s: Unknown machine type %i", __func__, hdr->e_machine);
+		do_relocate = PREF(doRelocate_unk);
+		fail = 1;
+	}
+	
+	TRACE("do_relocate = %p", do_relocate);
+
+	#define _doRelocate(r_info, ptr, bRela, addend)	\
+		do_relocate(&reloc_info, r_info, ptr, addend, bRela);
+
+	PREF(RelocInfo)	reloc_info = {
+		.Base = Base,
+		.iBaseDiff = iBaseDiff,
+		.strtab = dynstrtab,
+		.symtab = dynsymtab
+	};
+
+	// Parse Relocation Entries
+	if(rel && relSz)
+	{
+		TRACE("rel=0x%x, relSz=0x%x, relEntSz=0x%x", rel, relSz, relEntSz);
+		int max = relSz / relEntSz;
+		for( int i = 0; i < max; i++ )
+		{
+			PREF(Xword) *ptr = (void*)(iBaseDiff + rel[i].r_offset);
+			fail |= _doRelocate(rel[i].r_info, ptr, 0, *ptr);
+		}
+	}
+	// Parse Relocation Entries
+	if(rela && relaSz)
+	{
+		TRACE("rela=0x%x, relaSz=0x%x, relaEntSz=0x%x", rela, relaSz, relaEntSz);
+		int count = relaSz / relaEntSz;
+		for( int i = 0; i < count; i++ )
+		{
+			void *ptr = (void*)(iBaseDiff + rela[i].r_offset);
+			fail |= _doRelocate(rela[i].r_info, ptr, 1, rela[i].r_addend);
+		}
+	}
+	
+	// === Process PLT (Procedure Linkage Table) ===
+	if(plt && pltSz)
+	{
+		TRACE("Relocate PLT, plt=0x%x", plt);
+		if(pltType == DT_REL)
+		{
+			PREF(Rel)	*pltRel = plt;
+			int count = pltSz / sizeof(*pltRel);
+			TRACE("PLT Reloc Type = Rel, %i entries", count);
+			for(int i = 0; i < count; i ++)
+			{
+				PREF(Xword) *ptr = (void*)(iBaseDiff + pltRel[i].r_offset);
+				fail |= _doRelocate(pltRel[i].r_info, ptr, 0, *ptr);
+			}
+		}
+		else
+		{
+			PREF(Rela)	*pltRela = plt;
+			int count = pltSz / sizeof(*pltRela);
+			TRACE("PLT Reloc Type = Rela, %i entries", count);
+			for(int i=0;i<count;i++)
+			{
+				void *ptr = (void*)(iBaseDiff + pltRela[i].r_offset);
+				fail |= _doRelocate(pltRela[i].r_info, ptr, 1, pltRela[i].r_addend);
+			}
+		}
+	}
+
+	// Re-set readonly
+	for( int i = 0; i < hdr->e_phnum; i ++ )
+	{
+		// If load and not writable
+		if(phtab[i].p_type == PT_LOAD && !(phtab[i].p_flags & PF_W) )
+		{
+			uintptr_t	addr = phtab[i].p_vaddr + iBaseDiff;
+			uintptr_t	end = addr + phtab[i].p_memsz;
+			for( ; addr < end; addr += PAGE_SIZE )
+				_SysSetMemFlags(addr, 1, 1);	// Unset RO
+		}
+	}
+
+	if( fail ) {
+		TRACE("ElfRelocate: Failure");
+		return NULL;
+	}	
+
+	#undef _doRelocate
+
+	TRACE("RETURN 0x%x to %p", hdr->e_entry + iBaseDiff, __builtin_return_address(0));
+	return (void*)(intptr_t)( hdr->e_entry + iBaseDiff );
+}
+
+void PREF(int_GetBaseDyntab)(void* Base, const PREF(Phdr)* phtab, unsigned phentcount, intptr_t* iBaseDiff_p, PREF(Dyn)** dynamicTab_p)
+{
+	uintptr_t	iRealBase = UINTPTR_MAX;
+	assert(dynamicTab_p);
+	
+	for(unsigned i = 0; i < phentcount; i ++)
+	{
+		switch(phtab[i].p_type)
+		{
+		case PT_LOAD:
+			// Determine linked base address
+			if( iRealBase > phtab[i].p_vaddr)
+				iRealBase = phtab[i].p_vaddr;
+			break;
+		case PT_DYNAMIC:
+			// Find Dynamic Section
+			if(!*dynamicTab_p) {
+				*dynamicTab_p = (void *) (intptr_t) phtab[i].p_vaddr;
+			}
+			else {
+				WARNING("elf_relocate: Multiple PT_DYNAMIC segments");
+			}
+			break;
+		}
+	}
+	
+	// Page Align real base
+	iRealBase &= ~0xFFF;
+	
+	// Adjust "Real" Base
+	const intptr_t	iBaseDiff = (intptr_t)Base - iRealBase;
+	*iBaseDiff_p = iBaseDiff;
+
+	// Adjust Dynamic Table
+	if( *dynamicTab_p )
+	{
+		*dynamicTab_p = (void *)( (intptr_t)*dynamicTab_p + iBaseDiff );
+	}
+
+	TRACE("True Base = 0x%x, Compiled Base = 0x%x, Difference = 0x%x", Base, iRealBase, *iBaseDiff_p);
+}
+
+int PREF(doRelocate_unk)(const PREF(RelocInfo)* Info, PREF(Xword) r_info, PREF(Xword)* ptr, PREF(Addr) addend, bool bRela)
+{
+	return 1;
+}
+
+int PREF(GetSymbolVars)(void *Base, const PREF(Sym)** symtab, const PREF(Word)** pBuckets, const char **dynstrtab, uintptr_t* piBaseDiff)
+{
+	const PREF(Ehdr)*	hdr = Base;
+	PREF(Dyn)*	dynTab = NULL;
+	intptr_t	iBaseDiff = -1;
+	
+	PREF(int_GetBaseDyntab)(Base, (void*)( (uintptr_t)Base + hdr->e_phoff ), hdr->e_phnum, &iBaseDiff, &dynTab);
+	if( !dynTab ) {
+		SysDebug("ERROR - Unable to find DYNAMIC segment in %p", Base);
+		return 1;
+	}
+	
+	for( int i = 0; dynTab[i].d_tag != DT_NULL; i++)
+	{
+		switch(dynTab[i].d_tag)
+		{
+		// --- Symbol Table ---
+		case DT_SYMTAB:
+			*symtab = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);	// Rebased in Relocate
+			break;
+		case DT_STRTAB:
+			*dynstrtab = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);
+			break;
+		// --- Hash Table --
+		case DT_HASH:
+			*pBuckets = (void*)((intptr_t)dynTab[i].d_val + iBaseDiff);
+			break;
+		}
+	}
+	
+	if( !*symtab ) {
+		SysDebug("ERRO - No DT_SYMTAB in %p", Base);
+		return 1;
+	}
+	if( !*pBuckets ) {
+		SysDebug("ERRO - No DT_HASH in %p", Base);
+		return 1;
+	}
+	if( !*dynstrtab ) {
+		SysDebug("ERRO - No DT_STRTAB in %p", Base);
+		return 1;
+	}
+
+	// ... ok... maybe they haven't been relocated
+	if( (uintptr_t)*symtab < (uintptr_t)Base )
+	{
+		SysDebug("Executable not yet relocated (symtab,pBuckets,dynstrtab = %p,%p,%p + 0x%x)",
+			*symtab,*pBuckets,*dynstrtab, iBaseDiff);
+		*symtab    = (void*)( (uintptr_t)*symtab    + iBaseDiff );
+		*pBuckets  = (void*)( (uintptr_t)*pBuckets  + iBaseDiff );
+		*dynstrtab = (void*)( (uintptr_t)*dynstrtab + iBaseDiff );
+	}
+	*piBaseDiff = iBaseDiff;
+	return 0;
+}
+
+int PREF(GetSymbolInfo)(void *Base, const char *Name, void **Addr, size_t *Size, int* Section, int *Binding, int *Type)
+{
+	// Locate the tables
+	uintptr_t	iBaseDiff = -1;
+	const PREF(Sym)*	symtab = NULL;
+	const PREF(Word)*	pBuckets = NULL;
+	const char	*dynstrtab = NULL;
+	if( PREF(GetSymbolVars)(Base, &symtab, &pBuckets, &dynstrtab, &iBaseDiff) )
+		return 1;
+
+	unsigned nbuckets = pBuckets[0];
+//	int iSymCount = pBuckets[1];
+	pBuckets = &pBuckets[2];
+	
+	const PREF(Word)*	pChains = &pBuckets[ nbuckets ];
+	assert(pChains);
+
+	// Get hash
+	unsigned iNameHash = ElfHashString(Name);
+	iNameHash %= nbuckets;
+
+	// Walk Chain
+	unsigned idx = pBuckets[ iNameHash ];
+	do {
+		const PREF(Sym)*	sym = &symtab[idx];
+		assert(sym);
+		if( strcmp(dynstrtab + sym->st_name, Name) == 0 )
+		{
+			TRACE("*sym = {value:0x%x,size:0x%x,info:0x%x,other:0x%x,shndx:%i}",
+				sym->st_value, sym->st_size, sym->st_info,
+				sym->st_other, sym->st_shndx);
+			if(Addr)	*Addr = (void*)(intptr_t)( sym->st_value );
+			if(Size)	*Size = sym->st_size;
+			if(Binding)	*Binding = ELF32_ST_BIND(sym->st_info);
+			if(Type)	*Type = ELF32_ST_TYPE(sym->st_info);
+			if(Section)	*Section = sym->st_shndx;
+			return 0;
+		}
+	} while( (idx = pChains[idx]) != STN_UNDEF && idx != pBuckets[iNameHash] );
+	
+	TRACE("No symbol");
+	return 1;
+}
+
+int PREF(GetSymbol)(void *Base, const char *Name, void **ret, size_t *Size)
+{
+	 int	section, binding;
+	TRACE("%s(%p,%s,...)", __func__, Base, Name);
+	if( PREF(GetSymbolInfo)(Base, Name, ret, Size, &section, &binding, NULL) )
+		return 0;
+	if( section == SHN_UNDEF ) {
+		TRACE("%s: Undefined %p", __func__, *ret, (Size?*Size:0), section);
+		return 0;
+	}
+	if( binding == STB_WEAK ) {
+		TRACE("%s: Weak, return %p+0x%x,section=%i", __func__, *ret, (Size?*Size:0), section);
+		return 2;
+	}
+	TRACE("%s: Found %p+0x%x,section=%i", __func__, *ret, (Size?*Size:0), section);
+	return 1;
+}
diff --git a/Usermode/Libraries/ld-acess.so_src/export.c b/Usermode/Libraries/ld-acess.so_src/export.c
index 10c5c5cd5acbadfed08629fadf65a9bb2d9b1134..e40dd8eb250324624397de216db9f8a0dda881c6 100644
--- a/Usermode/Libraries/ld-acess.so_src/export.c
+++ b/Usermode/Libraries/ld-acess.so_src/export.c
@@ -11,6 +11,7 @@ extern int32_t	__modsi3(int32_t Num, int32_t Den);
 extern uint32_t	__udivsi3(uint32_t Num, uint32_t Den);
 extern uint32_t	__umodsi3(uint32_t Num, uint32_t Den);
 extern void	ldacess_DumpLoadedLibraries(void);
+extern void	_ZN4_sys5debugEPKcz(const char *,...);	// C++ "_sys::debug" used by STL debug
 
 #define _STR(x)	#x
 #define STR(x)	_STR(x)
diff --git a/Usermode/Libraries/ld-acess.so_src/include_exp/acess/_native_syscallmod.h b/Usermode/Libraries/ld-acess.so_src/include_exp/acess/_native_syscallmod.h
index 9da4e433e026e1c50ff3ac93367eb68b54b92cb2..b3d78bf1359f48e6ba76bf43565039052c6dfe21 100644
--- a/Usermode/Libraries/ld-acess.so_src/include_exp/acess/_native_syscallmod.h
+++ b/Usermode/Libraries/ld-acess.so_src/include_exp/acess/_native_syscallmod.h
@@ -24,6 +24,7 @@
 #define _SysUnloadBin	acess__SysUnloadBin
 #define _SysSetFaultHandler	acess__SysSetFaultHandler
 #define _SysDebug	acess__SysDebug
+#define _SysDebugHex	acess__SysDebugHex
 #define _SysGetPhys	acess__SysGetPhys
 #define _SysAllocate	acess__SysAllocate
 #define _SysSetMemFlags	acess__SysSetMemFlags
@@ -34,6 +35,7 @@
 #define _SysFDFlags	acess__SysFDFlags
 #define _SysClose	acess__SysClose
 #define _SysRead	acess__SysRead
+#define _SysTruncate	acess__SysTruncate
 #define _SysWrite	acess__SysWrite
 #define _SysSeek	acess__SysSeek
 #define _SysTell	acess__SysTell
@@ -46,5 +48,9 @@
 #define _SysSelect	acess__SysSelect
 #define _SysMkDir	acess__SysMkDir
 #define _SysUnlink	acess__SysUnlink
+#define _SysMMap	acess__SysMMap
+#define _SysMUnMap	acess__SysMUnMap
+#define _SysMarshalFD	acess__SysMarshalFD
+#define _SysUnMarshalFD	acess__SysUnMarshalFD
 
 #define _errno	acess__errno
diff --git a/Usermode/Libraries/ld-acess.so_src/include_exp/acess/devices/joystick.h b/Usermode/Libraries/ld-acess.so_src/include_exp/acess/devices/joystick.h
new file mode 100644
index 0000000000000000000000000000000000000000..e5d670f20ef6e404348f15964d485432fdae3b00
--- /dev/null
+++ b/Usermode/Libraries/ld-acess.so_src/include_exp/acess/devices/joystick.h
@@ -0,0 +1,47 @@
+/*
+ * Acess2 System Calls
+ * - By John Hodge (thePowersGang)
+ *
+ * acess/devices/joystick.h
+ * - Joystick IOCtls and structures
+ */
+#ifndef _SYS_DEVICES_JOYSTICK_H
+#define _SYS_DEVICES_JOYSTICK_H
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+struct mouse_attribute
+{
+	uint32_t	Num;
+	uint32_t	Value;
+};
+
+struct mouse_header
+{
+	uint16_t	NAxies;
+	uint16_t	NButtons;
+};
+
+struct mouse_axis
+{
+	 int16_t	MinValue;
+	 int16_t	MaxValue;
+	 int16_t	CurValue;
+	uint16_t	CursorPos;
+};
+
+enum {
+	JOY_IOCTL_GETSETAXISLIMIT = 6,
+	JOY_IOCTL_GETSETAXISPOSITION,
+};
+
+#if __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/Usermode/Libraries/ld-acess.so_src/include_exp/acess/devices/pty_cmds.h b/Usermode/Libraries/ld-acess.so_src/include_exp/acess/devices/pty_cmds.h
index 2ec0cd077af769b98b2ffba67c61432bd3b5db8e..fde5d837c7ba30458d52af85da2f52ca43c11daa 100644
--- a/Usermode/Libraries/ld-acess.so_src/include_exp/acess/devices/pty_cmds.h
+++ b/Usermode/Libraries/ld-acess.so_src/include_exp/acess/devices/pty_cmds.h
@@ -25,6 +25,7 @@ enum
 struct ptycmd_header
 {
 	uint8_t 	cmd;
+	// NOTE: Length is encoded as a count of 32-bit words
 	uint8_t 	len_low;
 	uint16_t	len_hi;
 } PACKED;
@@ -44,5 +45,11 @@ struct ptycmd_setcursorbmp
 	char	data[];
 } PACKED;
 
+struct ptycmd_senddata
+{
+	struct ptycmd_header	hdr;
+	uint32_t	ofs;
+} PACKED;
+
 #endif
 
diff --git a/Usermode/Libraries/ld-acess.so_src/include_exp/acess/sys.h b/Usermode/Libraries/ld-acess.so_src/include_exp/acess/sys.h
index d32f8c33b2e09d096be41cc7cc832e60a56cbc76..4e0f14524069191b98641b71e5f69a6dea8a435a 100644
--- a/Usermode/Libraries/ld-acess.so_src/include_exp/acess/sys.h
+++ b/Usermode/Libraries/ld-acess.so_src/include_exp/acess/sys.h
@@ -40,6 +40,14 @@ extern "C" {
 #define FILEFLAG_SYMLINK	0x20
 #define CLONE_VM	0x10
 
+#define MMAP_PROT_READ	0x001	//!< Readable memory
+#define MMAP_PROT_WRITE	0x002	//!< Writable memory
+#define MMAP_PROT_EXEC	0x004	//!< Executable memory
+#define MMAP_MAP_SHARED 	0x001	//!< Shared with all other users of the FD
+#define MMAP_MAP_PRIVATE	0x002	//!< Local (COW) copy
+#define MMAP_MAP_FIXED  	0x004	//!< Load to a fixed address
+#define MMAP_MAP_ANONYMOUS	0x008	//!< Not associated with a FD
+
 #ifdef ARCHDIR_is_native
 # include "_native_syscallmod.h"
 #endif
@@ -51,6 +59,7 @@ extern int	_errno;
 
 // === FUNCTIONS ===
 extern void	_SysDebug(const char *format, ...);
+extern void	_SysDebugHex(const char *Label, const void *Data, size_t Size);
 // --- Proc ---
 extern void	_exit(int status)	__attribute__((noreturn));
 extern int	_SysKill(int pid, int sig);
@@ -88,9 +97,12 @@ extern int	_SysReopen(int fd, const char *path, int flags);
 extern int	_SysCopyFD(int srcfd, int dstfd);
 extern int	_SysFDFlags(int fd, int mask, int newflags);
 extern size_t	_SysRead(int fd, void *buffer, size_t length);
+extern size_t	_SysReadAt(int fd, uint64_t offset, size_t length, void *buffer);
+extern uint64_t	_SysTruncate(int fd, uint64_t size);
 extern int	_SysClose(int fd);
 extern int	_SysFDCtl(int fd, int option, ...);
 extern size_t	_SysWrite(int fd, const void *buffer, size_t length);
+extern size_t	_SysWriteAt(int fd, uint64_t offset, size_t length, const void *buffer);
 extern int	_SysSeek(int fd, int64_t offset, int whence);
 extern uint64_t	_SysTell(int fd);
 extern int	_SysIOCtl(int fd, int id, void *data);
@@ -102,6 +114,16 @@ extern int	_SysSelect(int nfds, fd_set *read, fd_set *write, fd_set *err, int64_
 //#define select(nfs, rdfds, wrfds, erfds, timeout)	_SysSelect(nfs, rdfds, wrfds, erfds, timeout, 0)
 extern int	_SysMkDir(const char *dirname);
 extern int	_SysUnlink(const char *pathname);
+extern void*	_SysMMap(void *addr, size_t length, unsigned int _flags, int fd, uint64_t offset);
+#ifdef _SysMMap
+# undef _SysMMap
+# define _SysMMap(addr,length,flags,prot,fd,offset)	acess__SysMMap(addr,length,(flags|(prot<<16)), fd, offset)
+#else
+# define _SysMMap(addr,length,flags,prot,fd,offset)	_SysMMap(addr,length,(flags|(prot<<16)), fd, offset)
+#endif
+extern int	_SysMUnMap(void *addr, size_t length);
+extern uint64_t	_SysMarshalFD(int FD);
+extern int	_SysUnMarshalFD(uint64_t Handle);
 
 // --- IPC ---
 extern int	_SysSendMessage(int dest, size_t length, const void *Data);
diff --git a/Usermode/Libraries/ld-acess.so_src/loadlib.c b/Usermode/Libraries/ld-acess.so_src/loadlib.c
index 88aafc31a98010ebbed0ec94810612be8d0e04c1..31dcbd8acfa23840d954df1e2b555c7a78fee44f 100644
--- a/Usermode/Libraries/ld-acess.so_src/loadlib.c
+++ b/Usermode/Libraries/ld-acess.so_src/loadlib.c
@@ -4,6 +4,7 @@
 */
 #include "common.h"
 #include <stdint.h>
+#include <stdbool.h>
 #include <acess/sys.h>
 
 #define DEBUG	0
@@ -14,8 +15,7 @@
 # define DEBUGS(v...)	
 #endif
 
-// === PROTOTYPES ===
-void	*IsFileLoaded(const char *file);
+#define MAX_QUEUED_ENTRYPOINTS	8
 
 // === IMPORTS ===
 extern const struct {
@@ -26,10 +26,21 @@ extern const int	ciNumLocalExports;
 extern char	**gEnvP;
 extern char	gLinkedBase[];
 
+// === TYPES ===
+typedef void	tLibEntry(void *, int, char *[], char**);
+
+// === PROTOTYPES ===
+void	*IsFileLoaded(const char *file);
+
 // === GLOABLS ===
 tLoadedLib	gLoadedLibraries[MAX_LOADED_LIBRARIES];
 char	gsLoadedStrings[MAX_STRINGS_BYTES];
 char	*gsNextAvailString = gsLoadedStrings;
+struct sQueuedEntry {
+	void	*Base;
+	tLibEntry	*Entry;
+}	gaQueuedEntrypoints[MAX_QUEUED_ENTRYPOINTS];
+ int	giNumQueuedEntrypoints;
 //tLoadLib	*gpLoadedLibraries = NULL;
 
 // === CODE ===
@@ -45,6 +56,20 @@ void ldacess_DumpLoadedLibraries(void)
 	}
 }
 
+/**
+ * \brief Call queued up entry points (after relocations completed) 
+ */
+void CallQueuedEntrypoints(char **EnvP)
+{
+	while( giNumQueuedEntrypoints )
+	{
+		giNumQueuedEntrypoints --;
+		const struct sQueuedEntry	*qe = &gaQueuedEntrypoints[giNumQueuedEntrypoints];
+		DEBUGS("Calling EP %p for %p", qe->Entry, qe->Base);
+		qe->Entry(qe->Base, 0, NULL, EnvP);
+	}
+}
+
 const char *FindLibrary(char *DestBuf, const char *SoName, const char *ExtraSearchDir)
 {	
 	// -- #1: Executable Specified
@@ -72,14 +97,12 @@ const char *FindLibrary(char *DestBuf, const char *SoName, const char *ExtraSear
 void *LoadLibrary(const char *SoName, const char *SearchDir, char **envp)
 {
 	char	sTmpName[1024];
-	const char	*filename;
 	void	*base;
-	void	(*fEntry)(void *, int, char *[], char**);
 	
 	DEBUGS("LoadLibrary: (SoName='%s', SearchDir='%s', envp=%p)", SoName, SearchDir, envp);
 	
 	// Create Temp Name
-	filename = FindLibrary(sTmpName, SoName, SearchDir);
+	const char *filename = FindLibrary(sTmpName, SoName, SearchDir);
 	if(filename == NULL) {
 		DEBUGS("LoadLibrary: RETURN 0");
 		return 0;
@@ -91,6 +114,7 @@ void *LoadLibrary(const char *SoName, const char *SearchDir, char **envp)
 
 	DEBUGS(" LoadLibrary: SysLoadBin()");	
 	// Load Library
+	tLibEntry	*fEntry;
 	base = _SysLoadBin(filename, (void**)&fEntry);
 	if(!base) {
 		DEBUGS("LoadLibrary: RETURN 0");
@@ -106,10 +130,17 @@ void *LoadLibrary(const char *SoName, const char *SearchDir, char **envp)
 	}
 	
 	// Call Entrypoint
-	DEBUGS(" LoadLibrary: '%s' Entry %p", SoName, fEntry);
-	fEntry(base, 0, NULL, gEnvP);
+	// - TODO: Queue entrypoint calls
+	if( giNumQueuedEntrypoints >= MAX_QUEUED_ENTRYPOINTS ) {
+		SysDebug("ERROR - Maximum number of queued entrypoints exceeded on %p '%s'",
+			base, SoName);
+		return 0;
+	}
+	gaQueuedEntrypoints[giNumQueuedEntrypoints].Base  = base;
+	gaQueuedEntrypoints[giNumQueuedEntrypoints].Entry = fEntry;
+	giNumQueuedEntrypoints ++;
 	
-	DEBUGS("LoadLibrary: RETURN 1");
+	DEBUGS("LoadLibrary: RETURN success");
 	return base;
 }
 
@@ -223,34 +254,50 @@ void Unload(void *Base)
  \fn Uint GetSymbol(const char *name)
  \brief Gets a symbol value from a loaded library
 */
-int GetSymbol(const char *name, void **Value, size_t *Size)
+int GetSymbol(const char *name, void **Value, size_t *Size, void *IgnoreBase)
 {
-	 int	i;
-	
-	//SysDebug("ciNumLocalExports = %i", ciNumLocalExports);
-	for(i=0;i<ciNumLocalExports;i++)
+	//SysDebug("GetSymbol: (%s)");
+	for( int i = 0; i < ciNumLocalExports; i ++ )
 	{
 		if( strcmp(caLocalExports[i].Name, name) == 0 ) {
 			*Value = caLocalExports[i].Value;
 			if(Size)
 				*Size = 0;
+			//SysDebug("GetSymbol: Local %p+0x%x", *Value, 0);
 			return 1;
 		}
 	}
-	
-	// Entry 0 is ld-acess, ignore it
-	for(i = 0; i < MAX_LOADED_LIBRARIES; i ++)
+
+	bool have_weak = false;	
+	for(int i = 0; i < MAX_LOADED_LIBRARIES && gLoadedLibraries[i].Base != 0; i ++)
 	{
-		if(gLoadedLibraries[i].Base == 0)
-			break;
+		// Allow ignoring the current module
+		if( gLoadedLibraries[i].Base == IgnoreBase ) {
+			//SysDebug("GetSymbol: Ignore %p", gLoadedLibraries[i].Base);
+			continue ;
+		}
 		
 		//SysDebug(" GetSymbol: Trying 0x%x, '%s'",
 		//	gLoadedLibraries[i].Base, gLoadedLibraries[i].Name);
-		if(GetSymbolFromBase(gLoadedLibraries[i].Base, name, Value, Size))
-			return 1;
+		void	*tmpval;
+		size_t	tmpsize;
+		int rv = GetSymbolFromBase(gLoadedLibraries[i].Base, name, &tmpval, &tmpsize);
+		if(rv)
+		{
+			*Value = tmpval;
+			*Size = tmpsize;
+			if( rv == 1 ) {
+				return 1;
+			}
+			have_weak = true;
+		}
+	}
+	if(have_weak) {
+		return 2;
+	}
+	else {
+		return 0;
 	}
-	SysDebug("GetSymbol: === Symbol '%s' not found ===", name);
-	return 0;
 }
 
 /**
diff --git a/Usermode/Libraries/ld-acess.so_src/main.c b/Usermode/Libraries/ld-acess.so_src/main.c
index 40c603ee2f94988b8017d7691b982499deab98ed..976ceec445a3c6483d3020ca50129d93d14290a5 100644
--- a/Usermode/Libraries/ld-acess.so_src/main.c
+++ b/Usermode/Libraries/ld-acess.so_src/main.c
@@ -5,6 +5,7 @@
 #include <stdint.h>
 #include <stddef.h>
 #include "common.h"
+#undef SoMain
 
 // === PROTOTYPES ===
 void	*DoRelocate(void *base, char **envp, const char *Filename);
@@ -14,6 +15,7 @@ void	*DoRelocate(void *base, char **envp, const char *Filename);
 extern char	gLinkedBase[];
 char	**gEnvP;
 extern int	memcmp(const void *m1, const void *m2, size_t size);
+extern void	CallQueuedEntrypoints(char **EnvP);
  
 // === CODE ===
 /**
@@ -41,7 +43,6 @@ void *SoMain(void *base, int argc, char **argv, char **envp)
 	}
 
 	// Otherwise do relocations
-	//ret = DoRelocate( base, envp, "Executable" );
 	ret = DoRelocate( base, NULL, "Executable" );
 	if( ret == 0 ) {
 		SysDebug("ld-acess - SoMain: Relocate failed, base=0x%x\n", base);
@@ -49,7 +50,10 @@ void *SoMain(void *base, int argc, char **argv, char **envp)
 		for(;;);
 	}
 
-	SysDebug("ld-acess - SoMain: ret = %p", ret);	
+	// Call queued entry points (from libraries)
+	CallQueuedEntrypoints(envp);
+
+	SysDebug("ld-acess - SoMain: Program entry %p", ret);	
 	return ret;
 }
 
diff --git a/Usermode/Libraries/libaxwin4.so_src/Makefile b/Usermode/Libraries/libaxwin4.so_src/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..c530c558a4e110b9f66a94d6b99ee38f6f9b66fd
--- /dev/null
+++ b/Usermode/Libraries/libaxwin4.so_src/Makefile
@@ -0,0 +1,29 @@
+# Acess2 AxWin4 Library
+# Makefile
+
+-include ../Makefile.cfg
+
+AXWIN4DIR := ../../Applications/axwin4_src/
+
+CPPFLAGS += -I$(AXWIN4DIR)Common/include/
+CFLAGS   += -Wextra
+CXXFLAGS +=
+ASFLAGS  +=
+LDFLAGS  += -soname libaxwin4.so -Map map.txt
+LIBS  += -lc -lc++
+
+OBJ  = main.o ipc.o ipc_acessipcpipe.o
+OBJ += wm.o window_drawing.o
+OBJ += Common__serialisation.o
+BIN = libaxwin4.so
+
+include ../Makefile.tpl
+
+$(_OBJPREFIX)Common__%.o: $(AXWIN4DIR)/Common/%.cpp
+	@echo [CXX] -o $@
+	@mkdir -p $(dir $@)
+	$V$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ -MQ $@ -MP -MD -MF $(@:%=%.dep)
+
+
+
+
diff --git a/Usermode/Libraries/libaxwin4.so_src/include/CIPCChannel_AcessIPCPipe.hpp b/Usermode/Libraries/libaxwin4.so_src/include/CIPCChannel_AcessIPCPipe.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d04d0524a7d6ab4ea838d553e520d4ca5d52ebcd
--- /dev/null
+++ b/Usermode/Libraries/libaxwin4.so_src/include/CIPCChannel_AcessIPCPipe.hpp
@@ -0,0 +1,30 @@
+/*
+ * Acess2 GUIv4 Library
+ * - By John Hodge (thePowersGang)
+ *
+ * CIPCChannel_AcessIPCPipe.h
+ * - Acess IPC Datagram pipe
+ */
+#ifndef _LIBAXWIN4_CIPCCHANNEL_ACESSIPCPIPE_H_
+#define _LIBAXWIN4_CIPCCHANNEL_ACESSIPCPIPE_H_
+
+#include "IIPCChannel.hpp"
+
+namespace AxWin {
+
+class CIPCChannel_AcessIPCPipe:
+	public IIPCChannel
+{
+	 int	m_fd;
+public:
+	CIPCChannel_AcessIPCPipe(const char *Path);
+	virtual ~CIPCChannel_AcessIPCPipe();
+	virtual int	FillSelect(fd_set& fds);
+	virtual bool	HandleSelect(const fd_set& fds);
+	virtual void	Send(CSerialiser& message);
+};
+
+};	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Libraries/libaxwin4.so_src/include/IIPCChannel.hpp b/Usermode/Libraries/libaxwin4.so_src/include/IIPCChannel.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c4a514f7da3c061b2722a27915c33253dffd4621
--- /dev/null
+++ b/Usermode/Libraries/libaxwin4.so_src/include/IIPCChannel.hpp
@@ -0,0 +1,33 @@
+/*
+ * Acess2 GUIv4 Library
+ * - By John Hodge (thePowersGang)
+ *
+ * IIPCChannel.h
+ * - IPC Channel interface
+ */
+#ifndef _LIBAXWIN4_IIPCCHANNEL_H_
+#define _LIBAXWIN4_IIPCCHANNEL_H_
+
+#include <serialisation.hpp>
+
+#include <acess/sys.h>
+
+namespace AxWin {
+
+class IIPCChannel
+{
+public:
+	virtual ~IIPCChannel();
+	virtual int	FillSelect(fd_set& fds) = 0;
+	/**
+	 * \return False if the connection has dropped/errored
+	 */
+	virtual bool	HandleSelect(const fd_set& fds) = 0;
+	
+	virtual void	Send(CSerialiser& message) = 0;
+};
+
+};	// namespace AxWin
+
+#endif
+
diff --git a/Usermode/Libraries/libaxwin4.so_src/include/common.hpp b/Usermode/Libraries/libaxwin4.so_src/include/common.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e3aa80743f9d1009954f5d33a7dcf30eacf0a4af
--- /dev/null
+++ b/Usermode/Libraries/libaxwin4.so_src/include/common.hpp
@@ -0,0 +1,29 @@
+/*
+ * Acess2 GUIv4 Library
+ * - By John Hodge (thePowersGang)
+ *
+ * common.h
+ * - Library internal header
+ */
+#ifndef _LIBAXWIN4_COMMON_H_
+#define _LIBAXWIN4_COMMON_H_
+
+#include <serialisation.hpp>
+
+namespace AxWin {
+
+extern void	SendMessage(CSerialiser& message);
+extern void	RecvMessage(CDeserialiser& message);
+extern CDeserialiser	GetSyncReply(CSerialiser& request, unsigned int Message);
+
+};
+
+struct sAxWin4_Window
+{
+	unsigned int	m_id;
+	 int	m_fd;
+	void	*m_buffer;
+};
+
+#endif
+
diff --git a/Usermode/Libraries/libaxwin4.so_src/include_exp/axwin4/axwin.h b/Usermode/Libraries/libaxwin4.so_src/include_exp/axwin4/axwin.h
new file mode 100644
index 0000000000000000000000000000000000000000..255b0e1dbc6a343b239429bf9c0506cc00a4ac81
--- /dev/null
+++ b/Usermode/Libraries/libaxwin4.so_src/include_exp/axwin4/axwin.h
@@ -0,0 +1,88 @@
+/*
+ * Acess2 GUIv4 (AxWin4)
+ * - By John Hodge (thePowersGang)
+ *
+ * axwin4/axwin.h
+ * - Client library interface header
+ */
+#ifndef _LIBAXWIN4_AXWIN4_AXWIN_H_
+#define _LIBAXWIN4_AXWIN4_AXWIN_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <acess/sys.h>
+
+typedef struct sAxWin4_Window	tAxWin4_Window;
+
+// - Abstractions of core IPC methods
+extern bool	AxWin4_Connect(const char *URI);
+
+extern bool	AxWin4_WaitEventQueue(uint64_t Timeout);	
+extern bool	AxWin4_WaitEventQueueSelect(int nFDs, fd_set *rfds, fd_set *wfds, fd_set *efds, uint64_t Timeout);
+
+extern void	AxWin4_GetScreenDimensions(unsigned int ScreenIndex, unsigned int *Width, unsigned int *Height);
+
+extern tAxWin4_Window	*AxWin4_CreateWindow(const char *Name);
+extern void	AxWin4_DestroyWindow(tAxWin4_Window *Window);
+
+// Callbacks
+typedef int tAxWin4_KeyCallback(tAxWin4_Window* Winow, unsigned int Key, const char *Translated);
+extern void	AxWin4_SetCallback_Key(tAxWin4_Window* Window, tAxWin4_KeyCallback* cb);
+typedef int tAxWin4_MouseBtnCallback(tAxWin4_Window* Winow, unsigned int Mouse, unsigned int X, unsigned int Y, unsigned int Button, bool IsPress);
+extern void	AxWin4_SetCallback_MouseBtn(tAxWin4_Window* Window, tAxWin4_MouseBtnCallback* cb);
+typedef int tAxWin4_MouseMoveCallback(tAxWin4_Window* Winow, unsigned int Mouse, unsigned int X, unsigned int Y);
+
+extern void	AxWin4_ShowWindow(tAxWin4_Window *Window, bool Shown);
+extern void	AxWin4_SetWindowFlags(tAxWin4_Window *Window, unsigned int NewFlags);
+extern void	AxWin4_SetTitle(tAxWin4_Window *Window, const char *Title);
+extern void	AxWin4_MoveWindow(tAxWin4_Window *Window, int X, int Y);
+extern void	AxWin4_ResizeWindow(tAxWin4_Window *Window, unsigned int W, unsigned int H);
+
+extern void	AxWin4_DamageRect(tAxWin4_Window *Window, unsigned int X, unsigned int Y, unsigned int W, unsigned int H);
+extern void*	AxWin4_GetWindowBuffer(tAxWin4_Window *Window);
+
+/**
+ * \brief Set the render clipping region. Any attempts to render outside this area will silently fail
+ * \param Window	Target window
+ *
+ * \note Allows clipping other render functions to avoid excessive redraws
+ * \note Cleared when \a AxWin4_DamageRect is called, or when called with a zero width or height
+ */
+extern void	AxWin4_SetRenderClip(tAxWin4_Window *Window, int X, int Y, unsigned int W, unsigned int H);
+
+/**
+ * \brief Draw a user-supplied bitmap to the window
+ * \param Data	Bitmap data in the same format as the window's back buffer
+ * \note VERY SLOW
+ */
+extern void	AxWin4_DrawBitmap(tAxWin4_Window *Window, int X, int Y, unsigned int W, unsigned int H, void *Data);
+/**
+ * \brief Draw a "control" to the window
+ * \param Window	Target window
+ * \param X	Destination X
+ * \param Y	Destination Y
+ * \param W	Control width
+ * \param H	Control height
+ * \param ControlID	Specifies which control to use. Can be a global or application-registered (See eAxWin4_GlobalControls)
+ * \param Frame	Control frame number. Used to specify a variant of the control (e.g. hovered/pressed)
+ *
+ * Controls are server-side bitmaps that can be arbitarily scaled to fit a region.
+ */
+extern void	AxWin4_DrawControl(tAxWin4_Window *Window, int X, int Y, unsigned int W, unsigned int H, uint16_t ControlID, unsigned int Frame);
+
+extern void	AxWin4_FillRect(tAxWin4_Window *Window, int X, int Y, unsigned int W, unsigned int H, uint32_t Colour);
+
+extern void	AxWin4_DrawText(tAxWin4_Window *Window, int X, int Y, unsigned int W, unsigned int H, uint16_t FontID, const char *String);
+
+#include "definitions.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/Usermode/Libraries/libaxwin4.so_src/include_exp/axwin4/definitions.h b/Usermode/Libraries/libaxwin4.so_src/include_exp/axwin4/definitions.h
new file mode 100644
index 0000000000000000000000000000000000000000..5fe7e4db908a29435790490c1af061cb4cbfd559
--- /dev/null
+++ b/Usermode/Libraries/libaxwin4.so_src/include_exp/axwin4/definitions.h
@@ -0,0 +1,38 @@
+/*
+ * Acess2 GUIv4 (AxWin4)
+ * - By John Hodge (thePowersGang)
+ *
+ * axwin4/definitions.h
+ * - Shared definitions (Client and server)
+ */
+#ifndef _LIBAXWIN4_AXWIN4_DEFINITIONS_H_
+#define _LIBAXWIN4_AXWIN4_DEFINITIONS_H_
+
+/**
+ * \name Window Flags
+ * \{
+ */
+#define AXWIN4_WNDFLAG_NODECORATE	0x01	//!< Disable automatic inclusion of window decorations
+#define AXWIN4_WNDFLAG_KEEPBELOW	0x02	//!< Keep the window below all others, even when it has focus
+#define AXWIN4_WNDFLAG_KEEPABOVE	0x04	//!< Keep window above all others, ecen when it loses focus
+/**
+ * \}
+ */
+
+/**
+ * \brief Global controls
+ */
+enum eAxWin4_GlobalControls {
+	AXWIN4_CTL_BUTTON,	//!< Standard button (possibly rounded edges)
+	AXWIN4_CTL_BOX, 	//!< Grouping box in a window
+	AXWIN4_CTL_TOOLBAR,	//!< Toolbar (raised region)
+	AXWIN4_CTL_TEXTBOX,	//!< Text edit box
+};
+
+enum eAxWin4_GlobalFonts {
+	AXWIN4_FONT_DEFAULT,	//!< Default font (usually a sans-serif)
+	AXWIN4_FONT_MONOSPACE,	//!< Default monospace font
+};
+
+#endif
+
diff --git a/Usermode/Libraries/libaxwin4.so_src/ipc.cpp b/Usermode/Libraries/libaxwin4.so_src/ipc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d0455f53a8a673607d2dd42d591defdf85f6d2f7
--- /dev/null
+++ b/Usermode/Libraries/libaxwin4.so_src/ipc.cpp
@@ -0,0 +1,178 @@
+/*
+ * AxWin4 Interface Library
+ * - By John Hodge (thePowersGang)
+ *
+ * ipc.c
+ * - IPC Abstraction
+ */
+#include <axwin4/axwin.h>
+#include "include/common.hpp"
+#include "include/IIPCChannel.hpp"
+#include "include/CIPCChannel_AcessIPCPipe.hpp"
+#include <ipc_proto.hpp>
+#include <algorithm>
+#include <mutex>
+#include <stdexcept>
+
+#include <cstring>
+#include <cstdio>
+
+namespace AxWin {
+
+IIPCChannel*	gIPCChannel;
+::std::mutex	glSyncReply;
+bool	gSyncReplyActive;
+bool	gSyncReplyValid;
+CDeserialiser	gSyncReplyBuf;
+
+extern "C" bool AxWin4_Connect(const char *URI)
+{
+	_SysDebug("AxWin4_Connect('%s')", URI);
+	if( gIPCChannel ) {
+		return false;
+	}
+	try {
+		if( strncmp(URI, "ipcpipe://", 3+4+3) == 0 )
+		{
+			gIPCChannel = new CIPCChannel_AcessIPCPipe(URI+3+4+3);
+		}
+		else
+		{
+			_SysDebug("Unknown protocol");
+			return false;
+		}
+	}
+	catch( const ::std::exception& e )
+	{
+		fprintf(stderr, "AxWin4_Connect: %s\n", e.what());
+		return false;
+	}
+	return true;
+}
+
+extern "C" bool AxWin4_PeekEventQueue(void)
+{
+	return false;
+}
+
+extern "C" bool AxWin4_WaitEventQueue(uint64_t Timeout)
+{
+	return AxWin4_WaitEventQueueSelect(0, NULL, NULL, NULL, Timeout);
+}
+
+extern "C" bool AxWin4_WaitEventQueueSelect(int nFDs, fd_set *rfds, fd_set *wfds, fd_set *efds, uint64_t Timeout)
+{
+	fd_set	local_rfds;
+	if( !rfds ) {
+		FD_ZERO(&local_rfds);
+		rfds = &local_rfds;
+	}
+	
+	int64_t	select_timeout = Timeout;
+	int64_t	*select_timeout_p = (Timeout ? &select_timeout : 0);
+	
+	nFDs = ::std::max(nFDs, gIPCChannel->FillSelect(*rfds));
+	_SysSelect(nFDs, rfds, wfds, efds, select_timeout_p, 0);
+	return gIPCChannel->HandleSelect(*rfds);
+}
+
+void SendMessage(CSerialiser& message)
+{
+	gIPCChannel->Send(message);
+}
+void RecvMessage(CDeserialiser& message)
+{
+	uint8_t id = message.ReadU8();
+	_SysDebug("RecvMessage: id=%i", id);
+	switch(id)
+	{
+	case IPCMSG_PING:
+		// If we hear ping, we must pong
+		{
+		CSerialiser	pong;
+		pong.WriteU8(IPCMSG_REPLY);
+		pong.WriteU8(IPCMSG_PING);
+		SendMessage(pong);
+		}
+		break;
+	case IPCMSG_REPLY:
+		// Flag reply and take a copy of this message
+		if( !gSyncReplyActive )
+		{
+			_SysDebug("Unexpected reply message %i", message.ReadU8());
+		}
+		else if( gSyncReplyValid )
+		{
+			// Oh... that was unexpected
+			_SysDebug("Unexpected second reply message %i", message.ReadU8());
+		}
+		else
+		{
+			gSyncReplyValid = true;
+			gSyncReplyBuf = message;
+		}
+		break;
+	// TODO: Handle messages from server (input events, IPC)
+	// TODO: If an event is currently being processed, save the message in a queue to be handled when processing is complete
+	// - This will prevent deep recursion (and make server errors aparent)
+	case IPCMSG_INPUTEVENT:
+		_SysDebug("TODO: Input events");
+		break;
+	default:
+		_SysDebug("TODO: RecvMessage(%i)", id);
+		break;
+	}
+}
+
+CDeserialiser GetSyncReply(CSerialiser& request, unsigned int Message)
+{
+	::std::lock_guard<std::mutex>	lock(glSyncReply);
+	gSyncReplyActive = true;
+	gSyncReplyValid = false;
+	// Send once lock is acquired
+	SendMessage(request);
+	
+	while( !gSyncReplyValid )
+	{
+		// Tick along
+		if( !AxWin4_WaitEventQueue(0) )
+			throw ::std::runtime_error("Connection dropped while waiting for reply");
+	}
+	gSyncReplyActive = false;
+	
+	uint8_t id = gSyncReplyBuf.ReadU8();
+	if( id != Message )
+	{
+		_SysDebug("Unexpected reply message id=%i, expected %i",
+			id, Message);
+		throw ::std::runtime_error("Sequencing error, unexpected reply");
+	}
+	
+	// Use move to avoid copying
+	return ::std::move(gSyncReplyBuf);
+}
+
+extern "C" void AxWin4_GetScreenDimensions(unsigned int ScreenIndex, unsigned int *Width, unsigned int *Height)
+{
+	CSerialiser	req;
+	req.WriteU8(IPCMSG_GETGLOBAL);
+	req.WriteU16(IPC_GLOBATTR_SCREENDIMS);
+	req.WriteU8(ScreenIndex);
+	
+	CDeserialiser	response = GetSyncReply(req, IPCMSG_GETGLOBAL);
+	if( response.ReadU16() != IPC_GLOBATTR_SCREENDIMS ) {
+		
+	}
+	
+	*Width = response.ReadU16();
+	*Height = response.ReadU16();
+	
+	_SysDebug("AxWin4_GetScreenDimensions: %i = %ix%i", ScreenIndex, *Width, *Height);
+}
+
+IIPCChannel::~IIPCChannel()
+{
+}
+
+};	// namespace AxWin
+
diff --git a/Usermode/Libraries/libaxwin4.so_src/ipc_acessipcpipe.cpp b/Usermode/Libraries/libaxwin4.so_src/ipc_acessipcpipe.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..87cc6c9315d764ea07f6dfb70977d15163e0612e
--- /dev/null
+++ b/Usermode/Libraries/libaxwin4.so_src/ipc_acessipcpipe.cpp
@@ -0,0 +1,68 @@
+/*
+ * AxWin4 Interface Library
+ * - By John Hodge (thePowersGang)
+ *
+ * ipc_acessipcpipe.c
+ * - Acess2 /Devices/ipcpipe/ IPC Channel
+ */
+#include "include/common.hpp"
+#include "include/CIPCChannel_AcessIPCPipe.hpp"
+#include <system_error>
+#include <cerrno>
+
+namespace AxWin {
+
+CIPCChannel_AcessIPCPipe::CIPCChannel_AcessIPCPipe(const char *Path)
+{
+	m_fd = _SysOpen(Path, OPENFLAG_READ|OPENFLAG_WRITE);
+	if( m_fd == -1 ) {
+		throw ::std::system_error(errno, ::std::system_category());
+	}
+}
+
+CIPCChannel_AcessIPCPipe::~CIPCChannel_AcessIPCPipe()
+{
+}
+
+int CIPCChannel_AcessIPCPipe::FillSelect(fd_set& fds)
+{
+	FD_SET(m_fd, &fds);
+	return m_fd+1;
+}
+
+bool CIPCChannel_AcessIPCPipe::HandleSelect(const fd_set& fds)
+{
+	if( FD_ISSET(m_fd, &fds) )
+	{
+		::std::vector<uint8_t>	rxbuf(4096);
+		size_t len = _SysRead(m_fd, rxbuf.data(), rxbuf.capacity());
+		if( len == (size_t)-1 )
+			throw ::std::system_error(errno, ::std::system_category());
+		rxbuf.resize(len);
+		_SysDebug("CClient_AcessIPCPipe::HandleReceive - Rx %i/%i bytes", len, rxbuf.capacity());
+		//_SysDebugHex("CClient_AcessIPCPipe::HandleReceive", rxbuf.data(), len);
+		
+		CDeserialiser	msg(rxbuf);
+		::AxWin::RecvMessage( msg );
+	}
+	return true;
+}
+
+void CIPCChannel_AcessIPCPipe::Send(CSerialiser& message)
+{
+	const ::std::vector<uint8_t>& serialised = message.Compact();
+	if(serialised.size() > 0x1000 ) {
+		throw ::std::length_error("CIPCChannel_AcessIPCPipe::Send");
+	}
+	_SysDebug("CIPCChannel_AcessIPCPipe::Send(%i bytes)", serialised.size());
+	//_SysDebugHex("CIPCChannel_AcessIPCPipe::Send",     serialised.data(), serialised.size());
+	
+	size_t rv = _SysWrite(m_fd, serialised.data(), serialised.size());
+	if( rv != serialised.size() ) {
+		throw ::std::system_error(errno, ::std::system_category());
+	}
+}
+
+
+};	// namespace AxWin
+
diff --git a/Usermode/Libraries/libaxwin4.so_src/main.c b/Usermode/Libraries/libaxwin4.so_src/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..5d555823fb02d75de5bce64d7a4d4ebd74c5151a
--- /dev/null
+++ b/Usermode/Libraries/libaxwin4.so_src/main.c
@@ -0,0 +1,8 @@
+/*
+ */
+// === CODE ===
+int SoMain(void)
+{
+	return 0;
+}
+
diff --git a/Usermode/Libraries/libaxwin4.so_src/window_drawing.cpp b/Usermode/Libraries/libaxwin4.so_src/window_drawing.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e6a4448ce4cab82dc2165e919e093c7cef458576
--- /dev/null
+++ b/Usermode/Libraries/libaxwin4.so_src/window_drawing.cpp
@@ -0,0 +1,91 @@
+/*
+ * AxWin4 Interface Library
+ * - By John Hodge (thePowersGang)
+ *
+ * window_drawing.cpp
+ * - Window drawing code
+ */
+#include <axwin4/axwin.h>
+#include "include/common.hpp"
+#include <ipc_proto.hpp>
+#include <algorithm>	// min
+
+namespace AxWin {
+
+void _push_data(tAxWin4_Window *Window, int X, int Y, unsigned int W, unsigned int H, const void *Data)
+{
+	CSerialiser	message;
+	//_SysDebug("_push_data - (%i,%i), %ix%i %p", X, Y, W, H, Data);
+	message.WriteU8(IPCMSG_PUSHDATA);
+	message.WriteU16(Window->m_id);
+	message.WriteU16(X);
+	message.WriteU16(Y);
+	message.WriteU16(W);
+	message.WriteU16(H);
+	message.WriteBuffer(H*W*4, Data);
+	::AxWin::SendMessage(message);
+}
+
+extern "C" void AxWin4_DrawBitmap(tAxWin4_Window *Window, int X, int Y, unsigned int W, unsigned int H, void *Data)
+{
+	// TODO: Split message up into blocks such that it can be dispatched
+	if( W > 128 )
+	{
+		const uint32_t*	data32 = static_cast<uint32_t*>(Data);
+		const unsigned int rows_per_message = 1;	// 2048 / W;
+		for( unsigned int row = 0; row < H; row += rows_per_message )
+		{
+			_push_data(Window, X, Y+row, W, ::std::min(rows_per_message,H-row), data32);
+			data32 += W*rows_per_message;
+		}
+	}
+	else
+	{
+		_push_data(Window, X, Y, W, H, Data);
+	}
+}
+
+extern "C" void AxWin4_DrawControl(tAxWin4_Window *Window, int X, int Y, unsigned int W, unsigned int H, uint16_t ID, unsigned int Frame)
+{
+	CSerialiser	message;
+	//_SysDebug("AxWin4_DrawControl: (Window->ID=%i, (%i,%i) %ix%i %i 0x%06x", Window->m_id, X, Y, W, H, ID, Frame);
+	message.WriteU8(IPCMSG_DRAWCTL);
+	message.WriteU16(Window->m_id);
+	message.WriteU16(X);
+	message.WriteU16(Y);
+	message.WriteU16(W);
+	message.WriteU16(H);
+	message.WriteU16(ID);
+	message.WriteU16(Frame);
+	::AxWin::SendMessage(message);
+}
+
+extern "C" void AxWin4_FillRect(tAxWin4_Window *Window, int X, int Y, unsigned int W, unsigned int H, uint32_t Colour)
+{	
+	CSerialiser	message;
+	message.WriteU8(IPCMSG_FILLRECT);
+	message.WriteU16(Window->m_id);
+	message.WriteU16(X);
+	message.WriteU16(Y);
+	message.WriteU16(W);
+	message.WriteU16(H);
+	message.WriteU32(Colour);
+	::AxWin::SendMessage(message);
+}
+
+extern "C" void AxWin4_DrawText(tAxWin4_Window *Window, int X, int Y, unsigned int W, unsigned int H, uint16_t FontID, const char *String)
+{
+	CSerialiser	message;
+	message.WriteU8(IPCMSG_DRAWTEXT);
+	message.WriteU16(Window->m_id);
+	message.WriteU16(X);
+	message.WriteU16(Y);
+	message.WriteU16(W);
+	message.WriteU16(H);
+	message.WriteU16(FontID);
+	message.WriteString(String);
+	::AxWin::SendMessage(message);
+}
+
+};	// namespace AxWin
+
diff --git a/Usermode/Libraries/libaxwin4.so_src/wm.cpp b/Usermode/Libraries/libaxwin4.so_src/wm.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cae79c719a824d9c8005fe683eca2a746c8774ab
--- /dev/null
+++ b/Usermode/Libraries/libaxwin4.so_src/wm.cpp
@@ -0,0 +1,150 @@
+/*
+ * AxWin4 Interface Library
+ * - By John Hodge (thePowersGang)
+ *
+ * wm.cpp
+ * - Window Management
+ */
+#include <axwin4/axwin.h>
+#include "include/common.hpp"
+#include <ipc_proto.hpp>
+#include <vector>
+#include <algorithm>
+#include <mutex>
+#include <cassert>
+
+namespace AxWin {
+
+static const int	MAX_WINDOW_ID = 16;
+static ::std::mutex	glWindowList;
+static ::std::vector<tAxWin4_Window*>	gWindowList;
+
+extern "C" tAxWin4_Window *AxWin4_CreateWindow(const char *Name)
+{
+	// Allocate a window ID
+	::std::lock_guard<std::mutex>	lock(glWindowList);
+	int id = ::std::find(gWindowList.begin(), gWindowList.end(), nullptr) - gWindowList.begin();
+	if( id >= MAX_WINDOW_ID ) {
+		throw ::std::runtime_error("AxWin4_CreateWindow - Out of IDs (TODO: Better exception)");
+	}
+	if( id == gWindowList.size() )
+	{
+		gWindowList.push_back(nullptr);
+	}
+	assert(gWindowList[id] == nullptr);
+	
+	// Create window structure locally
+	tAxWin4_Window *ret = new tAxWin4_Window();
+	gWindowList[id] = ret;
+	ret->m_id = id;
+	ret->m_fd = -1;
+	ret->m_buffer = nullptr;
+	// Request creation of window
+	CSerialiser	message;
+	message.WriteU8(IPCMSG_CREATEWIN);
+	message.WriteU16(ret->m_id);
+	message.WriteString(Name);
+	::AxWin::SendMessage(message);
+	return ret;
+}
+
+extern "C" void AxWin4_ShowWindow(tAxWin4_Window *Window, bool Show)
+{
+	CSerialiser	message;
+	message.WriteU8(IPCMSG_SETWINATTR);
+	message.WriteU16(Window->m_id);
+	message.WriteU16(IPC_WINATTR_SHOW);
+	message.WriteU8( (Show ? 1 : 0) );
+	::AxWin::SendMessage(message);
+}
+
+extern "C" void AxWin4_SetWindowFlags(tAxWin4_Window *Window, unsigned int Flags)
+{
+	CSerialiser	message;
+	message.WriteU8(IPCMSG_SETWINATTR);
+	message.WriteU16(Window->m_id);
+	message.WriteU16(IPC_WINATTR_FLAGS);
+	message.WriteU8( Flags );
+	::AxWin::SendMessage(message);
+}
+
+extern "C" void AxWin4_MoveWindow(tAxWin4_Window *Window, int X, int Y)
+{
+	CSerialiser	message;
+	message.WriteU8(IPCMSG_SETWINATTR);
+	message.WriteU16(Window->m_id);
+	message.WriteU16(IPC_WINATTR_POSITION);
+	message.WriteS16(X);
+	message.WriteS16(Y);
+	::AxWin::SendMessage(message);
+}
+extern "C" void AxWin4_ResizeWindow(tAxWin4_Window *Window, unsigned int W, unsigned int H)
+{
+	CSerialiser	message;
+	message.WriteU8(IPCMSG_SETWINATTR);
+	message.WriteU16(Window->m_id);
+	message.WriteU16(IPC_WINATTR_DIMENSIONS);
+	message.WriteU16(W);
+	message.WriteU16(H);
+	::AxWin::SendMessage(message);
+}
+
+extern "C" void AxWin4_SetTitle(tAxWin4_Window *Window, const char *Title)
+{
+	CSerialiser	message;
+	message.WriteU8(IPCMSG_SETWINATTR);
+	message.WriteU16(Window->m_id);
+	message.WriteU16(IPC_WINATTR_TITLE);
+	message.WriteString(Title);
+	::AxWin::SendMessage(message);
+}
+
+extern "C" void AxWin4_DamageRect(tAxWin4_Window *Window, unsigned int X, unsigned int Y, unsigned int W, unsigned int H)
+{
+	CSerialiser	message;
+	message.WriteU8(IPCMSG_DAMAGERECT);
+	message.WriteU16(Window->m_id);
+	message.WriteU16(X);
+	message.WriteU16(Y);
+	message.WriteU16(W);
+	message.WriteU16(H);
+	::AxWin::SendMessage(message);
+}
+
+extern "C" void *AxWin4_GetWindowBuffer(tAxWin4_Window *Window)
+{
+	if( Window->m_fd == -1 )
+	{
+		CSerialiser	req;
+		req.WriteU8(IPCMSG_GETWINBUF);
+		req.WriteU16(Window->m_id);
+		
+		CDeserialiser	response = GetSyncReply(req, IPCMSG_GETWINBUF);
+		unsigned int rspwin = response.ReadU16();
+		if( rspwin != Window->m_id )
+		{
+			_SysDebug("AxWin4_GetWindowBuffer: GETWINBUF reply for different window (%u != %u)", rspwin, Window->m_id);
+			return NULL;
+		}
+		
+		uint64_t handle = response.ReadU64();
+		Window->m_fd = _SysUnMarshalFD(handle);
+		if( Window->m_fd == -1 ) {
+			_SysDebug("AxWin4_GetWindowBuffer: Unable to unmarshal resultant FD (0x%llx)", handle);
+			return NULL;
+		}
+		
+		_SysDebug("AxWin4_GetWindowBuffer: %llx = %i", handle, Window->m_fd);
+	}
+
+	if( !Window->m_buffer )
+	{
+		size_t	size = 640*480*4;
+		Window->m_buffer = _SysMMap(NULL, size, MMAP_PROT_WRITE, MMAP_MAP_SHARED, Window->m_fd, 0);
+	}
+
+	return Window->m_buffer;
+}
+
+};	// namespace AxWin
+
diff --git a/Usermode/Libraries/libc++.so_src/Makefile b/Usermode/Libraries/libc++.so_src/Makefile
index 6cec94f11e6bf5f5abe329f4ea7a2db9f32c4486..72538379f47c0e1b39788ff9d576ffee560c71af 100644
--- a/Usermode/Libraries/libc++.so_src/Makefile
+++ b/Usermode/Libraries/libc++.so_src/Makefile
@@ -7,13 +7,22 @@ CPPFLAGS +=
 CFLAGS   += -Wall -Werror -Wextra
 CXXFLAGS += -Wall -Werror -Wextra
 ASFLAGS  +=
-LDFLAGS  += -Map map.txt -lc
+LDFLAGS  += -nostdlib
+PRELINK  := $(CRTI) $(CRTBEGINS) $(CRT0S)
+LIBS     += -lc $(LIBGCC_PATH) $(CRTENDS) $(CRTN)
+USE_CXX_LINK := yes
 
 OBJ  = misc.o new.o guard.o cxxabi.o typeinfo.o
+OBJ += string.o mutex.o
+OBJ += exceptions.o exception_handling.o system_error.o
+OBJ += gxx_personality.o
+ifeq ($(ARCHDIR),native)
+# - Include libgcc_eh (separate in linux), and the linux libc (space avoids hack in Makefile.tpl)
+LIBS += -lgcc_eh -l c
+endif
 DEPFILES := $(OBJ:%.o=%.d)
 BIN = libc++.so
 ifeq ($(ARCHDIR),native)
- OBJ := $(filter-out heap.o,$(OBJ))
  BIN = libc++_acess.so
 endif
 
diff --git a/Usermode/Libraries/libc++.so_src/cxxabi.cc b/Usermode/Libraries/libc++.so_src/cxxabi.cc
index adda291c7982acd1cfc976bebbcc9a18a5d4ba2d..bd35690af4146f4dd554260814b024c8b21feffc 100644
--- a/Usermode/Libraries/libc++.so_src/cxxabi.cc
+++ b/Usermode/Libraries/libc++.so_src/cxxabi.cc
@@ -4,8 +4,13 @@
  *
  * cxxabi.cc
  * - C++ ABI Namespace
+ * 
+ * NOTE: GCC follows the Itaniumâ„¢ C++ ABI on all platforms
+ * http://mentorembedded.github.io/cxx-abi/abi.html
+ * http://libcxxabi.llvm.org/spec.html
  */
 #include <cxxabi.h>
+#include <acess/sys.h>
 
 namespace __cxxabiv1 {
 
@@ -30,3 +35,28 @@ __vmi_class_type_info::~__vmi_class_type_info()
 
 };	// namespace __cxxabiv1
 
+extern "C" void __cxa_bad_cast ()
+{
+	_SysDebug("__cxa_bad_cast");
+	for(;;);
+	//throw ::std::bad_cast;
+}
+
+extern "C" void __cxa_bad_typeid ()
+{
+	_SysDebug("__cxa_bad_typeid");
+	for(;;);
+	//throw ::std::bad_typeid;
+}
+
+extern "C" void* __dynamic_cast(
+	const void *sub,
+	const __cxxabiv1::__class_type_info *src,
+	const __cxxabiv1::__class_type_info *dst,
+	ptrdiff_t src2dst_offset
+	)
+{
+	_SysDebug("TODO: __dynamic_cast %p %s to %s, hint=%p", sub, dst->name(), src->name(), src2dst_offset);
+	return NULL;
+}
+
diff --git a/Usermode/Libraries/libc++.so_src/exception_handling.cc b/Usermode/Libraries/libc++.so_src/exception_handling.cc
new file mode 100644
index 0000000000000000000000000000000000000000..81d460524443a3b42a66a670513a438b02beba0b
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/exception_handling.cc
@@ -0,0 +1,152 @@
+/*
+ * Acess2 C++ Support Library
+ * - By John Hodge (thePowersGang)
+ *
+ * exception_handling.cc
+ * - Exception handling code (defined by C++ ABI)
+ *
+ * References:
+ * - LLVM Exception handling
+ *  > http://llvm.org/docs/ExceptionHandling.html
+ * - Itanium C++ ABI
+ *  >http://mentorembedded.github.io/cxx-abi/abi-eh.html
+ * - HP's "aC++" Document, Ch 7 "Exception Handling Tables"
+ *  > http://mentorembedded.github.io/cxx-abi/exceptions.pdf
+ */
+#include <typeinfo>
+#include <cstdint>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <exception>
+#include "exception_handling_cxxabi.h"
+
+#include <acess/sys.h>
+
+#define DEBUG_ENABLED	1
+
+#if DEBUG_ENABLED
+# define	DEBUG(v...)	::_SysDebug(v)
+#else
+# define	DEBUG(v...)	do{}while(0)
+#endif
+
+/*__thread*/ struct __cxa_eh_globals {
+	__cxa_exception	*caughtExceptions;
+	unsigned  int	uncaughtExceptions;
+} eh_globals;
+
+/*__thread*/ struct {
+	__cxa_exception	info;
+	char	buf[32];
+} emergency_exception;
+/*__thread*/ bool	emergency_exception_used;
+
+static bool TEST_AND_SET(bool& flag) {
+	bool ret = flag;
+	flag = true;
+	return ret;
+}
+
+// === CODE ===
+extern "C" __cxa_eh_globals *__cxa_get_globals(void)
+{
+	return &eh_globals;
+}
+extern "C" __cxa_eh_globals *__cxa_get_globals_fast(void)
+{
+	return &eh_globals;
+}
+extern "C" void __cxa_call_unexpected(void *)
+{
+	// An unexpected exception was thrown from a function that lists its possible exceptions
+	_SysDebug("__cxa_call_unexpected - TODO");
+	for(;;);
+}
+
+extern "C" void *__cxa_allocate_exception(size_t thrown_size)
+{
+	DEBUG("__cxa_allocate_exception(%i)", thrown_size);
+	__cxa_exception	*ret = static_cast<__cxa_exception*>( malloc( sizeof(__cxa_exception) + thrown_size ) );
+	if( !ret ) {
+		if( thrown_size <= sizeof(emergency_exception.buf) && TEST_AND_SET(emergency_exception_used) )
+		{
+			ret = &emergency_exception.info;
+		}
+	}
+	if( !ret ) {
+		_SysDebug("__cxa_allocate_exception - No free space");
+		::std::terminate();
+	}
+	DEBUG("__cxa_allocate_exception: return %p", ret+1);
+	return ret + 1;
+}
+
+extern "C" void __cxa_free_exception(void *thrown_exception)
+{
+	DEBUG("__cxa_free_exception(%p)", thrown_exception);
+	if(thrown_exception == &emergency_exception.buf) {
+		//assert(emergency_exception_used);
+		emergency_exception_used = false;
+	}
+	else {
+		__cxa_exception	*except = static_cast<__cxa_exception*>( thrown_exception ) - 1;
+		free(except);
+	}
+}
+
+extern "C" void __cxa_throw(void *thrown_exception, std::type_info *tinfo, void (*dest)(void*))
+{
+	#if DEBUG_ENABLED
+	DEBUG("__cxa_throw(%p,%p,%p) '%s' by %p",
+		thrown_exception, tinfo, dest, tinfo->name(), __builtin_return_address(0)
+		);
+	{
+		const ::std::exception* e = reinterpret_cast<const ::std::exception*>(thrown_exception);
+		DEBUG("- e.what() = '%s'", e->what());
+	}
+	#endif
+
+	__cxa_exception	*except = static_cast<__cxa_exception*>( thrown_exception ) - 1;
+	
+	except->unexpectedHandler = 0;
+	except->terminateHandler = 0;
+	except->exceptionType = tinfo;
+	except->exceptionDestructor = dest;
+	memcpy(&except->unwindHeader.exception_class, EXCEPTION_CLASS_ACESS, 8);
+	__cxa_get_globals()->uncaughtExceptions ++;
+	
+	int rv = _Unwind_RaiseException( &except->unwindHeader );
+	
+	::_SysDebug("__cxa_throw(%p,%s) :: UNCAUGHT %i", thrown_exception, tinfo->name(), rv);
+	::std::terminate();
+}
+
+extern "C" void *__cxa_begin_catch(_Unwind_Exception *exceptionObject)
+{
+	__cxa_exception	*except = reinterpret_cast<__cxa_exception*>( exceptionObject+1 )-1;
+	DEBUG("__cxa_begin_catch(%p) - except=%p", exceptionObject, except);
+	
+	except->handlerCount ++;
+	
+	except->nextException = __cxa_get_globals()->caughtExceptions;
+	__cxa_get_globals()->caughtExceptions = except;
+	
+	__cxa_get_globals_fast()->uncaughtExceptions --;
+	
+	return except+1;
+}
+
+extern "C" void __cxa_end_catch()
+{
+	struct __cxa_exception	*except = __cxa_get_globals()->caughtExceptions;
+	DEBUG("__cxa_end_catch - %p", except);
+	except->handlerCount --;
+	__cxa_get_globals()->caughtExceptions = except->nextException;
+	
+	if( except->handlerCount == 0 ) {
+		__cxa_free_exception(except+1);
+	}
+}
+
+
diff --git a/Usermode/Libraries/libc++.so_src/exception_handling_acxx.h b/Usermode/Libraries/libc++.so_src/exception_handling_acxx.h
new file mode 100644
index 0000000000000000000000000000000000000000..536016effc5c526663e5ab6602d3b5627926e16b
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/exception_handling_acxx.h
@@ -0,0 +1,59 @@
+/*
+ * Acess2 C++ Support Library
+ * - By John Hodge (thePowersGang)
+ *
+ * exception_handling_acxx.h
+ * - C++ Exception handling definions from HP's aC++ document
+ *
+ * Reference: http://mentorembedded.github.io/cxx-abi/exceptions.pdf
+ */
+#ifndef _EXCEPTION_HANLDING_ACXX_H_
+#define _EXCEPTION_HANLDING_ACXX_H_
+
+/**
+ * \brief Language Specific Data Area
+ * \note Pointer obtained via _Unwind_GetLanguageSpecificData
+ */
+struct sLSDA_Header
+{
+	const void	*Base;
+	uintptr_t	LPStart;
+	uint8_t 	TTEncoding;
+	uintptr_t	TypePtrBase;	// base address for type pointers
+	uintptr_t	TTBase;	// Base address for type offsets
+	uint8_t	CallSiteEncoding;
+	const void	*CallSiteTable;
+	const void	*ActionTable;
+};
+
+/* Pointer encodings, from dwarf2.h.  */ 
+#define DW_EH_PE_absptr         0x00 
+#define DW_EH_PE_omit           0xff 
+
+// - Data format (mask with 0x0F)
+#define DW_EH_PE_fmtmask	0x0F
+#define DW_EH_PE_uleb128        0x01 
+#define DW_EH_PE_udata2         0x02 
+#define DW_EH_PE_udata4         0x03 
+#define DW_EH_PE_udata8         0x04 
+#define DW_EH_PE_sleb128        0x09 
+#define DW_EH_PE_sdata2         0x0A 
+#define DW_EH_PE_sdata4         0x0B 
+#define DW_EH_PE_sdata8         0x0C 
+#define DW_EH_PE_signed         0x08 
+// - Data maipulation (0x70)
+#define DW_EH_PE_relmask	0x70
+#define DW_EH_PE_pcrel          0x10 
+#define DW_EH_PE_textrel        0x20 
+#define DW_EH_PE_datarel        0x30 
+#define DW_EH_PE_funcrel        0x40 
+#define DW_EH_PE_aligned        0x50 
+ 
+#define DW_EH_PE_indirect       0x80 
+
+typedef	uint64_t	leb128u_t;
+typedef	 int64_t	leb128s_t;
+
+
+#endif
+
diff --git a/Usermode/Libraries/libc++.so_src/exception_handling_cxxabi.h b/Usermode/Libraries/libc++.so_src/exception_handling_cxxabi.h
new file mode 100644
index 0000000000000000000000000000000000000000..69f373592f0e33b84ba74d62117c3c96f2f13753
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/exception_handling_cxxabi.h
@@ -0,0 +1,88 @@
+/*
+ * Acess2 C++ Support Library
+ * - By John Hodge (thePowersGang)
+ *
+ * exception_handling_cxxabi.h
+ * - C++ Exception Handling definitions from the Itanium C++ ABI
+ */
+#ifndef _EXCEPTION_HANLDING_CXXABI_H_
+#define _EXCEPTION_HANLDING_CXXABI_H_
+
+typedef void *(unexpected_handler)(void);
+typedef void *(terminate_handler)(void);
+
+struct _Unwind_Context;
+
+typedef enum {
+	_URC_NO_REASON = 0,
+	_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
+	_URC_FATAL_PHASE2_ERROR = 2,
+	_URC_FATAL_PHASE1_ERROR = 3,
+	_URC_NORMAL_STOP = 4,
+	_URC_END_OF_STACK = 5,
+	_URC_HANDLER_FOUND = 6,
+	_URC_INSTALL_CONTEXT = 7,
+	_URC_CONTINUE_UNWIND = 8
+} _Unwind_Reason_Code;
+
+typedef int	_Unwind_Action;
+static const _Unwind_Action	_UA_SEARCH_PHASE  = 1;
+static const _Unwind_Action	_UA_CLEANUP_PHASE = 2;
+static const _Unwind_Action	_UA_HANDLER_FRAME = 4;
+static const _Unwind_Action	_UA_FORCE_UNWIND  = 8;
+
+typedef void	(*_Unwind_Exception_Cleanup_Fn)(_Unwind_Reason_Code reason, struct _Unwind_Exception *exc);
+
+struct _Unwind_Exception
+{
+	uint64_t	exception_class;
+	_Unwind_Exception_Cleanup_Fn	exception_cleanup;
+	uint64_t	private_1;
+	uint64_t	private_2;
+};
+
+struct __cxa_exception
+{
+	std::type_info*	exceptionType;
+	void (*exceptionDestructor)(void *);
+	unexpected_handler*	unexpectedHandler;
+	terminate_handler*	terminateHandler;
+	__cxa_exception*	nextException;
+	
+	int	handlerCount;
+	
+	int	handlerSwitchValue;
+	const char*	actionRecord;
+	const char*	languageSpecificData;
+	void*	catchTemp;
+	void*	adjustedPtr;
+	
+	_Unwind_Exception	unwindHeader;
+};
+
+#define EXCEPTION_CLASS_ACESS	"Ac20C++\0"
+
+typedef _Unwind_Reason_Code	(*_Unwind_Stop_Fn)(int version, _Unwind_Action actions, uint64_t exception_class, struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context, void *stop_parameter);
+
+// Exports
+extern "C" void __cxa_call_unexpected(void *);
+extern "C" void *__cxa_allocate_exception(size_t thrown_size);
+extern "C" void __cxa_free_exception(void *thrown_exception);
+extern "C" void __cxa_throw(void *thrown_exception, std::type_info *tinfo, void (*dest)(void*));
+extern "C" void *__cxa_begin_catch(_Unwind_Exception *exceptionObject);
+extern "C" void __cxa_end_catch();
+
+// Imports
+extern "C" _Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception *exception_object);
+extern "C" _Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, void *stop_parameter);
+extern "C" void	_Unwind_DeleteException(struct _Unwind_Exception *exception_object);
+extern "C" uint64_t	_Unwind_GetGR(struct _Unwind_Context *context, int index);
+extern "C" void 	_Unwind_SetGR(struct _Unwind_Context *context, int index, uint64_t new_value);
+extern "C" uint64_t	_Unwind_GetIP(struct _Unwind_Context *context);
+extern "C" void 	_Unwind_SetIP(struct _Unwind_Context *context, uint64_t new_value);
+extern "C" uint64_t	_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context);
+extern "C" uint64_t	_Unwind_GetRegionStart(struct _Unwind_Context *context);
+
+#endif
+
+
diff --git a/Usermode/Libraries/libc++.so_src/exceptions.cc b/Usermode/Libraries/libc++.so_src/exceptions.cc
new file mode 100644
index 0000000000000000000000000000000000000000..cc7c8c27558fb6a9b6acf3deceba3e0b175ea1a5
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/exceptions.cc
@@ -0,0 +1,79 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * exceptions.cc
+ * - exception and friends
+ */
+#include <string>
+#include <exception>
+#include <stdexcept>
+#include <acess/sys.h>
+
+// === CODE ===
+namespace std {
+
+exception::exception() throw()
+{
+}
+exception::exception(const exception&) throw()
+{
+}
+exception& exception::operator=(const exception&) throw()
+{
+	return *this;
+}
+exception::~exception() throw()
+{
+}
+const char* exception::what() const throw()
+{
+	return "generic exception";
+}
+
+void terminate()
+{
+	_SysDebug("terminate()");
+	_exit(0);
+}
+
+_bits::str_except::str_except(const string& what_arg):
+	m_str(what_arg)
+{
+}
+_bits::str_except::~str_except() noexcept
+{
+}
+_bits::str_except& _bits::str_except::operator=(const str_except& e) noexcept
+{
+	m_str = e.m_str;
+	return *this;
+}
+const char* _bits::str_except::what() const throw()
+{
+	return m_str.c_str();
+}
+
+// --- Standar Exceptions ---
+logic_error::logic_error(const string& what_str):
+	_bits::str_except(what_str)
+{
+}
+
+out_of_range::out_of_range(const string& what_str):
+	logic_error(what_str)
+{
+}
+
+length_error::length_error(const string& what_str):
+	logic_error(what_str)
+{
+}
+
+runtime_error::runtime_error(const string& what_str):
+	_bits::str_except(what_str)
+{
+}
+
+}	// namespace std
+
diff --git a/Usermode/Libraries/libc++.so_src/guard.cc b/Usermode/Libraries/libc++.so_src/guard.cc
index a075a7b610830dc087f49ad5643f534562e3ad4f..6468a676300fdcae4736960f4047cff55e1f1b81 100644
--- a/Usermode/Libraries/libc++.so_src/guard.cc
+++ b/Usermode/Libraries/libc++.so_src/guard.cc
@@ -6,24 +6,33 @@
  * - One-time construction API
  */
 #include <stdint.h>
+#include <acess/sys.h>
+#include <cstdlib>
+
+#define FLAG_INIT_COMPLETE	(1<<0)
+#define FLAG_INIT_LOCKED	(1<<1)
 
 extern "C" int __cxa_guard_acquire ( int64_t *guard_object )
 {
 	// TODO: Mutex!
-	if( *guard_object )
-		return 1;
-	*guard_object = 1;
-	return 0;
+	if( *guard_object == FLAG_INIT_COMPLETE )
+		return 0;
+	if( *guard_object == FLAG_INIT_LOCKED ) {
+		_SysDebug("ERROR: __cxa_guard_acquire - nested");
+	}
+	*guard_object = FLAG_INIT_LOCKED;
+	return 1;
 }
 
 extern "C" void __cxa_guard_release ( int64_t *guard_object )
 {
-	*guard_object = 0;
+	*guard_object = FLAG_INIT_COMPLETE;
 }
 
 extern "C" void __cxa_guard_abort ( int64_t *guard_object )
 {
-	*guard_object = 0;
-	// TODO: abort
+	*guard_object = FLAG_INIT_COMPLETE;
+	_SysDebug("__cxa_guard_abort");
+	abort();
 }
 
diff --git a/Usermode/Libraries/libc++.so_src/gxx_personality.cc b/Usermode/Libraries/libc++.so_src/gxx_personality.cc
new file mode 100644
index 0000000000000000000000000000000000000000..fbd1623417db6e03d193a7174659bc8add90a415
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/gxx_personality.cc
@@ -0,0 +1,461 @@
+/*
+ * Acess2 C++ Support Library
+ * - By John Hodge (thePowersGang)
+ *
+ * gxx_personality.cc
+ * - Implementation of GNU C++ Exception handling "Personality"
+ */
+#include <typeinfo>
+#include <cstdint>	// [u]intN_t
+#include <cstdlib>	// abort
+#include <cstring>	// memcmp
+#include "exception_handling_acxx.h"
+#include "exception_handling_cxxabi.h"
+#include <acess/sys.h>
+#include <cassert>
+#include <cxxabi.h>	// __dynamic_cast
+
+#define DEBUG_ENABLED	1
+
+#if DEBUG_ENABLED
+# define DEBUG(v...)	::_SysDebug(v)
+#else
+# define DEBUG(v...)	do{}while(0)
+#endif
+
+// === PROTOTYPES ===
+extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass,
+		struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context);
+static int get_frame_action(const sLSDA_Header &header, _Unwind_Context *context, const std::type_info *throw_type,
+	uint64_t &landingpad, int64_t &switch_value);
+static bool exception_matches_single(const std::type_info *throw_type, const struct sLSDA_Header &header, int type_index);
+static bool exception_matches_list(const std::type_info *throw_type, const struct sLSDA_Header &header, int list_index);
+static void read_lsda_header(const void *&ptr, _Unwind_Context *context, struct sLSDA_Header *header);
+static size_t _get_encoded_size(int encoding);
+static uint64_t _get_base(uint8_t encoding, _Unwind_Context *context);
+static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding, uint64_t base);
+static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding);
+template <typename T> static T read(const void *&ptr);
+static uint64_t _read_leb128u(const void *&ptr);
+static int64_t _read_leb128s(const void *&ptr);
+
+// === CODE ===
+extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass,
+		struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
+{
+	//::_SysDebug("__gxx_personality_v0(%i, 0x%x, 0x%llx, ...)", version, actions, exceptionClass);
+	//::_SysDebug("__gxx_personality_v0(..., %p, %p)", exceptionObject, context);
+	
+	//if( exceptionObject ) {
+	//	::_SysDebug("exception_object: Class 0x%llx", exceptionObject->exception_class);
+	//}
+	//if( context )
+	//{
+	//	::_SysDebug("context: IP 0x%llx", _Unwind_GetIP(context));
+	//	::_SysDebug("context: RegionStart 0x%llx", _Unwind_GetRegionStart(context));
+	//	::_SysDebug("context: LangSpecData 0x%llx", _Unwind_GetLanguageSpecificData(context));
+	//}
+	
+	if( version != 1 ) {
+		::_SysDebug("%s: Unexpected version %i != exp 1", __func__, version);
+		return _URC_FATAL_PHASE1_ERROR;
+	}
+	
+	const void *lsda_ptr = (const void*)_Unwind_GetLanguageSpecificData(context);
+	struct sLSDA_Header	header;
+	read_lsda_header(lsda_ptr, context, &header);
+
+	const void *exception_object = exceptionObject + 1;
+	const __cxa_exception *exception_info = static_cast<const __cxa_exception*>(exception_object) - 1;
+	std::type_info *throw_type = (
+		memcmp(&exceptionClass,EXCEPTION_CLASS_ACESS,8) == 0 ? exception_info->exceptionType : NULL
+		);
+
+	uint64_t	landingpad = 0;
+	 int64_t	switch_value = 0;
+	int frame_action = get_frame_action(header, context, throw_type,  landingpad, switch_value);
+
+	if( (actions & _UA_SEARCH_PHASE) && (actions & _UA_CLEANUP_PHASE) )
+	{
+		_SysDebug("__gxx_personality_v0: ERROR Both _UA_SEARCH_PHASE and _UA_CLEANUP_PHASE set");
+		abort();
+	}	
+
+	// Search
+	if( actions & _UA_SEARCH_PHASE )
+	{
+		// If a handler was found
+		if( frame_action == 2 ) {
+			// - return _URC_HANDLER_FOUND
+			DEBUG("SEARCH: 0x%llx Handler %p(%i)",
+				_Unwind_GetIP(context), landingpad, switch_value);
+			return _URC_HANDLER_FOUND;
+		}
+		// - If no handler (either nothing, or cleanups), return _URC_CONTINUE_UNWIND
+		DEBUG("SEARCH: 0x%llx no handler %p(%i)",
+			_Unwind_GetIP(context), landingpad, switch_value);
+		return _URC_CONTINUE_UNWIND;
+	}
+
+	// Cleanup	
+	if( actions & _UA_CLEANUP_PHASE )
+	{
+		// No action, continue
+		if( frame_action == 0 ) {
+			return _URC_CONTINUE_UNWIND;
+		}
+		assert(landingpad);
+	
+		if( actions & _UA_FORCE_UNWIND )
+		{
+			// Unwind forced, override switch_value to 0
+			// - Disables any attempt to handle exception
+			switch_value = 0;
+		}
+	
+		DEBUG("Install context IP=0x%x, R%i=%p/R%i=%i",
+			(uintptr_t)landingpad,
+			__builtin_eh_return_data_regno(0), exceptionObject,
+			__builtin_eh_return_data_regno(1), switch_value
+			);
+		_Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)exceptionObject);
+		_Unwind_SetGR(context, __builtin_eh_return_data_regno(1), switch_value);
+		_Unwind_SetIP(context, landingpad);
+		return _URC_INSTALL_CONTEXT;
+	}
+	
+	_SysDebug("__gxx_personality_v0: Neither _UA_SEARCH_PHASE or _UA_CLEANUP_PHASE set");
+	return _URC_FATAL_PHASE1_ERROR;
+}
+
+int get_frame_action(const sLSDA_Header &header, _Unwind_Context *context, const std::type_info* throw_type,
+	uint64_t &landingpad, int64_t &switch_value)
+{
+	uint64_t ip = _Unwind_GetIP(context) - _Unwind_GetRegionStart(context);
+	DEBUG("get_frame_action: IP = 0x%llx + 0x%llx", _Unwind_GetRegionStart(context), ip);
+	// Check if there is a handler for this exception in this frame
+	// - Search call site table for this return address (corresponds to a try block)
+	uintptr_t	cs_ldgpad;
+	uint64_t	cs_action;
+	const void *lsda_ptr = header.CallSiteTable;
+	while( lsda_ptr < header.ActionTable )
+	{
+		uintptr_t cs_start  = _read_encoded(lsda_ptr, context, header.CallSiteEncoding);
+		uintptr_t cs_len    = _read_encoded(lsda_ptr, context, header.CallSiteEncoding);
+		cs_ldgpad = _read_encoded(lsda_ptr, context, header.CallSiteEncoding);
+		cs_action = _read_leb128u(lsda_ptr);
+		
+		// If IP falls below this entry, it's not on the list
+		if( ip <= cs_start ) {
+			lsda_ptr = header.ActionTable;
+			break ;
+		}
+		if( ip > cs_start + cs_len )
+			continue;
+		
+		break;
+	}
+	if( lsda_ptr > header.ActionTable ) {
+		_SysDebug("__gxx_personality_v0: Malformed Call Site Table, reading overran the end (%p>%p)",
+			lsda_ptr, header.ActionTable);
+	}
+	if( lsda_ptr >= header.ActionTable ) {
+		// No match!
+		DEBUG("__gxx_personality_v0: No entry for IP 0x%x", ip);
+		return 0;
+	}
+	
+	// Found it
+	if( cs_ldgpad == 0 ) {
+		DEBUG("No landingpad, hence no action");
+		if( cs_action != 0 ) {
+			_SysDebug("%s: NOTICE cs_ldgpad==0 but cs_action(0x%llx)!=0",
+				__func__, cs_action);
+		}
+		return 0;
+	}
+	else if( cs_action == 0 ) {
+		DEBUG("No action, cleanups only");
+		switch_value = 0;
+		landingpad = header.LPStart + cs_ldgpad;
+		return 1;	// 1 = cleanup only
+	}
+	else {
+		switch_value = 0;
+		landingpad = header.LPStart + cs_ldgpad;
+		// catch() handler
+		bool	has_cleanups = false;	// set to true to override return value
+		const void *action_list = (const char*)header.ActionTable + (cs_action-1);
+		for(;;)	// broken by 0 length entry
+		{
+			leb128s_t filter = _read_leb128s(action_list);
+			leb128s_t disp = _read_leb128s(action_list);
+			if( filter == 0 )
+			{
+				// Cleanup
+				has_cleanups = true;
+			}
+			else if( filter < 0 )
+			{
+				// Exception specifcation
+				if( !exception_matches_list(throw_type, header, -filter) )
+				{
+					switch_value = filter;
+					return 2;
+				}
+			}
+			else
+			{
+				// Catch
+				if( exception_matches_single(throw_type, header, filter) )
+				{
+					switch_value = filter;
+					return 2;
+				}
+			}
+			
+			if( disp == 0 )
+				break;
+			action_list = (const char*)action_list + disp-1;
+		}
+		
+		// If a cleanup request was seen, tell the caller that we want to handle it
+		if( has_cleanups ) {
+			return 1;	// 1 = cleanup only
+		}
+		// Else, there's nothing here to handle
+		return 0;
+	}
+}
+
+const ::std::type_info *get_type_info(const struct sLSDA_Header &header, int type_index)
+{
+	size_t	encoded_size = _get_encoded_size(header.TTEncoding);
+	assert(encoded_size > 0);
+	const void *ptr = (const void*)(header.TTBase - encoded_size * type_index);
+	assert( header.TTBase );
+	assert( ptr > header.ActionTable );
+	
+	uintptr_t type_ptr = _read_encoded(ptr, NULL, header.TTEncoding, header.TypePtrBase);
+	return reinterpret_cast< ::std::type_info* >(type_ptr);
+}
+
+const ::std::type_info *get_exception_type(const void *exception_object)
+{
+	if( !exception_object )
+		return NULL;
+	const struct _Unwind_Exception	*u_execept = (const struct _Unwind_Exception*)exception_object;
+	const struct __cxa_exception *except = (const struct __cxa_exception*)(u_execept + 1) - 1;
+	if( memcmp(&except->unwindHeader.exception_class, EXCEPTION_CLASS_ACESS, 8) != 0 )
+		return NULL;
+	
+	return except->exceptionType;
+}
+
+bool exception_matches_single(const std::type_info *throw_type, const struct sLSDA_Header &header, int type_index)
+{
+	const ::std::type_info *catch_type = get_type_info(header, type_index);
+	DEBUG("catch_type = %p", catch_type);
+
+	if( !catch_type )
+	{
+		DEBUG("catch(...)");
+		return true;
+	}
+	else if( !throw_type )
+	{
+		DEBUG("threw UNK");
+		return false;
+	}
+	else
+	{
+		DEBUG("catch(%s), throw %s", catch_type->name(), throw_type->name());
+		size_t ofs = 0;
+		if( !catch_type->__is_child(*throw_type, ofs) ) {
+			_SysDebug("> No match");
+			return false;
+		}
+		
+		return true;
+	}
+}
+bool exception_matches_list(const std::type_info *throw_type, const struct sLSDA_Header &header, int list_index)
+{
+	_SysDebug("TODO: exception_matches_list %i", list_index);
+	abort();
+	(void)throw_type;
+	(void)header;
+	return true;
+}
+
+template <typename T> T read(const void *&ptr)
+{
+	T rv = *reinterpret_cast<const T*>(ptr);
+	ptr = (const char*)ptr + sizeof(T);
+	return rv;
+}
+
+static void read_lsda_header(const void *&ptr, _Unwind_Context *context, struct sLSDA_Header *header)
+{
+	header->Base = ptr;
+	
+	uint8_t	start_encoding = read<uint8_t>(ptr);
+	if( start_encoding == DW_EH_PE_omit )
+		header->LPStart = _Unwind_GetRegionStart(context);
+	else
+		header->LPStart = _read_encoded(ptr, context, start_encoding);
+	
+	header->TTEncoding = read<uint8_t>(ptr);
+	if( header->TTEncoding == DW_EH_PE_omit )
+		header->TTBase = 0;
+	else {
+		ptrdiff_t	tt_ofs = _read_leb128u(ptr);
+		header->TTBase = (uintptr_t)ptr + tt_ofs;
+	}
+	header->TypePtrBase = _get_base(header->TTEncoding, context);
+
+	header->CallSiteEncoding = read<uint8_t>(ptr);
+	uint64_t call_site_size = _read_leb128u(ptr);
+	header->CallSiteTable = ptr;
+	header->ActionTable = (const void*)( (uintptr_t)ptr + call_site_size );
+	
+	#if 0
+	::_SysDebug("LSDA header:");
+	::_SysDebug("->LPStart = 0x%lx", header->LPStart);
+	::_SysDebug("->TTEncoding = 0x%02x", header->TTEncoding);
+	::_SysDebug("->TTBase = 0x%lx", header->TTBase);
+	::_SysDebug("->CallSiteEncoding = 0x%02x", header->CallSiteEncoding);
+	::_SysDebug("->CallSiteTable = %p", header->CallSiteTable);
+	::_SysDebug("->ActionTable = %p", header->ActionTable);
+	#endif
+}
+
+static size_t _get_encoded_size(int encoding)
+{
+	switch(encoding & DW_EH_PE_fmtmask)
+	{
+	case DW_EH_PE_absptr:	// absolute
+		return sizeof(void*);
+	case DW_EH_PE_udata4:
+		return 4;
+	default:
+		_SysDebug("_get_encoded_size: Unknown encoding 0x%02x", encoding);
+		return 0;
+	}
+}
+
+/*
+ * \brief Read a DWARF encoded pointer from the stream
+ */
+static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding, uint64_t base)
+{
+	(void)context;
+	if( encoding == DW_EH_PE_aligned ) {
+		void	**aligned = (void**)( ((uintptr_t)ptr + sizeof(void*) - 1) & -(sizeof(void*)-1) );
+		ptr = (void*)( (uintptr_t)aligned + sizeof(void*) );
+		return (uintptr_t)*aligned;
+	}
+	uint64_t	rv;
+	uintptr_t	orig_ptr = (uintptr_t)ptr;
+	switch(encoding & DW_EH_PE_fmtmask)
+	{
+	case DW_EH_PE_absptr:	// absolute
+		rv = (uintptr_t)read<void*>(ptr);
+		break;
+	case DW_EH_PE_uleb128:
+		rv = _read_leb128u(ptr);
+		break;
+	case DW_EH_PE_sleb128:
+		rv = _read_leb128s(ptr);
+		break;
+	case DW_EH_PE_udata2:
+		rv = read<uint16_t>(ptr);
+		break;
+	case DW_EH_PE_udata4:
+		rv = read<uint32_t>(ptr);
+		break;
+	case DW_EH_PE_udata8:
+		rv = read<uint64_t>(ptr);
+		break;
+	default:
+		::_SysDebug("_read_encoded: Unknown encoding format 0x%x", encoding & DW_EH_PE_fmtmask);
+		::abort();
+	}
+	if( rv != 0 )
+	{
+		if( (encoding & DW_EH_PE_relmask) == DW_EH_PE_pcrel ) {
+			rv += orig_ptr;
+		}
+		else {
+			rv += base;
+		}
+		
+		if( encoding & DW_EH_PE_indirect ) {
+			rv = (uintptr_t)*(void**)(uintptr_t)rv;
+		}
+		else {
+			// nothing
+		}
+	}
+	return rv;
+}
+static uint64_t _get_base(uint8_t encoding, _Unwind_Context *context)
+{
+	if( encoding == 0xFF )
+		return 0;
+	switch(encoding & DW_EH_PE_relmask)
+	{
+	case DW_EH_PE_absptr:
+	case DW_EH_PE_pcrel:
+	case DW_EH_PE_aligned:
+		return 0;
+	//case DW_EH_PE_textrel:
+		//return _Unwind_GetTextRelBase(context);
+	//case DW_EH_PE_datarel:
+		//return _Unwind_GetDataRelBase(context);
+	case DW_EH_PE_funcrel:
+		return _Unwind_GetRegionStart(context);
+	default:
+		::_SysDebug("_get_base: Unknown encoding relativity 0x%x", (encoding & DW_EH_PE_relmask));
+		::abort();
+		for(;;);
+	}
+}
+static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding)
+{
+	return _read_encoded(ptr, context, encoding, _get_base(encoding, context));
+}
+
+static uint64_t _read_leb128_raw(const void *&ptr, int *sizep)
+{
+	 int	size = 0;
+	uint64_t val = 0;
+	const uint8_t	*ptr8 = static_cast<const uint8_t*>(ptr);
+	do
+	{
+		val |= (*ptr8 & 0x7F) << (size*7);
+		size ++;
+	} while( *ptr8++ & 0x80 );
+	
+	ptr = ptr8;
+	*sizep = size;
+	
+	return val;
+}
+
+static uint64_t _read_leb128u(const void *&ptr)
+{
+	 int	unused_size;
+	return _read_leb128_raw(ptr, &unused_size);
+}
+static int64_t _read_leb128s(const void *&ptr)
+{
+	 int	size;
+	uint64_t val = _read_leb128_raw(ptr, &size);
+	if( size*7 <= 64 && (val & (1 << (size*7-1))) )
+	{
+		val |= 0xFFFFFFFFFFFFFFFF << (size*7);
+	}
+	return val;
+}
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/_libcxx_helpers.h b/Usermode/Libraries/libc++.so_src/include_exp/_libcxx_helpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..bd3b805c0f426dbdbf3a7232c3c4172f37478cd4
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/_libcxx_helpers.h
@@ -0,0 +1,31 @@
+
+#ifndef _LIBCXX__LIBCXX_HELEPRS_H_
+#define _LIBCXX__LIBCXX_HELEPRS_H_
+
+#if __cplusplus > 199711L	// C++11 check
+# define _CXX11_AVAIL	1
+#else
+# define _CXX11_AVAIL	0
+#endif
+
+#define _libcxx_assert(cnd) do { \
+	if(!(cnd)) {\
+		::_sys::debug("libc++ assert failure %s:%i - %s", __FILE__, __LINE__, #cnd);\
+		::_sys::abort(); \
+	} \
+} while(0)
+
+namespace _sys {
+extern void abort() __asm__ ("abort") __attribute__((noreturn));
+extern void debug(const char *, ...);
+extern void hexdump(const char *, const void *, unsigned int);
+};
+
+#if _CXX11_AVAIL
+#define _CXX11_MOVE(val)	::std::move(val)
+#else
+#define _CXX11_MOVE(val)	val
+#endif
+
+#endif
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/algorithm b/Usermode/Libraries/libc++.so_src/include_exp/algorithm
new file mode 100644
index 0000000000000000000000000000000000000000..c08e28f3e538980fc2f0533b99be0aaff6783fa8
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/algorithm
@@ -0,0 +1,79 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * algorithm (header)
+ * - C++'s generic algorithms
+ */
+#ifndef _LIBCXX_ALGORITHM_
+#define _LIBCXX_ALGORITHM_
+
+#include <utility>
+
+#include "_libcxx_helpers.h"
+
+namespace std {
+
+// --- Non-modifiying sequence operations ---
+#if _CXX11_AVAIL
+// TODO: all_of
+// TODO: any_of
+// TODO: none_of
+#endif
+template <class InputIterator, class Function>
+Function for_each(InputIterator first, InputIterator last, Function fn)
+{
+	while( first != last )
+	{
+		fn( *first );
+		++ first;
+	}
+	return _CXX11_MOVE(fn);
+}
+
+template <class InputIterator, class T>
+InputIterator find(InputIterator first, InputIterator last, const T& val)
+{
+	while( first != last )
+	{
+		if( *first == val )
+			return first;
+		++ first;
+	}
+	return last;
+}
+// TODO: find_if
+// TODO: find_if_not (C++11)
+// TODO: find_end
+// TODO: find_first_of
+
+// Maximum
+template <class T>
+const T& max(const T& a, const T& b)
+{
+	return (a<b) ? b : a;
+}
+
+template <class T, class Compare>
+const T& max(const T& a, const T& b, Compare comp)
+{
+	return comp(a, b) ? b : a;
+}
+
+template <class T>
+const T& min(const T& a, const T& b)
+{
+	return (a<b) ? a : b;
+}
+
+template <class T, class Compare>
+const T& min(const T& a, const T& b, Compare comp)
+{
+	return comp(a, b) ? a : b;
+}
+
+};	// namespace std
+
+#endif
+
+// vim: ft=cpp
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/allocator b/Usermode/Libraries/libc++.so_src/include_exp/allocator
new file mode 100644
index 0000000000000000000000000000000000000000..1608a3c72ed36f273230782e84a58104be4c1a3b
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/allocator
@@ -0,0 +1,121 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * string (header)
+ * - C++'s String type
+ */
+#ifndef _LIBCXX_ALLOCATOR_
+#define _LIBCXX_ALLOCATOR_
+
+#include "_libcxx_helpers.h"
+
+#include "new"
+#include "cstddef"
+#include "utility"
+
+namespace std {
+
+template <class T> class allocator;
+
+namespace _bits {
+
+template <class T>
+class allocator_real
+{
+public:
+	typedef T	value_type;
+	typedef T*	pointer;
+	typedef T&	reference;
+	typedef const T* const_pointer;
+	typedef const T&	const_reference;
+	typedef size_t	size_type;
+	typedef ptrdiff_t	difference_type;
+	
+	template <class Type>
+	struct rebind
+	{
+		typedef allocator<Type> other;
+	};
+	
+	allocator_real() throw() {
+	}
+	allocator_real(const allocator_real& alloc __attribute__((unused))) throw() {
+	}
+	template <class U>
+	allocator_real(const allocator_real<U>& alloc) throw() {
+	}
+	~allocator_real() throw() {
+	}
+	
+	pointer address(reference x) const {
+		return &x;
+	}
+	const_pointer address(const_reference x) const {
+		return &x;
+	}
+	pointer allocate(size_type n, const void* hint=0) {
+		hint = hint;
+		return static_cast<pointer>( ::operator new (n * sizeof(value_type)) );
+	}
+	void deallocate(pointer p, size_type n) {
+		n=n;
+		::operator delete(p);
+	}
+	size_type max_size() {
+		return ((size_type)-1) / sizeof(value_type);
+	}
+	void construct( pointer p, const_reference val ) {
+		new ((void*)p) value_type (val);
+	}
+	// C++11
+	#if _CXX11_AVAIL
+	template<class U, class... Args>
+	void construct( U* p, Args&&... args ) {
+		::new ((void*)p) U (::std::forward<Args>(args)...);
+	}
+	#endif
+	void destroy(pointer p) {
+		p->~value_type();
+	}
+};
+
+template <class T>
+class allocator_noconstruct:
+	public ::std::_bits::allocator_real<T>
+{
+public:
+	void construct( typename allocator_real<T>::pointer p, typename allocator_real<T>::const_reference val ) {
+		*p = val;
+	}
+	void destroy(typename allocator_real<T>::pointer p) {
+	}
+};
+
+};
+
+template <class T>
+class allocator:
+	public _bits::allocator_real<T>
+{
+};
+
+#if 1
+template <>
+class allocator<unsigned char>:
+	public _bits::allocator_noconstruct<unsigned char>
+{
+};
+template <>
+class allocator<unsigned long>:
+	public _bits::allocator_noconstruct<unsigned long>
+{
+};
+#endif
+
+};
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/cassert b/Usermode/Libraries/libc++.so_src/include_exp/cassert
new file mode 100644
index 0000000000000000000000000000000000000000..d11b7462abe8ed920edb1f87e44eb7c607cd94a6
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/cassert
@@ -0,0 +1,3 @@
+extern "C" {
+#include <assert.h>
+}
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/cerrno b/Usermode/Libraries/libc++.so_src/include_exp/cerrno
new file mode 100644
index 0000000000000000000000000000000000000000..3ebd48a3f458bcb0120fd47b6154fc9c94006e8e
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/cerrno
@@ -0,0 +1,6 @@
+/*
+ */
+extern "C" {
+#include <errno.h>
+}
+// vim: ft=cpp
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/cstddef b/Usermode/Libraries/libc++.so_src/include_exp/cstddef
new file mode 100644
index 0000000000000000000000000000000000000000..b7b0c1363d24fc502f7477ec3ba42120b80723a3
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/cstddef
@@ -0,0 +1,15 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * cstddef (header)
+ * - C++ wrapper around stddef.h
+ */
+#ifndef _LIBCXX_CSTDDEF_
+#define _LIBCXX_CSTDDEF_
+
+extern "C" {
+#include <stddef.h>
+};
+
+#endif
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/cstdint b/Usermode/Libraries/libc++.so_src/include_exp/cstdint
new file mode 100644
index 0000000000000000000000000000000000000000..c5575d9f232c0167eff709360db17ac4f38fab83
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/cstdint
@@ -0,0 +1,16 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * cstdint (header)
+ * - C++ wrapper around stdint.h
+ */
+#ifndef _LIBCXX_CSTDINT_
+#define _LIBCXX_CSTDINT_
+
+extern "C" {
+#include <stdint.h>
+};
+
+#endif
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/cstdio b/Usermode/Libraries/libc++.so_src/include_exp/cstdio
new file mode 100644
index 0000000000000000000000000000000000000000..f41fd058a1415cd66018f243744ae53bc798f8e9
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/cstdio
@@ -0,0 +1,6 @@
+/*
+ */
+extern "C" {
+#include <stdio.h>
+}
+// vim: ft=cpp
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/cstdlib b/Usermode/Libraries/libc++.so_src/include_exp/cstdlib
new file mode 100644
index 0000000000000000000000000000000000000000..7d119a76a924d065ea65f0a4c166fb8eaf0cde0d
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/cstdlib
@@ -0,0 +1,17 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * cstdlib (header)
+ * - C++ wrapper around stdlib.h
+ */
+#ifndef _LIBCXX_CSTDLIB_
+#define _LIBCXX_CSTDLIB_
+
+extern "C" {
+#include <stdlib.h>
+};
+
+#endif
+
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/cstring b/Usermode/Libraries/libc++.so_src/include_exp/cstring
new file mode 100644
index 0000000000000000000000000000000000000000..66149e035c98f672aba2a10f3f22f102ae62e266
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/cstring
@@ -0,0 +1,17 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * cstring (header)
+ * - C++ wrapper around string.h
+ */
+#ifndef _LIBCXX_CSTRING_
+#define _LIBCXX_CSTRING_
+
+extern "C" {
+#include <string.h>
+};
+
+#endif
+
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/cxxabi.h b/Usermode/Libraries/libc++.so_src/include_exp/cxxabi.h
index a8e1f5ca1c7fffef70a28c9bfe9d17307663d1c0..2fbe897942d0a32e1ee6bd2d351f4cbb90581826 100644
--- a/Usermode/Libraries/libc++.so_src/include_exp/cxxabi.h
+++ b/Usermode/Libraries/libc++.so_src/include_exp/cxxabi.h
@@ -8,6 +8,8 @@
 #ifndef _LIBCXX__CXXABI_H_
 #define _LIBCXX__CXXABI_H_
 
+#include <cstddef>
+
 #include <typeinfo>
 
 namespace __cxxabiv1 {
@@ -71,6 +73,13 @@ public:
 	};
 };
 
+extern "C" void* __dynamic_cast(
+		const void *sub,
+		const __class_type_info *src,
+		const __class_type_info *dst,
+		ptrdiff_t src2dst_offset
+		);
+
 };	// namespace __cxxabiv1
 
 #endif
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/exception b/Usermode/Libraries/libc++.so_src/include_exp/exception
new file mode 100644
index 0000000000000000000000000000000000000000..9e1469d0c88f2675db39ff67fafe1a1389246149
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/exception
@@ -0,0 +1,46 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * exception (header)
+ * - C++'s base exception type
+ */
+#ifndef _LIBCXX_EXCEPTION_
+#define _LIBCXX_EXCEPTION_
+
+#define noexcept	throw()
+
+namespace std {
+
+class exception
+{
+public:
+	exception() noexcept;
+	exception(const exception& e) noexcept;
+	exception& operator= (const exception& e) noexcept;
+	virtual ~exception() noexcept;
+	virtual const char* what() const noexcept;
+};
+
+class bad_exception:
+	public exception
+{
+public:
+	bad_exception() noexcept;
+	const char* what() const noexcept;
+};
+
+typedef void (*terminate_handler)();
+typedef void (*unexpected_handler)();
+
+extern void set_terminate(terminate_handler f) throw();
+extern void set_unexpected(unexpected_handler f) throw();
+extern void terminate();
+extern void unexpected();
+extern bool uncaught_exception() throw();
+
+};	// namespace std
+
+#endif
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/functional b/Usermode/Libraries/libc++.so_src/include_exp/functional
new file mode 100644
index 0000000000000000000000000000000000000000..1ac05dec00daa6108f2973fc01f3375e1bc9dce2
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/functional
@@ -0,0 +1,31 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * functional (header)
+ * - Funcional programming features
+ */
+#ifndef _LIBCXX_FUNCTIONAL_
+#define _LIBCXX_FUNCTIONAL_
+
+#include "_libcxx_helpers.h"
+
+namespace std {
+
+template <class T>
+struct less
+{
+	bool operator() (const T& x, const T& y) const {
+		return x < y;
+	}
+	typedef T	first_argument_type;
+	typedef T	second_argument_type;
+	typedef bool	result_type;
+};
+
+};	// namespace std
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/initializer_list b/Usermode/Libraries/libc++.so_src/include_exp/initializer_list
new file mode 100644
index 0000000000000000000000000000000000000000..565980fcb8a8a463f24c21150e5ae915312f55c8
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/initializer_list
@@ -0,0 +1,55 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * vector (header)
+ * - C++'s vector (dynamic array) type
+ */
+#ifndef _LIBCXX__INITIALIZER_LIST_
+#define _LIBCXX__INITIALIZER_LIST_
+
+namespace std {
+
+template <class T>
+class initializer_list
+{
+public:
+	typedef T	value_type;
+	typedef const T&	reference;
+	typedef const T&	const_reference;
+	typedef size_t	size_type;
+	typedef const T*	iterator;
+	typedef const T*	const_iterator;
+private:
+	// ORDER MATTERS : The first item must be a pointer to the array, the second must be the size
+	value_type*	m_values;
+	size_type	m_len;
+public:	
+	constexpr initializer_list() noexcept:
+		m_len(0)
+	{
+	}
+
+	size_type size() const noexcept
+	{
+		return m_len;
+	}
+	
+	const T* begin() const noexcept
+	{
+		return &m_values[0];
+	}
+	const T* end() const noexcept
+	{
+		return &m_values[m_len];
+	}
+};
+
+};
+
+template <class T> const T* begin(const ::std::initializer_list<T>& il) { return il.begin(); }
+template <class T> const T* end  (const ::std::initializer_list<T>& il) { return il.end(); }
+
+#endif
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/iterator b/Usermode/Libraries/libc++.so_src/include_exp/iterator
new file mode 100644
index 0000000000000000000000000000000000000000..e58ab803d3b8660c375b22516a78fe7af351ecc7
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/iterator
@@ -0,0 +1,35 @@
+/*
+ */
+#ifndef _LIBCXX_ITERATOR_
+#define _LIBCXX_ITERATOR_
+
+namespace std {
+
+struct input_iterator_tag {};
+struct output_iterator_tag {};
+struct forward_iterator_tag {};
+struct bidirectional_iterator_tag {};
+struct random_access_iterator_tag {};
+
+template <class Category, class T, class Distance = ptrdiff_t, class Pointer = T*, class Reference = T&>
+class iterator
+{
+public:
+	typedef T	value_type;	
+	typedef Distance	difference_type;
+	typedef Pointer	pointer_type;
+	typedef Reference	reference;
+	typedef Category	iterator_category;
+};
+
+template <class Iterator> class iterator_traits;
+template <class T> class iterator_traits<T*>;
+template <class T> class iterator_traits<const T*>;
+
+
+};	// namespace std
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/list b/Usermode/Libraries/libc++.so_src/include_exp/list
new file mode 100644
index 0000000000000000000000000000000000000000..3bc614e4656903f0b8f122f71d02ce38cfc9f6b5
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/list
@@ -0,0 +1,331 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * list (header)
+ * - List container
+ */
+#ifndef _LIBCXX_LIST_
+#define _LIBCXX_LIST_
+
+#include <cstddef>
+#include "allocator"
+#include "stdexcept"
+#include "utility"
+
+namespace std {
+
+namespace _bits {
+template <class ListType, class T> class list_iterator;
+template <class T> class list_item;
+}
+
+template <class T, class Alloc = allocator<T> >
+class list
+{
+	typedef ::std::_bits::list_item<T>	item_type;
+	typedef ::std::_bits::list_item<const T>	const_item_type;
+	friend class ::std::_bits::list_iterator<list, T>;
+	
+	typedef typename Alloc::template rebind<item_type>::other	item_allocator;
+public:
+	typedef T value_type;
+	typedef Alloc	allocator_type;
+	typedef typename allocator_type::reference	reference;
+	typedef typename allocator_type::const_reference	const_reference;
+	typedef typename allocator_type::pointer	pointer;
+	typedef typename allocator_type::const_pointer	const_pointer;
+	typedef _bits::list_iterator<list,T>	iterator;
+	typedef _bits::list_iterator<list,const T>	const_iterator;
+	typedef int	difference_type;
+	typedef size_t	size_type;
+
+private:
+	item_allocator	m_item_allocator;
+	allocator_type	m_allocator;
+	item_type	*m_start;
+	item_type	*m_end;
+	size_type	m_item_count;
+
+public:
+	list(const allocator_type& alloc = allocator_type()):
+		m_item_allocator(),
+		m_allocator(alloc),
+		m_start(0), m_end(0)
+	{
+	}
+	list(size_t n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type()):
+		list()
+	{
+		assign(n, val);
+	}
+	list(const list& x);
+	~list() {
+		clear();
+	}
+	
+	list& operator =(const list& x);
+	
+	iterator begin() {
+		return iterator(*this, m_start);
+	}
+	const_iterator begin() const {
+		return const_iterator(*this, m_start);
+	}
+
+	iterator end() {
+		return iterator(*this, 0);
+	}
+	const_iterator end() const {
+		return const_iterator(*this, 0);
+	}
+	
+	bool empty() const {
+		return !m_start;
+	}
+	size_t size() const {
+		return m_item_count;
+	}
+	size_t max_size() const {
+		return (size_type)-1 / sizeof(item_type);
+	}
+	
+	T& front() {
+		return m_start->value;
+	}
+	const T& front() const {
+		return m_start->value;
+	}
+	T& back() {
+		return m_end->value;
+	}
+	const T& back() const {
+		return m_end->value;
+	}
+	
+	void assign(size_type n, const value_type& val) {
+		clear();
+		for( size_t i = 0; i < n; i ++ )
+		{
+			push_back(val);
+		}
+	}
+	
+	void push_front(const value_type& val) {
+		insert(front(), val);
+	}
+	void pop_front() {
+		erase(front());
+	}
+	void push_back(const value_type& val) {
+		insert(end(), val);
+	}
+	void pop_back() {
+		erase(end());
+	}
+	
+	template <class... Args>
+	iterator emplace(iterator position, Args&&... args) {
+		item_type *newi = m_item_allocator.allocate(1);
+		m_allocator.construct(&newi->value, ::std::forward<Args>(args)...);
+		return insert_item(position, newi);
+	}
+	
+	iterator insert(iterator position, const value_type& val) {
+		item_type *newi = m_item_allocator.allocate(1);
+		m_allocator.construct(&newi->value, val);
+		return insert_item(position, newi);
+	}
+	void insert(iterator position, size_type n, const value_type& val) {
+		for( size_type i = 0; i < n; i ++ )
+		{
+			position = insert(position, val);
+		}
+	}
+	iterator erase(iterator position) {
+		if( position == end() ) {
+		}
+		else {
+			item_type *oldi = position.m_cur;
+			++ position;
+			
+			if(oldi->prev)
+				oldi->prev->next = oldi->next;
+			else
+				m_start = oldi->next;
+			if(oldi->next)
+				oldi->next->prev = oldi->prev;
+			else
+				m_end = oldi->prev;
+
+			m_item_count --;
+			m_allocator.destroy(&oldi->value);
+			m_item_allocator.deallocate(oldi, 1);
+		}
+		return position;
+	}
+	
+	void clear() {
+		while( m_start ) {
+			item_type* item = m_start;
+			m_start = m_start->next;
+			delete item;
+		}
+		m_item_count = 0;
+	}
+
+	void splice(iterator position, list& x) {
+		splice(position, x, x.begin(), x.end());
+	}
+	void splice(iterator position, list& x, iterator i) {
+		splice(position, x, i, x.end());
+	}
+	void splice(iterator position, list& x, iterator first, iterator last);
+
+private:
+	class _equal
+	{
+		const value_type&	m_val;
+	public:
+		_equal(const value_type& val):
+			m_val(val)
+		{
+		};
+		bool operator() (const value_type& v1)
+		{
+			return m_val == v1;
+		}
+	};
+public:
+	void remove(const value_type& val) {
+		remove_if(_equal(val));
+	}
+	template <class Predicate> void remove_if(Predicate pred) {
+		for( iterator it = begin(); it != end(); )
+		{
+			if( pred(*it) )
+				it = erase(it);
+			else
+				++ it;
+		}
+	}
+	
+	void unique();
+	template <class BinaryPredicate> void unique(BinaryPredicate binary_pred);
+	
+	void merge(list& x);
+	template <class Compare> void merge(list& x, Compare comp);
+	
+	void sort();
+	template <class Compare> void sort(Compare comp);
+	
+	void reverse() throw();
+private:
+	iterator insert_item(iterator position, item_type *newi) {
+		m_item_count ++;
+		if( m_start == 0 ) {
+			newi->next = 0;
+			newi->prev = m_end;
+			m_start = newi;
+			m_end = newi;
+			return end();
+		}
+		if( position == end() ) {
+			newi->next = 0;
+			newi->prev = m_end;
+			//assert(m_end);
+			m_end->next = newi;
+			m_end = newi;
+		}
+		else if( position == begin() ) {
+			newi->next = m_start;
+			newi->prev = 0;
+			//assert(m_start);
+			m_start->prev = newi;
+			m_start = newi;
+		}
+		else {
+			newi->prev = position.m_cur->prev;
+			newi->next = position.m_cur;
+			position.m_cur->prev->next = newi;
+			position.m_cur->prev = newi;
+		}
+		return ++iterator(*this, newi);
+	}
+};
+
+
+namespace _bits {
+
+template <class T>
+struct list_item
+{
+	typedef T	value_type;
+	list_item<T>	*next;
+	list_item<T>	*prev;
+	value_type	value;
+};
+
+template <class ListType, class T>
+class list_iterator//:
+	//public bidirectional_iterator_tag;
+{
+	const ListType*	m_list;
+	list_item<T>	*m_cur;
+	friend ListType;
+public:
+	list_iterator(const list_iterator& x):
+		m_list(x.m_list),
+		m_cur (x.m_cur)
+	{
+	}
+	list_iterator& operator=(const list_iterator& x) {
+		m_list = x.m_list;
+		m_cur  = x.m_cur;
+	}
+	
+	bool operator == (const list_iterator& other) const {
+		return m_cur == other.m_cur;
+	}
+	bool operator != (const list_iterator& other) const {
+		return !(*this == other);
+	}
+	
+	T& operator * () {
+		return m_cur->value;
+	}
+	T& operator -> () {
+		return m_cur->value;
+	}
+	list_iterator& operator ++ () {
+		if(!m_cur)
+			throw ::std::logic_error("list iterator ++ on end");
+		m_cur = m_cur->next;
+		return *this;
+	}
+	list_iterator& operator -- () {
+		if( m_cur == m_list->m_start )
+			throw ::std::logic_error("list iterator -- on start");
+		if(!m_cur)
+			m_cur = m_list->m_end;
+		else
+			m_cur = m_cur->prev;
+		return *this;
+	}
+	
+private:
+	list_iterator(const ListType& list, list_item<T> *item):
+		m_list(&list),
+		m_cur(item)
+	{
+	}
+};
+
+};	// namespace _bits
+
+};	// namespace std
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/map b/Usermode/Libraries/libc++.so_src/include_exp/map
new file mode 100644
index 0000000000000000000000000000000000000000..2f9031b93fac5d540692b0019c5f77119143433b
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/map
@@ -0,0 +1,418 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * map (header)
+ * - C++'s map type
+ */
+#ifndef _LIBCXX_MAP_
+#define _LIBCXX_MAP_
+
+#include "_libcxx_helpers.h"
+#include <allocator>
+#include <stdexcept>
+#include <iterator>
+#include <utility>
+#include <functional>	// less
+
+namespace std {
+
+namespace _bits {
+
+template <class Val, class Map>
+class map_iterator:
+	public ::std::iterator< ::std::bidirectional_iterator_tag, Val >
+{
+	friend Map;
+	typedef Val	value_type;
+	Map	*m_map;
+	size_t	m_index;
+public:
+	map_iterator():
+		m_map(0), m_index(0)
+	{
+	}
+	map_iterator(Map *map, typename Map::size_type ofs):
+		m_map(map), m_index(ofs)
+	{
+	}
+	map_iterator(const map_iterator& x) {
+		*this = x;
+	}
+	map_iterator& operator=(const map_iterator& x) {
+		m_map = x.m_map;
+		m_index = x.m_index;
+		return *this;
+	}
+	
+	map_iterator& operator++() {
+		m_index ++;
+	}
+	map_iterator& operator--() {
+		m_index --;
+	}
+	
+	bool operator==(const map_iterator& x) const {
+		return m_index == x.m_index;
+	}
+	bool operator!=(const map_iterator& x) const {
+		return !(*this == x);
+	}
+	value_type& operator*() {
+		_libcxx_assert(m_index < m_map->m_size);
+		return m_map->m_items[m_index];
+	}
+	value_type* operator->() {
+		_libcxx_assert(m_index < m_map->m_size);
+		return &m_map->m_items[m_index];
+	}
+};
+
+}	// namespace _bits
+
+template <class Key, class T, class Compare=less<Key>, class Alloc=allocator<pair<const Key,T> > >
+class map
+{
+	typedef map	this_type;
+public:
+	typedef Key	key_type;
+	typedef T	mapped_type;
+	typedef pair<const Key, T>	value_type;
+	typedef Compare	key_compare;
+	typedef Alloc	allocator_type;
+	typedef typename allocator_type::reference	reference;
+	typedef typename allocator_type::const_reference	const_reference;
+	typedef typename allocator_type::pointer	pointer;
+	typedef typename allocator_type::const_pointer	const_pointer;
+	typedef _bits::map_iterator<value_type,this_type>	iterator;
+	typedef _bits::map_iterator<const value_type,this_type>	const_iterator;
+	typedef size_t	size_type;
+
+private:
+	friend class ::std::_bits::map_iterator<value_type, this_type>;
+	friend class ::std::_bits::map_iterator<const value_type, this_type>;
+
+	key_compare	m_comp;
+	allocator_type	m_alloc;
+	value_type*	m_items;	// sorted array
+	size_type	m_size;
+	size_type	m_capacity;
+
+public:
+	map(const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()):
+		m_comp(comp),
+		m_alloc(alloc),
+		m_items(0), m_size(0), m_capacity(0)
+	{
+	}
+	template <class InputInterator>
+	map(InputInterator first, InputInterator last):
+		map()
+	{
+		insert(first, last);
+	}
+	map(const map& x):
+		map(x.m_comp, x.m_alloc)
+	{
+		*this = x;
+	}
+	~map() {
+		clear();
+		reserve(0);
+	}
+	map& operator=(const map& x) {
+		clear();
+		reserve(x.m_size);
+		for( size_type i = 0; i < x.m_size; i ++ ) {
+			emplace_hint( end(), x.m_items[i] );
+		}
+		return *this;
+	}
+	
+	// Iterators
+	iterator begin() {
+		return iterator(this, 0);
+	}
+	const_iterator begin() const {
+		return const_iterator(this, 0);
+	}
+	iterator end() {
+		return iterator(this, m_size);
+	}
+	const_iterator end() const {
+		return const_iterator(this, m_size);
+	}
+	#if _CXX11_AVAIL
+	const_iterator cbegin() const {
+		return const_iterator(this, 0);
+	}
+	const_iterator cend() const {
+		return const_iterator(this, m_size);
+	}
+	#endif
+	//reverse_iterator rbegin();
+	//const_reverse_iterator rbegin() const;
+	//const_reverse_iterator crbegin() const;
+	//reverse_iterator rend();
+	//const reverse_iterator rend() const;
+	//const reverse_iterator crend() const;
+	
+	// Capacity
+	bool empty() const {
+		return m_size == 0;
+	}
+	size_type size() const {
+		return m_size;
+	}
+	size_type max_size() const {
+		return (size_type)-1 / sizeof(value_type);
+	}
+	
+	// Element access
+	mapped_type& operator[](const key_type& k) {
+		iterator it = upper_bound(k);
+		if( it == end() || m_comp(k, it->first) ) {	// if k < it->first, no match
+			insert_at(it.m_index, value_type(k,mapped_type()) );
+		}
+		return it->second;
+	}
+	#if _CXX11_AVAIL
+	mapped_type& at(const key_type& k) {
+		iterator it = lower_bound(k);
+		if( it == end() || m_comp(it->first, k) )
+			throw ::std::out_of_range("::std:map::at");
+		return it->second;
+	}
+	const mapped_type& at(const key_type& k) const {
+		iterator it = lower_bound(k);
+		if( it == end() || m_comp(it->first, k) )
+			throw ::std::out_of_range("::std:map::at");
+		return it->second;
+	}
+	#endif
+	
+	// Modifiers
+	// - insert
+	pair<iterator,bool> insert(const value_type& val)
+	{
+		const key_type&	k = val.first;
+		iterator it = upper_bound(k);
+		if( it == end() || m_comp(k, it->first) ) {	// if k < it->first, no match
+			insert_at(it.m_index, val);
+			return pair<iterator,bool>(it, true);
+		}
+		else {
+			return pair<iterator,bool>(it, false);
+		}
+	}
+	iterator insert(iterator position, const value_type& val);
+	template <class InputInterator>
+	void insert(InputInterator first, InputInterator last);
+	// - erase
+	void erase(iterator position)
+	{
+		auto pos = position;
+		erase(pos, ++position);
+	}
+	size_type erase(const key_type& k)
+	{
+		auto it = find(k);
+		if( it != end() ) {
+			erase(it);
+			return 1;
+		}
+		else {
+			return 0;
+		}
+	}
+	void erase(iterator first, iterator last)
+	{
+		_libcxx_assert(first.m_index <= last.m_index);
+		unsigned int ofs = first.m_index;
+		unsigned int count = last.m_index - first.m_index;
+		for( unsigned int i = 0; i < count; i ++ )
+		{
+			// Construct new item
+			m_alloc.destroy(&m_items[ofs]);
+			m_size --;
+			// Move following items down
+			shift_items(&m_items[ofs+1], &m_items[ofs], m_size-ofs);
+		}
+	}
+	// - swap
+	void swap(map& x);
+	// - clear
+	void clear() {
+		for( size_type i = 0; i < m_size; i ++ ) {
+			m_alloc.destroy( &m_items[i] );
+		}
+		m_size = 0;
+	}
+	// - emplace
+	#if _CXX11_AVAIL
+	template <class... Args>
+	pair<iterator,bool> emplace(Args&&... args) {
+		return emplace_hint(begin(), args...);
+	}
+	template <class... Args>
+	pair<iterator,bool> emplace_hint(iterator position, Args&&... args);
+	#endif
+	
+	// TODO: Observers
+	
+	// Operators
+	iterator find(const key_type& k) {
+		iterator it = lower_bound(k);
+		if( it == end() || m_comp(it->first, k) )	// if it->first < k
+			return end();
+		return it;
+	}
+	const_iterator find(const key_type& k) const {
+		const_iterator it = lower_bound(k);
+		if( it == end() || m_comp(it->first, k) )	// if it->first < k
+			return end();
+		return it;
+	}
+	size_type count(const key_type& k) {
+		if( find(k) == end() )
+			return 0;
+		return 1;
+	}
+	iterator lower_bound(const key_type& k) {
+		size_type idx;
+		if( _search(k, idx) ) {
+			// found, return direct iterator
+			return iterator(this, idx);
+		}
+		else {
+			// not found, return iterator to before
+			if( idx == 0 )	return begin();
+			return iterator(this, idx-1);
+		}
+	}
+	const_iterator lower_bound(const key_type& k) const {
+		size_type idx;
+		if( _search(k, idx) ) {
+			// found, return direct iterator
+			return iterator(this, idx);
+		}
+		else {
+			// not found, return iterator to before
+			if( idx == 0 )	return begin();
+			return iterator(this, idx-1);
+		}
+	}
+	iterator upper_bound(const key_type& k) {
+		size_type idx;
+		_search(k, idx);
+		// idx == item or just after it
+		return iterator(this, idx);
+	}
+	const_iterator upper_bound(const key_type& k) const {
+		size_type idx;
+		_search(k, idx);
+		return const_iterator(this, idx);
+	}
+	pair<iterator,iterator> equal_range(const key_type& k);
+	pair<const_iterator,const_iterator> equal_range(const key_type& k) const;
+
+private:
+	// Change reservation to fit 'n' items
+	// - NOTE: Will also resize down
+	void reserve(size_type n) {
+		n = (n + 31) & ~31;
+		if( n > max_size() )
+			throw ::std::length_error("::std::map::reserve");
+		if( n != m_capacity )
+		{
+			auto new_area = m_alloc.allocate(n);
+			for( size_type i = 0; i < m_size && i < n; i ++ )
+				m_alloc.construct(&new_area[i], m_items[i]);
+			for( size_type i = n; i < m_size; i ++ )
+				m_alloc.destroy( &m_items[i] );
+			m_alloc.deallocate(m_items, m_capacity);
+			m_items = new_area;
+			m_capacity = n;
+			if(m_size > n)
+				m_size = n;
+		}
+	}
+	// Returns the index above or equal to 'k'
+	// - TODO: Reimplement as a binary search
+	bool _search(const key_type& k, size_type &pos_out) const {
+		#if 0
+		size_type pos = m_size / 2;
+		size_type step = m_size / 4;
+		while( step > 0 ) {
+			const key_type& item_key = m_items[pos].first;
+			if( m_comp(item_key, k) )
+				pos += step;
+			else if( m_comp(k, item_key) )
+				pos -= step;
+			else {
+				pos_out = pos;
+				return true;
+			}
+			step /= 2;
+		}
+		pos_out = pos;
+		return false;
+		#else
+		//::_sys::debug("map::_search (m_size=%i)", m_size);
+		for( size_type pos = 0; pos < m_size; pos ++ )
+		{
+			const key_type& item_key = m_items[pos].first;
+			if( m_comp(item_key, k) ) {
+				continue;
+			}
+			else if( m_comp(k, item_key) ) {
+				//::_sys::debug("map::_search - Passed %i", pos);
+				pos_out = pos;
+				return false;
+			}
+			else {
+				//::_sys::debug("map::_search - Found %i", pos);
+				pos_out = pos;
+				return true;
+			}
+		}
+		//::_sys::debug("map::_search - Off end %i", m_size);
+		pos_out = m_size;
+		return false;
+		#endif
+	}
+	void insert_at(size_type ofs, const value_type& val) {
+		//_libcxx_assert( ofs == 0 || m_comp(m_items[ofs-1].first, val.first) );
+		//_libcxx_assert( ofs == m_size || m_comp(m_items[ofs].first, val.first) );
+		// Resize up
+		reserve( m_size + 1 );
+		// Move following items up
+		shift_items(&m_items[ofs], &m_items[ofs+1], m_size-ofs);
+		// Construct new item
+		m_alloc.construct(&m_items[ofs], val);
+		m_size ++;
+	}
+	void shift_items(value_type *start, value_type *end, size_type count) {
+		if( start < end ) {
+			for( size_type i = count; i --; ) {
+				#if _CXX11_AVAIL && 0
+				m_alloc.construct(&end[i], ::std::move(start[i]));
+				#else
+				m_alloc.construct(&end[i], start[i]);
+				m_alloc.destroy(&start[i]);
+				#endif
+			}
+		}
+		else {
+			for( size_type i = 0; i < count; i ++ ) {
+			}
+		}
+	}
+};
+
+}	// namespace std
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/memory b/Usermode/Libraries/libc++.so_src/include_exp/memory
new file mode 100644
index 0000000000000000000000000000000000000000..b604fbe89e67ed74ab3f07c812e3251e64de0ceb
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/memory
@@ -0,0 +1,20 @@
+
+#ifndef _LIBCXX_MEMORY_
+#define _LIBCXX_MEMORY_
+
+namespace std {
+
+template <>
+class allocator<void>
+{
+public:
+  typedef void* pointer;
+  typedef const void* const_pointer;
+  typedef void value_type;
+  template <class U> struct rebind { typedef allocator<U> other; };
+};
+
+}
+
+#endif
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/mutex b/Usermode/Libraries/libc++.so_src/include_exp/mutex
new file mode 100644
index 0000000000000000000000000000000000000000..d3d9ffd379f17c81c8cdadc922124b2925feaa62
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/mutex
@@ -0,0 +1,76 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * mutex (header)
+ * - C++11's tutex handling
+ */
+#ifndef _LIBCXX_MUTEX_
+#define _LIBCXX_MUTEX_
+
+#include "_libcxx_helpers.h"
+
+#if !_CXX11_AVAIL
+# error	"<mutex> requires C++11 support"
+#endif
+
+namespace std {
+
+#if _CXX11_AVAIL
+
+class mutex
+{
+public:
+	constexpr mutex() noexcept:
+		m_flag(false)
+	{
+	}
+	mutex(const mutex&) = delete;
+	mutex& operator=(const mutex&) = delete;
+	~mutex();
+	
+	void lock();
+	bool try_lock();
+	void unlock();
+
+	typedef void*	native_handle;
+private:
+	// TODO: Proper userland mutex support
+	bool	m_flag;
+};
+
+struct defer_lock_t {};
+struct try_to_lock_t {};
+struct adopt_lock_t {};
+
+template< class Mutex >
+class lock_guard
+{
+public:
+	typedef Mutex	mutex_type;
+private:
+	mutex_type&	m_lock;
+public:
+	lock_guard(mutex_type& m):
+		m_lock(m)
+	{
+		m_lock.lock();
+	}
+	lock_guard(mutex_type& m, std::adopt_lock_t t):
+		m_lock(m)
+	{
+		// Adopted
+	}
+	~lock_guard() {
+		m_lock.unlock();
+	}
+};
+
+#endif
+
+};	// namespace std
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/new b/Usermode/Libraries/libc++.so_src/include_exp/new
new file mode 100644
index 0000000000000000000000000000000000000000..b895832905eeef4b40cca37466f3ac58a3c57a16
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/new
@@ -0,0 +1,45 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * new (header)
+ * - C++'s new operators
+ */
+#ifndef _LIBCXX_NEW_
+#define _LIBCXX_NEW_
+
+
+#include "cstddef"
+
+//extern void* operator new(size_t size) throw (::std::bad_alloc);
+//extern void* operator new(size_t size, const std::nothrow_t& nothrow_value) throw();
+inline void* operator new(size_t /*size*/, void* ptr) throw() {
+	return ptr;
+}
+
+//extern void* operator new[](size_t size) throw (::std::bad_alloc);
+//extern void* operator new[](size_t size, const std::nothrow_t& nothrow_value) throw();
+inline void* operator new[](size_t /*size*/, void* ptr) throw() {
+	return ptr;
+}
+
+#include "exception"
+
+namespace std {
+
+class bad_alloc:
+	public ::std::exception
+{
+public:
+	bad_alloc() noexcept;
+	~bad_alloc() noexcept;
+	
+	const char *what() const noexcept;
+};
+
+}	// namespace std
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/stdexcept b/Usermode/Libraries/libc++.so_src/include_exp/stdexcept
new file mode 100644
index 0000000000000000000000000000000000000000..75562073bf8ce252b3e020f94cca2ccd3ded0b25
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/stdexcept
@@ -0,0 +1,64 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * string (header)
+ * - C++'s String type
+ */
+#ifndef _LIBCXX_STDEXCEPT_
+#define _LIBCXX_STDEXCEPT_
+
+#include "exception"
+#include "string"
+
+namespace std {
+
+namespace _bits {
+
+class str_except:
+	public exception
+{
+	::std::string	m_str;
+public:
+	explicit str_except(const string& what_arg);
+	virtual ~str_except() noexcept;
+	str_except& operator= (const str_except& e) noexcept;
+	virtual const char* what() const noexcept;
+};
+
+} // namespace _bits
+
+class logic_error:
+	public _bits::str_except
+{
+public:
+	explicit logic_error(const string& what_arg);
+};
+
+class runtime_error:
+	public _bits::str_except
+{
+public:
+	explicit runtime_error(const string& what_arg);
+};
+
+class out_of_range:
+	public logic_error
+{
+public:
+	explicit out_of_range(const string& what_arg);
+};
+
+class length_error:
+	public logic_error
+{
+public:
+	explicit length_error(const string& what_arg);
+};
+
+};	// namespace std
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/string b/Usermode/Libraries/libc++.so_src/include_exp/string
new file mode 100644
index 0000000000000000000000000000000000000000..f36077dda2c1b5e07248ef6d0e8e85418b9bdf32
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/string
@@ -0,0 +1,604 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * string (header)
+ * - C++'s String type
+ */
+#ifndef _LIBCXX_STRING_
+#define _LIBCXX_STRING_
+
+#include "_libcxx_helpers.h"
+#include <allocator>
+#include <initializer_list>
+
+namespace std {
+
+template <class charT>
+struct char_traits
+{
+};
+
+template <>
+struct char_traits<char>
+{
+	typedef char	char_type;
+	typedef int	int_type;
+	//typedef streamoff	off_type;
+	//typedef streampos	pos_type;
+	//typedef mbstate_t	state_type;
+	
+	static bool eq(const char_type& c, const char_type& d) {
+		return c == d;
+	}
+	static bool lt(const char_type& c, const char_type& d) {
+		return c < d;
+	}
+	static size_t length(const char_type* s) {
+		size_t	ret = 0;
+		while(*s++)	ret ++;
+		return ret;
+	}
+	static int compare (const char_type* p, const char_type* q, size_t n) {
+ 		while (n--) {
+			if( !eq(*p,*q) )
+				return lt(*p,*q) ? -1 : 1;
+			++p; ++q;
+		}
+		return 0;
+	}
+};
+
+template <>
+struct char_traits<wchar_t>
+{
+	typedef wchar_t	char_type;
+	typedef int	int_type;
+	//typedef streamoff	off_type;
+	//typedef streampos	pos_type;
+	//typedef mbstate_t	state_type;
+	
+	static size_t length(const char_type* s) {
+		size_t	ret = 0;
+		while(*s++)	ret ++;
+		return ret;
+	}
+	static bool eq(const char_type& c, const char_type& d) {
+		return c == d;
+	}
+	static bool lt(const char_type& c, const char_type& d) {
+		return c < d;
+	}
+};
+
+extern void _throw_out_of_range(const char *message);
+
+template < class charT, class traits=char_traits<charT>, class Alloc=allocator<charT> >
+class basic_string
+{
+public:
+	typedef traits	traits_type;
+	typedef Alloc	allocator_type;
+	typedef charT	value_type;
+	typedef typename allocator_type::reference	reference;
+	typedef typename allocator_type::const_reference	const_reference;
+	typedef size_t	size_type;
+	
+	typedef charT*	iterator;
+	typedef const charT*	const_iterator;
+
+private:
+	struct dynamic_info
+	{
+		allocator_type	m_allocator;
+		 int	m_ref_count = 1;
+		size_type	m_capacity = 0;
+		size_type	m_size = 0;
+		typename allocator_type::pointer m_data = 0;
+		dynamic_info(const allocator_type& alloc):
+			m_allocator(alloc)
+		{
+		}
+		dynamic_info(const dynamic_info& other):
+			m_allocator(other.m_allocator),
+			m_ref_count(1),
+			m_capacity(other.m_capacity),
+			m_size(other.m_size)
+		{
+			m_data = m_allocator.allocate(m_capacity);
+			for( size_type i = 0; i < m_size; i ++ )
+				m_data[i] = other.m_data[i];
+		}
+	};
+
+	allocator_type	m_allocator;
+	dynamic_info	*m_content;
+
+public:
+	basic_string(const allocator_type& alloc = allocator_type()):
+		m_allocator(alloc),
+		m_content(0)
+	{
+	}
+	basic_string(const basic_string& str) throw():
+		basic_string(allocator_type())
+	{
+		*this = str;
+	}
+	#if _CXX11_AVAIL
+	basic_string(basic_string&& str):
+		m_allocator(str.m_allocator),
+		m_content(str.m_content)
+	{
+		str.m_content = 0;
+		::_sys::debug("basic_string(move) %p %s", m_content, c_str());
+	}
+	#endif
+	basic_string(const basic_string& str, const allocator_type& alloc):
+		basic_string(str, 0, str.length(), alloc)
+	{
+	}
+	basic_string(const basic_string& str, size_type pos, size_type len = npos, const allocator_type& alloc = allocator_type()):
+		basic_string(alloc)
+	{
+		if( pos < str.length() )
+		{
+			if( len > str.length() - pos )
+				len = str.length() - pos;
+			reserve(len);
+			for( size_type i = 0; i < len; i ++ )
+				m_content->m_data[i] = str.m_content->m_data[pos+i];
+			m_content->m_size = len;
+		}
+	}
+	basic_string(const charT *s, const allocator_type& alloc = allocator_type()):
+		basic_string(s, traits::length(s), alloc)
+	{
+	}
+	basic_string(const charT *s, size_type n, const allocator_type& alloc = allocator_type()):
+		basic_string(alloc)
+	{
+		if( n > 0 )
+		{
+			reserve(n);
+			for( size_type i = 0; i < n; i ++ )
+				m_content->m_data[i] = s[i];
+			m_content->m_data[n] = 0;
+			m_content->m_size = n;
+		}
+	}
+	basic_string(size_type n, charT c, const allocator_type& alloc = allocator_type()):
+		basic_string(alloc)
+	{
+		if( n > 0 )
+		{
+			reserve(n);
+			for( size_type i = 0; i < n; i ++ )
+				m_content->m_data[i] = c;
+			m_content->m_data[n] = 0;
+			m_content->m_size = n;
+		}
+	}
+	#if __cplusplus < 199711L
+	basic_string(basic_string&& str) noexcept:
+		basic_string(allocator_type())
+	{
+	}
+	basic_string(basic_string&& str, const allocator_type& alloc) noexcept:
+		basic_string(alloc)
+	{
+		m_content = str.m_content;
+		str.m_content = 0;
+	}
+	#endif
+	~basic_string()
+	{
+		release_content();
+	}
+	basic_string& operator=(const basic_string& str) throw() {
+		return assign(str);
+	}
+	basic_string& operator=(const charT* s) {
+		return assign(s);
+	}
+	basic_string& operator=(charT c) {
+		return assign(c);
+	}
+	
+	// iterators
+	
+	// capacity
+	size_type size() const {
+		return m_content ? m_content->m_size : 0;
+	}
+	size_type length() const {
+		return size();
+	}
+	size_type max_size() const {
+		return -1;
+	}
+	void resize(size_type size, charT c = 0) {
+		reserve(size);
+		if( m_content->m_size < size ) {
+			for( size_type ofs = m_content->m_size; ofs < size; ofs ++ )
+				m_content->m_data[ofs] = c;
+			m_content->m_data[size] = 0;
+		}
+		m_content->m_size = size;
+		m_content->m_data[size] = 0;
+	}
+	size_type capacity() const {
+		return m_content ? m_content->m_capacity : 0;
+	}
+	void reserve(size_type size) {
+		own_content();
+		size = (size+1 + 31) & ~31;
+		if( size > m_content->m_capacity ) {
+			auto new_area = m_allocator.allocate(size);
+			for( size_type i = 0; i < m_content->m_size; i ++ )
+				new_area[i] = m_content->m_data[i];
+			m_allocator.deallocate(m_content->m_data, m_content->m_capacity);
+			m_content->m_data = new_area;
+			m_content->m_capacity = size;
+		}
+	}
+	void clear() {
+		own_content();
+		m_content->m_size = 0;
+	}
+	bool empty() const {
+		return length() == 0;
+	}
+	#if _CXX11_AVAIL
+	void shrink_to_fit();
+	#endif
+	
+	// Access
+	reference operator[] (size_type pos) {
+		own_content();
+		return m_content->m_data[pos];
+	}
+	const_reference operator[] (size_type pos) const {
+		return (m_content ? m_content->m_data[pos] : *(const charT*)0);
+	}
+	reference at(size_type pos) {
+		own_content();
+		if(pos >= m_content->m_size)
+			_throw_out_of_range("basic_string - at");
+		return m_content->m_data[pos];
+	}
+	const_reference at(size_type pos) const {
+		if(!m_content || pos >= m_content.m_size)
+			_throw_out_of_range("basic_string - at");
+		return m_content->m_data[pos];
+	}
+	#if _CXX11_AVAIL
+	reference back() {
+		own_content();
+		return m_content->m_data[m_content->m_size];
+	}
+	const_reference back() const {
+		return m_content->m_data[m_content->m_size];
+	}
+	reference front() {
+		own_content();
+		return m_content->m_data[0];
+	}
+	const_reference front() const {
+		return m_content->m_data[0];
+	}
+	#endif
+	
+	// Modifiers
+	basic_string& operator +=(const basic_string& str) {
+		return append(str);
+	}
+	basic_string& operator +=(const charT* s) {
+		return append(s);
+	}
+	basic_string& operator +=(charT c) {
+		push_back(c);
+		return *this;
+	}
+	basic_string& append(const basic_string& str) {
+		return append(str, 0, npos);
+	}
+	basic_string& append(const basic_string& str, size_type subpos, size_type sublen) {
+		if(subpos >= str.size())
+			_throw_out_of_range("basic_string - assign source");
+		if( sublen > str.size() - subpos )
+			sublen = str.size() - subpos;
+		append( str.data() + subpos, sublen );
+		return *this;
+	}
+	basic_string& append(const charT* s) {
+		return append(s, traits::length(s));
+	}
+	basic_string& append(const charT* s, size_type n) {
+		reserve(size() + n);
+		for( size_type i = 0; i < n; i ++ )
+			m_content->m_data[size() + i] = s[i];
+		m_content->m_data[size()+n] = '\0';
+		m_content->m_size += n;
+		return *this;
+	}
+	basic_string& append(size_type n, charT c) {
+		reserve(size() + n);
+		for( size_type i = 0; i < n; i ++ )
+			m_content->m_data[size() + i] = c;
+		m_content->m_data[size()+n] = '\0';
+		m_content->m_size += n;
+		return *this;
+	}
+	void push_back(charT c) {
+		append(1, c);
+	}
+	basic_string& assign(const basic_string& str) throw() {
+		// Special case, triggers copy-on-write.
+		release_content();
+		m_content = str.m_content;
+		m_content->m_ref_count ++;
+		return *this;
+	}
+	basic_string& assign(const basic_string& str, size_type subpos, size_type sublen) {
+		if(subpos >= str.size())
+			_throw_out_of_range("basic_string - assign source");
+		if( sublen > str.size() - subpos )
+			sublen = str.size() - subpos;
+		
+		return assign(str.data() + subpos, sublen);
+	}
+	basic_string& assign(const charT* s) {
+		return assign(s, traits::length(s));
+	}
+	basic_string& assign(const charT* s, size_type n) {
+		release_content();
+		reserve(n);
+		for( size_type i = 0; i < n; i ++ )
+			m_content->m_data[i] = s[i];
+		m_content->m_data[n] = '\0';
+		m_content->m_size = n;
+		return *this;
+	}
+	basic_string& assign(size_type n, charT c) {
+		release_content();
+		reserve(n);
+		for( size_type i = 0; i < n; i ++ )
+			m_content->m_data[i] = c;
+		m_content->m_data[n] = '\0';
+		m_content->m_size = n;
+		return *this;
+	}
+	
+	basic_string& insert(size_type pos, const basic_string& str);
+	basic_string& insert(size_type pos, const basic_string& str, size_type subpos, size_type sublen);
+	basic_string& insert(size_type pos, const charT& s);
+	basic_string& insert(size_type pos, const charT& s, size_type n);
+	basic_string& insert(size_type pos, size_type n, charT c);
+	iterator insert(const_iterator p, size_type n, charT c);
+	iterator insert(const_iterator p, charT c);
+	template <class InputIterator>
+	iterator insert(iterator p, InputIterator first, InputIterator last);
+	#if _CXX11_AVAIL
+	basic_string& insert(const_iterator p, initializer_list<charT> il);
+	#endif
+	
+	basic_string& erase(size_type pos = 0, size_type len = npos);
+	iterator erase(const_iterator p);
+	iterator erase(const_iterator first, const_iterator last);
+	
+	basic_string& replace(size_type pos, size_type len, const basic_string& str);
+	basic_string& replace(const_iterator i1, const_reference i2, const basic_string& str);
+	basic_string& replace(size_type pos, size_type len, const basic_string& str, size_type subpos, size_type sublen);
+	basic_string& replace(size_type pos, size_type len, const charT *s);
+	basic_string& replace(const_iterator i1, const_reference i2, const charT* s);
+	basic_string& replace(size_type pos, size_type len, const charT *s, size_type n);
+	basic_string& replace(const_iterator i1, const_reference i2, const charT* s, size_type n);
+	basic_string& replace(size_type pos, size_type len, size_type n, charT c);
+	basic_string& replace(const_iterator i1, const_reference i2, size_type n, charT c);
+	template <class InputIterator>
+	basic_string& replace(const_iterator i1, const_reference i2, InputIterator first, InputIterator last);
+	basic_string& replace(const_iterator i1, const_iterator i2, initializer_list<charT> il);
+	
+	void swap(basic_string& str)
+	{
+		auto tmp = m_content;
+		m_content = str.m_content;
+		str.m_content = tmp;
+	}
+	
+	#if _CXX11_AVAIL
+	void pop_back();
+	#endif
+	
+	// String operations
+	const charT *c_str() const noexcept
+	{
+		// TODO: this is const, but also might need to do processing
+		if(m_content) {
+			_libcxx_assert(m_content->m_data[m_content->m_size] == '\0');
+		}
+		return (m_content ? m_content->m_data : "");
+	}
+	const charT *data() const
+	{
+		return (m_content ? m_content->m_data : NULL);
+	}
+	allocator_type get_allocator() const noexcept
+	{
+		return m_allocator;
+	}
+	size_type copy(charT* s, size_type len, size_type pos = 0) const;
+	
+	size_type find(const basic_string& str, size_type pos = 0) const noexcept;
+	size_type find(const charT* s, size_type pos = 0) const;
+	size_type find(const charT* s, size_type pos, size_type n) const;
+	size_type find(charT c, size_type pos = 0) const noexcept;
+	
+	size_type rfind(const basic_string& str, size_type pos = 0) const noexcept;
+	size_type rfind(const charT* s, size_type pos = 0) const;
+	size_type rfind(const charT* s, size_type pos, size_type n) const;
+	size_type rfind(charT c, size_type pos = 0) const noexcept;
+	
+	size_type find_first_of(const basic_string& str, size_type pos = 0) const noexcept;
+	size_type find_first_of(const charT* s, size_type pos = 0) const;
+	size_type find_first_of(const charT* s, size_type pos, size_type n) const;
+	size_type find_first_of(charT c, size_type pos = 0) const noexcept;
+	
+	size_type find_last_of(const basic_string& str, size_type pos = 0) const noexcept;
+	size_type find_last_of(const charT* s, size_type pos = 0) const;
+	size_type find_last_of(const charT* s, size_type pos, size_type n) const;
+	size_type find_last_of(charT c, size_type pos = 0) const noexcept;
+
+	size_type find_first_not_of(const basic_string& str, size_type pos = 0) const noexcept;
+	size_type find_first_not_of(const charT* s, size_type pos = 0) const;
+	size_type find_first_not_of(const charT* s, size_type pos, size_type n) const;
+	size_type find_first_not_of(charT c, size_type pos = 0) const noexcept;
+
+	size_type find_last_not_of(const basic_string& str, size_type pos = 0) const noexcept;
+	size_type find_last_not_of(const charT* s, size_type pos = 0) const;
+	size_type find_last_not_of(const charT* s, size_type pos, size_type n) const;
+	size_type find_last_not_of(charT c, size_type pos = 0) const noexcept;
+
+	basic_string substr(size_type pos = 0, size_type len = npos) const;
+	
+	int compare(const basic_string& str) const noexcept {
+		return compare(0, size(), str.data(), str.size());
+	}
+	int compare(size_type pos, size_type len, const basic_string& str) const {
+		_libcxx_assert(pos <= size());
+		_libcxx_assert(len <= size());
+		_libcxx_assert(pos+len <= size());
+		return compare(pos, len, str.data(), str.size());
+	}
+	int compare(size_type pos, size_type len, const basic_string& str, size_type subpos, size_type sublen) const {
+		// TODO: check
+		_libcxx_assert(subpos <= str.size());
+		_libcxx_assert(sublen <= str.size());
+		_libcxx_assert(subpos+sublen <= str.size());
+		return compare(pos, len, str.data()+subpos, sublen);
+	}
+	int compare(const charT* s) const {
+		return compare(0, npos, s, traits::length(s));
+	}
+	int compare(size_type pos, size_type len, const charT* s) const {
+		return compare(pos, len, s, traits::length(s));
+	}
+	int compare(size_type pos, size_type len, const charT* s, size_type n) const {
+		if( n <= len ) {
+			int rv = traits::compare(data()+pos, s, n);
+			if( rv == 0 && n < len ) {
+				rv = -1;
+			}
+			return rv;
+		}
+		else {
+			int rv = traits::compare(data()+pos, s, len);
+			if(rv == 0) {
+				rv = 1;
+			}
+			return rv;
+		}
+	}
+	
+	static const size_type npos = -1;
+private:
+	void own_content() {
+		if(!m_content)
+		{
+			m_content = new dynamic_info(m_allocator);
+		}
+		else if( m_content->m_ref_count > 1 )
+		{
+			dynamic_info *new_cont = new dynamic_info(*m_content);
+			m_content->m_ref_count --;
+			m_content = new_cont;
+		}
+		else
+		{
+			// already owned
+		}
+	}
+	void release_content() {
+		if( m_content )
+		{
+			m_content->m_ref_count --;
+			if( m_content->m_ref_count == 0 ) {
+				m_allocator.deallocate(m_content->m_data, m_content->m_capacity);
+				delete m_content;
+			}
+			m_content = NULL;
+		}
+	}
+};
+
+typedef basic_string<char>	string;
+
+#define _libcxx_str	basic_string<charT,traits,Alloc>
+
+template <class charT, class traits, class Alloc>
+basic_string<charT,traits,Alloc> operator+(const basic_string<charT,traits,Alloc>& lhs, const basic_string<charT,traits,Alloc>& rhs)
+{
+	basic_string<charT,traits,Alloc>	ret;
+	ret.reserve(lhs.size() + rhs.size());
+	ret += lhs;
+	ret += rhs;
+	return ret;
+}
+template <class charT, class traits, class Alloc>
+basic_string<charT,traits,Alloc> operator+(const basic_string<charT,traits,Alloc>& lhs, const charT* rhs)
+{
+	basic_string<charT,traits,Alloc>	ret;
+	ret.reserve(lhs.size() + traits::length(rhs));
+	ret += lhs;
+	ret += rhs;
+	return ret;
+}
+template <class charT, class traits, class Alloc>
+basic_string<charT,traits,Alloc> operator+(const charT* lhs, const basic_string<charT,traits,Alloc>& rhs)
+{
+	basic_string<charT,traits,Alloc>	ret;
+	ret.reserve(traits::length(lhs) + rhs.size());
+	ret += lhs;
+	ret += rhs;
+	return ret;
+}
+template <class charT, class traits, class Alloc>
+basic_string<charT,traits,Alloc> operator+(const basic_string<charT,traits,Alloc>& lhs, const charT rhs)
+{
+	basic_string<charT,traits,Alloc>	ret;
+	ret.reserve(lhs.size() + 1);
+	ret += lhs;
+	ret += rhs;
+	return ret;
+}
+template <class charT, class traits, class Alloc>
+basic_string<charT,traits,Alloc> operator+(const charT lhs, const basic_string<charT,traits,Alloc>& rhs)
+{
+	basic_string<charT,traits,Alloc>	ret;
+	ret.reserve(1 + rhs.size());
+	ret += lhs;
+	ret += rhs;
+	return ret;
+}
+
+// Three overloads each
+// name: Actual operator, opp: reversed operator
+#define _libcxx_string_def_cmp(name, opp) \
+	template <class charT, class traits, class Alloc> \
+	bool operator name(const _libcxx_str& lhs, const _libcxx_str& rhs) { return lhs.compare(rhs) name 0; } \
+	template <class charT, class traits, class Alloc> \
+	bool operator name(const charT* lhs, const _libcxx_str& rhs) { return rhs.compare(lhs) opp 0; } \
+	template <class charT, class traits, class Alloc> \
+	bool operator name(const _libcxx_str& lhs, const charT* rhs) { return lhs.compare(rhs) name 0; }
+
+_libcxx_string_def_cmp(<, >)
+_libcxx_string_def_cmp(<=, >=)
+_libcxx_string_def_cmp(==, ==)
+_libcxx_string_def_cmp(>=, <=)
+_libcxx_string_def_cmp(>, <)
+};
+
+#endif
+
+// vim: ft=cpp
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/system_error b/Usermode/Libraries/libc++.so_src/include_exp/system_error
new file mode 100644
index 0000000000000000000000000000000000000000..496970eb08b048bbe6112564589fb4b2b72bdb27
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/system_error
@@ -0,0 +1,189 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * system_error (header)
+ * - C++11's system_error exception
+ */
+#ifndef _LIBCXX_SYSTEM_ERROR_
+#define _LIBCXX_SYSTEM_ERROR_
+
+#include "_libcxx_helpers.h"
+
+#if !_CXX11_AVAIL
+# error "This header requires C++11 support enabled"
+#endif
+
+#include <exception>
+#include <string>
+
+namespace std {
+
+class error_category;
+class error_condition;
+class error_code;
+
+static bool operator==(const error_condition& lhs, const error_condition& rhs) noexcept;
+static bool operator!=(const error_condition& lhs, const error_condition& rhs) noexcept;
+static bool operator< (const error_condition& lhs, const error_condition& rhs) noexcept;
+static bool operator==(const error_condition& lhs, const error_code& rhs) noexcept;
+static bool operator==(const error_code& lhs, const error_condition& rhs) noexcept;
+static bool operator!=(const error_condition& lhs, const error_code& rhs) noexcept;
+static bool operator!=(const error_code& lhs, const error_condition& rhs) noexcept;
+
+extern const error_category&	generic_category() noexcept;
+extern const error_category&	system_category() noexcept;
+
+class error_condition
+{
+	 int	m_val;
+	const error_category*	m_cat;
+public:
+	error_condition() noexcept:
+		error_condition(0, ::std::generic_category())
+	{
+	}
+	error_condition(int val, const error_category& cat) noexcept:
+		m_val(val),
+		m_cat(&cat)
+	{
+	}
+	//template <class ErrorConditionEnum> error_condition(ErrorConditionEnum e) noexcept;
+	//template <class ErrorConditionEnum> error_condition& operator=(ErrorConditionEnum e) noexcept;
+	void assign(int val, const error_category& cat) noexcept {
+		m_val = val;
+		m_cat = &cat;
+	}
+	void clear() noexcept {
+		assign(0, ::std::generic_category());
+	}
+	int value() const noexcept {
+		return m_val;
+	}
+	const error_category& category() const noexcept {
+		return *m_cat;
+	}
+	string message() const;
+	explicit operator bool() const noexcept {
+		return m_val != 0;
+	}
+};
+
+class error_category
+{
+public:
+	error_category() {
+	}
+	error_category(const error_category&) = delete;	// disallow copying
+	virtual ~error_category() noexcept {
+	}
+	bool operator==(const error_category& rhs) const noexcept {
+		return this == &rhs;
+	}
+	bool operator!=(const error_category& rhs) const noexcept {
+		return !(*this == rhs);
+	}
+	bool operator<(const error_category& rhs) const noexcept {
+		return this < &rhs;
+	}
+	virtual const char* name() const noexcept = 0;
+	virtual error_condition default_error_condition(int val) const noexcept {
+		return error_condition(val, *this);
+	}
+	virtual bool equivalent(int valcode, const ::std::error_condition& cond) const noexcept {
+		return default_error_condition(valcode) == cond;
+	}
+	virtual bool equivalent(const error_code& code, int valcond) const noexcept;	// in system_error.cc
+	virtual ::std::string message(int val) const = 0;
+};
+
+class error_code
+{
+	int	m_ev;
+	const ::std::error_category*	m_ecat;
+public:
+	error_code() noexcept:
+		error_code(0, ::std::generic_category())
+	{
+	}
+	error_code(int ev, const ::std::error_category& ecat) noexcept:
+		m_ev(ev),
+		m_ecat(&ecat)
+	{
+	}
+	//template <class ErrorCodeEnum>
+	//error_code(ErrorCodeEnum e) noexcept;
+	void assign(int val, const error_category& ecat) noexcept {
+		m_ev = val;
+		m_ecat = &ecat;
+	}
+	//template <class ErrorCodeEnum>
+	//error_code& operator= (ErrorCodeEnum e) noexcept;
+	void clear() noexcept {
+		m_ev = 0;
+		m_ecat = 0;
+	}
+	int value() const noexcept {
+		return m_ev;
+	}
+	const error_category& category() const noexcept {
+		return *m_ecat;
+	}
+	error_condition default_error_condition() const noexcept {
+		return category().default_error_condition(value());
+	}
+	::std::string message() const {
+		return category().message(value());
+	}
+	operator bool() const noexcept {
+		return m_ev != 0;
+	}
+};
+
+class system_error:
+	public ::std::exception
+{
+	const error_code	m_error_code;
+	::std::string	m_what_str;
+public:
+	system_error(::std::error_code ec);
+	system_error(::std::error_code ec, const ::std::string& what_arg);
+	system_error(::std::error_code ec, const char* what_arg);
+	system_error(int ev, const ::std::error_category& ecat);
+	system_error(int ev, const ::std::error_category& ecat, const ::std::string& what_arg);
+	system_error(int ev, const ::std::error_category& ecat, const char* what_arg);
+	~system_error() noexcept;
+	
+	const char* what() const noexcept;
+};
+
+static inline bool operator==(const error_condition& lhs, const error_condition& rhs) noexcept {
+	return lhs.category() == rhs.category() && lhs.value() == rhs.value();
+}
+static inline bool operator!=(const error_condition& lhs, const error_condition& rhs) noexcept {
+	return !(lhs == rhs);
+}
+static inline bool operator< (const error_condition& lhs, const error_condition& rhs) noexcept {
+	return lhs.category() < rhs.category() || lhs.value() < rhs.value();
+}
+static inline bool operator==(const error_condition& lhs, const error_code& rhs) noexcept {
+	return lhs.category().equivalent(rhs, lhs.value()) || rhs.category().equivalent(rhs.value(), lhs);
+}
+static inline bool operator==(const error_code& lhs, const error_condition& rhs) noexcept {
+	return lhs.category().equivalent(lhs.value(),rhs) || rhs.category().equivalent(lhs,rhs.value());
+}
+static inline bool operator!=(const error_condition& lhs, const error_code& rhs) noexcept {
+	return !(lhs == rhs);
+}
+static inline bool operator!=(const error_code& lhs, const error_condition& rhs) noexcept {
+	return !(lhs == rhs);
+}
+
+
+
+};	// namespace std
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/type_traits b/Usermode/Libraries/libc++.so_src/include_exp/type_traits
new file mode 100644
index 0000000000000000000000000000000000000000..62103ac2f1c36c53a5589f07bcb0f413a790f103
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/type_traits
@@ -0,0 +1,24 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * type_traits (header)
+ * - C++11 type traits
+ */
+#ifndef _LIBCXX_TYPE_TRAITS_
+#define _LIBCXX_TYPE_TRAITS_
+
+#include "_libcxx_helpers.h"
+
+#if !_CXX11_AVAIL
+# error "This header requires C++11 support enabled"
+#endif
+
+template <class T> struct remove_reference	{ typedef T	type; };
+template <class T> struct remove_reference<T&>	{ typedef T	type; };
+template <class T> struct remove_reference<T&&>	{ typedef T	type; };
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/typeinfo b/Usermode/Libraries/libc++.so_src/include_exp/typeinfo
index c02d388b79d5191019a13ac80ac62e32c96adc5b..c8b037ceab3ff39e5879b28938bd3539dec8d6d1 100644
--- a/Usermode/Libraries/libc++.so_src/include_exp/typeinfo
+++ b/Usermode/Libraries/libc++.so_src/include_exp/typeinfo
@@ -8,6 +8,8 @@
 #ifndef _LIBCXX__TYPEINFO_
 #define _LIBCXX__TYPEINFO_
 
+#include <cstddef>
+
 namespace std {
 
 // Type information class
@@ -19,9 +21,16 @@ public:
 	bool operator!=(const type_info &) const;
 	bool before(const type_info &) const;
 	const char* name() const;
+	
+	// acess
+	bool __is_child(const type_info &, size_t&) const;
 private:
 	type_info (const type_info& rhs);
 	type_info& operator= (const type_info& rhs);
+
+	// acess
+	bool is_class() const;
+	bool is_subclass() const;
 	
 	// CXX ABI
 	const char *__type_name;
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/utility b/Usermode/Libraries/libc++.so_src/include_exp/utility
new file mode 100644
index 0000000000000000000000000000000000000000..b3e40c53b8961619ebfb21abeee4556eb1927136
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/utility
@@ -0,0 +1,91 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * string (header)
+ * - C++'s String type
+ */
+#ifndef _LIBCXX_UTILITY_
+#define _LIBCXX_UTILITY_
+
+#include "_libcxx_helpers.h"
+#include "type_traits"
+
+namespace std {
+
+template <class T1, class T2>
+class pair
+{
+public:
+	typedef T1	first_type;
+	typedef T2	second_type;
+	
+	first_type	first;
+	second_type	second;
+	
+	pair()
+	{
+	}
+	template <class U, class V>
+	pair(const pair<U,V>& pr):
+		first (pr.first),
+		second(pr.second)
+	{
+	}
+	pair(const first_type& a, const second_type& b):
+		first (a),
+		second(b)
+	{
+	}
+	pair(const pair& pr):
+		first(pr.first),
+		second(pr.second)
+	{
+	}
+	pair(pair&& pr):
+		first(pr.first), second(pr.second)
+	{
+	}
+	// operator = is implicit
+	pair& operator=(const pair& x) {
+		first  = x.first;
+		second = x.second;
+		return *this;
+	}
+};
+
+template <class T1, class T2>	
+bool operator== (const pair<T1,T2>& lhs, const pair<T1,T2>& rhs) {
+	return lhs.first == rhs.first && lhs.second == rhs.second;
+}
+template <class T1, class T2>	
+bool operator!= (const pair<T1,T2>& lhs, const pair<T1,T2>& rhs) {
+	return !(lhs == rhs);
+}
+
+#if _CXX11_AVAIL
+template <class T>
+T&& forward(typename remove_reference<T>::type& arg) noexcept {
+	return static_cast<decltype(arg)&&>(arg);
+}
+template <class T>
+T&& forward(typename remove_reference<T>::type&& arg) noexcept {
+	return static_cast<decltype(arg)&&>(arg);
+}
+
+template <class T>
+typename remove_reference<T>::type&& move( T&& t) noexcept {
+	return static_cast<typename remove_reference<T>::type&&>(t);
+}
+//template <class T>
+//constexpr typename ::std::remove_reference<T>::type&& move( T&& t) noexcept {
+//	return static_cast<typename ::std::remove_reference<T>::type&&>(t);
+//}
+#endif
+
+};	// namespace std
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/include_exp/vector b/Usermode/Libraries/libc++.so_src/include_exp/vector
new file mode 100644
index 0000000000000000000000000000000000000000..ab42c9c889fcfb1700535aed5cc33178f4e03438
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/include_exp/vector
@@ -0,0 +1,397 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * vector (header)
+ * - C++'s vector (dynamic array) type
+ */
+#ifndef _LIBCXX_VECTOR_
+#define _LIBCXX_VECTOR_
+
+#include <allocator>
+#include <stdexcept>
+#include <initializer_list>
+
+extern "C" void _SysDebug(const char *, ...);
+
+namespace std {
+
+namespace _bits {
+template <class VectorType, class T>
+class vector_iterator//:
+	//public random_acess_iterator_tag
+{
+	friend VectorType;
+	
+	typedef typename VectorType::size_type	size_type;
+	typedef typename VectorType::difference_type	difference_type;
+	
+	T*	m_array;
+	size_type	m_pos;
+	size_type	m_max;
+public:
+	vector_iterator():
+		vector_iterator(0,0,0)
+	{
+	}
+	vector_iterator(const vector_iterator& x):
+		vector_iterator()
+	{
+		*this = x;
+	}
+	vector_iterator(T* array, size_type start, size_type max):
+		m_array(array),
+		m_pos(start),
+		m_max(max)
+	{
+	}
+	vector_iterator& operator=(const vector_iterator& x)
+	{
+		m_array = x.m_array;
+		m_pos = x.m_pos;
+		m_max = x.m_max;
+		return *this;
+	}
+	bool operator==(const vector_iterator& other) const {
+		return m_pos == other.m_pos;
+	}
+	bool operator!=(const vector_iterator& other) const {
+		return !(*this == other);
+	}
+	T& operator*() const {
+		return m_array[m_pos];
+	}
+	T& operator->() const {
+		return m_array[m_pos];
+	}
+	T& operator[](difference_type n) {
+		return *(*this + n);
+	}
+	vector_iterator& operator++() {
+		if(m_pos < m_max) {
+			m_pos ++;
+		}
+		return *this;
+	}
+	const vector_iterator operator++(int) {
+		vector_iterator ret(*this);
+		++*this;
+		return ret;
+	}
+	vector_iterator& operator--() {
+		if(m_pos > 0) {
+			m_pos --;
+		}
+		return *this;
+	}
+	const vector_iterator operator--(int) {
+		vector_iterator ret(*this);
+		--*this;
+		return ret;
+	}
+	vector_iterator& operator+=(difference_type n) {
+		if( n < 0 )
+			return (*this -= -n);
+		if( n > 0 )
+			m_pos = (m_pos + n < m_max ? m_pos + n : m_max); 
+		return *this;
+	}
+	vector_iterator& operator-=(difference_type n) {
+		if( n < 0 )
+			return (*this += -n);
+		if( n > 0 )
+			m_pos = (m_pos >= n ? m_pos - n : 0);
+		return *this;
+	}
+	const difference_type operator-(const vector_iterator& it2) const {
+		//_libcxx_assert(m_array == it2.m_array);
+		return m_pos - it2.m_pos;
+	}
+	bool operator<(const vector_iterator& o) const { return m_pos < o.m_pos; }
+	bool operator>(const vector_iterator& o) const { return m_pos > o.m_pos; }
+	bool operator<=(const vector_iterator& o) const { return m_pos <= o.m_pos; }
+	bool operator>=(const vector_iterator& o) const { return m_pos >= o.m_pos; }
+};
+#define vector_iterator_tpl class VectorType, class T
+#define vector_iterator	vector_iterator<VectorType, T>
+template <vector_iterator_tpl>
+const vector_iterator operator+(const vector_iterator& it, typename VectorType::difference_type n) {
+	return vector_iterator(it) += n;
+}
+template <vector_iterator_tpl>
+const vector_iterator operator+(typename VectorType::difference_type n, const vector_iterator& it) {
+	return vector_iterator(it) += n;
+}
+template <vector_iterator_tpl>
+const vector_iterator operator-(const vector_iterator& it, typename VectorType::difference_type n) {
+	return vector_iterator(it) -= n;
+}
+#undef vector_iterator_tpl
+#undef vector_iterator
+
+}
+
+template <class T, class Alloc = allocator<T> >
+class vector
+{
+public:
+	typedef T	value_type;
+	typedef Alloc	allocator_type;
+	typedef typename allocator_type::reference	reference;
+	typedef typename allocator_type::const_reference	const_reference;
+	typedef typename allocator_type::pointer	pointer;
+	typedef typename allocator_type::const_pointer	const_pointer;
+	typedef int	difference_type;
+	typedef size_t	size_type;
+	typedef ::std::_bits::vector_iterator<vector,T>	iterator;
+	typedef ::std::_bits::vector_iterator<vector,const T>	const_iterator;
+
+private:
+	allocator_type	m_alloc;
+	size_type	m_size;
+	size_type	m_capacity;
+	value_type*	m_data;
+
+public:	
+	vector(const allocator_type& alloc = allocator_type()):
+		m_alloc(alloc),
+		m_size(0),
+		m_capacity(0),
+		m_data(0)
+	{
+	}
+	vector(size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type()):
+		vector(alloc)
+	{
+		resize(n, val);
+	}
+	template <class InputIterator>
+	vector(InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type()):
+		vector(alloc)
+	{
+		insert(begin(), first, last);
+	}
+	vector(const vector& x):
+		vector(x.m_alloc)
+	{
+		*this = x;
+	}
+	#if _CXX11_AVAIL
+	vector(vector&& x):
+		m_alloc(x.m_alloc),
+		m_size(x.m_size),
+		m_capacity(x.m_capacity),
+		m_data(x.m_data)
+	{
+		x.m_data = nullptr;
+		x.m_capacity = 0;
+		x.m_size = 0;
+	}
+	vector(vector&& x, const allocator_type& alloc):
+		m_alloc(alloc),
+		m_size(x.m_size),
+		m_capacity(x.m_capacity),
+		m_data(x.m_data)
+	{
+		x.m_data = nullptr;
+		x.m_capacity = 0;
+		x.m_size = 0;
+	}
+	vector(std::initializer_list<value_type> il, const allocator_type& alloc = allocator_type()):
+		vector(alloc)
+	{
+		reserve(il.size());
+		insert(begin(), il.begin(), il.end());
+	}
+	#endif
+	vector& operator=(const vector& x)
+	{
+		clear();
+		m_alloc.deallocate(m_data, m_capacity);
+		m_capacity = 0;
+		m_data = nullptr;
+		
+		reserve(x.size());
+		for( size_type i = 0; i < x.size(); i ++ )
+			push_back( x[i] );
+		
+		return *this;
+	}
+	
+	~vector()
+	{
+		clear();
+		m_alloc.deallocate(m_data, m_capacity);
+		m_capacity = 0;
+		m_data = nullptr;
+	}
+	
+	// Iterators
+	iterator       begin()       { return iterator_to(0); }
+	const_iterator begin() const { return iterator_to(0); }
+	iterator       end()       { return iterator_to(m_size); }
+	const_iterator end() const { return iterator_to(m_size); }
+	
+	// Capacity
+	size_type size() const {
+		return m_size;
+	}
+	size_type max_size() const {
+		return -1 / sizeof(value_type);
+	}
+	void resize(size_type new_cap, value_type val = value_type()) {
+		reserve(new_cap);
+		if( new_cap > m_size )
+		{
+			for( size_type i = m_size; i < new_cap; i ++ ) {
+				m_alloc.construct( &m_data[i], val );
+			}
+		}
+		else
+		{
+			for( size_type i = new_cap; i < m_size; i ++ )
+				m_alloc.destroy( &m_data[i] );
+		}
+		m_size = new_cap;
+	}
+	size_type capacity() const {
+		return m_capacity;
+	}
+	bool empty() const {
+		return m_size == 0;
+	}
+	void reserve(size_type n) {
+		if( n > max_size() )
+			throw ::std::length_error("::std::vector::reserve");
+		if( n > m_capacity )
+		{
+			size_type size = (n + 0x1F) & ~0x1F;
+			auto new_area = m_alloc.allocate(size);
+			for( size_type i = 0; i < m_size; i ++ )
+				new_area[i] = m_data[i];
+			m_alloc.deallocate(m_data, m_capacity);
+			m_data = new_area;
+			m_capacity = size;
+			//::_SysDebug("::std::vector::resize - m_capacity=%i for n=%i", m_capacity, n);
+		}
+	}
+	void shrink_to_fit() {
+	}
+	
+	// Element access
+	reference operator[] (size_type n) {
+		return m_data[n];
+	}
+	const_reference operator[] (size_type n) const {
+		return m_data[n];
+	}
+	reference at(size_type n) {
+		if(n > size())
+			_throw_out_of_range("::std::vector - at");
+		return m_data[n];
+	}
+	const_reference at(size_type n) const {
+		if(n > size())
+			_throw_out_of_range("::std::vector - at");
+		return m_data[n];
+	}
+	reference front() {
+		return m_data[0];
+	}
+	const_reference front() const {
+		return m_data[0];
+	}
+	reference back() {
+		return m_data[size()-1];
+	}
+	const_reference back() const {
+		return m_data[size()-1];
+	}
+	pointer data() noexcept {
+		return m_data;
+	}
+	const_pointer data() const noexcept {
+		return m_data;
+	}
+	
+	// Modifiers
+	void assign(size_type n, const value_type& val) {
+		clear();
+		resize(n, val);
+	}
+	void push_back(const value_type& val) {
+		resize(size()+1, val);
+	}
+	void pop_back() {
+		if( !empty() ) {
+			resize(size()-1);
+		}
+	}
+	iterator insert(iterator position, const value_type& val) {
+		insert(position, 1, val);
+		return iterator_to(position.m_pos);
+	}
+	void insert(iterator position, size_type n, const value_type& val) {
+		reserve(m_size + n);
+		if( position != end() ) {
+			::_sys::debug("TODO: vector::insert within vector (%i!=%i)",
+				position-begin(), end()-begin());
+			::_sys::abort();
+		}
+		size_type	pos = m_size;
+		while( n -- )
+		{
+			//::_sys::debug("vector::insert - %x at %i", val, pos);
+			m_alloc.construct( &m_data[pos], val );
+			pos ++;
+			m_size ++;
+		}
+	}
+	template <class InputIterator>
+	void insert(iterator position, InputIterator first, InputIterator last) {
+		InputIterator	it = first;
+		size_type	len = 0;
+		while(it != last) {
+			++ it;
+			len ++;
+		}
+		reserve(m_size + len);
+		
+		it = first;
+		while(it != last)
+		{
+			//::_sys::debug("vector::insert - to %i, from %p:%i",
+			//	position.m_pos, it.m_array, it.m_pos);
+			position = insert(position, *it) + 1;
+			++it;
+		}
+	}
+	iterator erase(iterator position);
+	iterator erase(iterator first, iterator last);
+	//void swap(vector& x) {
+	//	::std::swap(m_size, x.m_size);
+	//	::std::swap(m_capacity, x.m_capacity);
+	//	::std::swap(m_data, x.m_data);
+	//}
+	void clear() {
+		for( size_type i = 0; i < m_size; i ++ ) {
+			m_alloc.destroy( &m_data[i] );
+		}
+		m_size = 0;
+	}
+private:
+	iterator iterator_to(size_type index) {
+		_libcxx_assert(index <= m_size);
+		return iterator(m_data, index, m_size);
+	}
+	const_iterator iterator_to(size_type index) const {
+		_libcxx_assert(index <= m_size);
+		return const_iterator(m_data, index, m_size);
+	}
+};
+
+};	// namespace std
+
+#endif
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc++.so_src/misc.cc b/Usermode/Libraries/libc++.so_src/misc.cc
index ed0a2eb63c431b321c60eda34a71dbd94df1fbfc..2ce7a5178b5c1db85cd7b129e6c2d0b0b2a47bc5 100644
--- a/Usermode/Libraries/libc++.so_src/misc.cc
+++ b/Usermode/Libraries/libc++.so_src/misc.cc
@@ -6,9 +6,13 @@
  * - Miscelanious functions
  */
 #include <string.h>
+#include <acess/sys.h>
+#include <exception>
 
 extern "C" int SoMain()
 {
+	//extern void _init();
+	//_init();
 	// nope
 	return 0;
 }
@@ -16,11 +20,8 @@ extern "C" int SoMain()
 extern "C" void __cxa_pure_virtual()
 {
 	// dunno
-}
-
-extern "C" void __gxx_personality_v0()
-{
-	// TODO: Handle __gxx_personality_v0 somehow
+	::_SysDebug("__cxa_pure_virtual by %p", __builtin_return_address(0));
+	::std::terminate();
 }
 
 
diff --git a/Usermode/Libraries/libc++.so_src/mutex.cc b/Usermode/Libraries/libc++.so_src/mutex.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5ff27dfa8392dfbbfed7e7cbcf7a95e772e5f8ad
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/mutex.cc
@@ -0,0 +1,39 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * mutex.cc
+ * - ::std::mutex and helpers
+ */
+#include <mutex>
+#include <system_error>
+#include <cerrno>
+
+// === CODE ===
+::std::mutex::~mutex()
+{
+	
+}
+
+void ::std::mutex::lock()
+{
+	if( m_flag )
+	{
+		_sys::debug("TODO: userland mutexes");
+		throw ::std::system_error(ENOTIMPL, ::std::system_category());
+	}
+	m_flag = true;
+}
+
+bool ::std::mutex::try_lock()
+{
+	bool rv = m_flag;
+	m_flag = true;
+	return !rv;
+}
+
+void ::std::mutex::unlock()
+{
+	m_flag = false;
+}
+
diff --git a/Usermode/Libraries/libc++.so_src/new.cc b/Usermode/Libraries/libc++.so_src/new.cc
index 1d09fbc3dc1f09facd291503d1a6752befc8a831..f1ebc453143f88b2dc990e1ba52b1fce743d364b 100644
--- a/Usermode/Libraries/libc++.so_src/new.cc
+++ b/Usermode/Libraries/libc++.so_src/new.cc
@@ -7,25 +7,51 @@
  */
 #include <stddef.h>
 #include <stdlib.h>
+#include <acess/sys.h>
+#include <new>
+
+// === IMPORTS ===
+extern "C" bool	_libc_free(void *mem);	// from libc.so, actual free.
 
 // === CODE ===
 void *operator new( size_t size )
 {
+	//_SysDebug("libc++ - operator new(%i)", size);
 	return malloc( size );
 }
 
 void *operator new[]( size_t size )
 {
+	//_SysDebug("libc++ - operator new[](%i)", size);
 	return malloc( size );
 }
 
 void operator delete(void *ptr)
 {
-	free(ptr);
+	if( !_libc_free(ptr) ) {
+		_SysDebug("delete of invalid by %p", __builtin_return_address(0));
+		throw ::std::bad_alloc();
+	}
 }
 
 void operator delete[](void *ptr)
 {
-	free(ptr);
+	if( !_libc_free(ptr) ) {
+		_SysDebug("delete[] of invalid by %p", __builtin_return_address(0));
+		throw ::std::bad_alloc();
+	}
+}
+
+
+::std::bad_alloc::bad_alloc() noexcept
+{
+}
+::std::bad_alloc::~bad_alloc() noexcept
+{
+}
+
+const char *::std::bad_alloc::what() const noexcept
+{
+	return "allocation failure";
 }
 
diff --git a/Usermode/Libraries/libc++.so_src/string.cc b/Usermode/Libraries/libc++.so_src/string.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f063e3434b85141123a2f33fe231c8ccc3a25e4c
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/string.cc
@@ -0,0 +1,15 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * exceptions.cc
+ * - ::std::exception and friends
+ */
+#include <string>
+#include <stdexcept>
+
+void ::std::_throw_out_of_range(const char *message)
+{
+	throw ::std::out_of_range( ::std::string(message) );
+}
+
diff --git a/Usermode/Libraries/libc++.so_src/system_error.cc b/Usermode/Libraries/libc++.so_src/system_error.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9a9fb4d2352db0302a6bf7d21775f5d18a8c31c1
--- /dev/null
+++ b/Usermode/Libraries/libc++.so_src/system_error.cc
@@ -0,0 +1,107 @@
+/*
+ * Acess2 C++ Library
+ * - By John Hodge (thePowersGang)
+ *
+ * system_error.cc
+ * - ::std::system_error and other helpers
+ */
+#include <system_error>
+#include <cerrno>
+
+namespace std {
+
+system_error::system_error(::std::error_code ec):
+	m_error_code(ec),
+	m_what_str( (::std::string)ec.category().name() + ":" + ec.message())
+{
+	::_sys::debug("system_error(%s:%s)", ec.category().name(), ec.message().c_str());
+}
+system_error::system_error(::std::error_code ec, const ::std::string& what_arg):
+	system_error(ec)
+{
+	m_what_str += " - ";
+	m_what_str += what_arg;
+}
+system_error::system_error(::std::error_code ec, const char* what_arg):
+	system_error(ec)
+{
+	m_what_str += " - ";
+	m_what_str += what_arg;
+}
+system_error::system_error(int ev, const ::std::error_category& ecat):
+	system_error( ::std::error_code(ev, ecat) )
+{
+}
+system_error::system_error(int ev, const ::std::error_category& ecat, const ::std::string& what_arg):
+	system_error(ev, ecat)
+{
+	m_what_str += " - ";
+	m_what_str += what_arg;
+}
+system_error::system_error(int ev, const ::std::error_category& ecat, const char* what_arg):
+	system_error(ev, ecat)
+{
+	m_what_str += " - ";
+	m_what_str += what_arg;
+}
+
+system_error::~system_error() noexcept
+{
+}
+
+const char* system_error::what() const noexcept
+{
+	return m_what_str.c_str();
+}
+
+
+bool error_category::equivalent(const error_code& code, int valcond) const noexcept {
+	return *this == code.category() && code.value() == valcond;
+}
+
+
+class class_generic_category:
+	public error_category
+{
+public:
+	class_generic_category() {
+	}
+	~class_generic_category() noexcept {
+	}
+	const char *name() const noexcept {
+		return "generic";
+	}
+	::std::string message(int val) const {
+		return ::std::string( ::strerror(val) );
+	}
+} g_generic_category;
+
+const ::std::error_category& generic_category() noexcept
+{
+	return g_generic_category;
+}
+
+
+class class_system_category:
+	public error_category
+{
+public:
+	class_system_category() {
+	}
+	~class_system_category() noexcept {
+	}
+	const char *name() const noexcept {
+		return "system";
+	}
+	::std::string message(int val) const {
+		return ::std::string( ::strerror(val) );
+	}
+} g_system_category;
+
+const ::std::error_category& system_category() noexcept
+{
+	return g_system_category;
+}
+
+};	// namespace std
+
diff --git a/Usermode/Libraries/libc++.so_src/typeinfo.cc b/Usermode/Libraries/libc++.so_src/typeinfo.cc
index b0a9b3e1b9020d2455e3337e380a4aaa5c16f4da..99bfedc5de1ce40e459ac1de54e064da592aa34d 100644
--- a/Usermode/Libraries/libc++.so_src/typeinfo.cc
+++ b/Usermode/Libraries/libc++.so_src/typeinfo.cc
@@ -6,6 +6,9 @@
  * - typeid and dynamic_cast
  */
 #include <typeinfo>
+#include <cxxabi.h>
+#include <acess/sys.h>
+#include <cstdlib>
 
 namespace std {
 
@@ -16,16 +19,19 @@ type_info::~type_info()
 
 bool type_info::operator==(const type_info& other) const
 {
+	//_SysDebug("type_info::operator== - '%s' == '%s'", this->__type_name, other.__type_name);
 	return this->__type_name == other.__type_name;
 }
 
 bool type_info::operator!=(const type_info& other) const
 {
+	//_SysDebug("type_info::operator!= - '%s' != '%s'", this->__type_name, other.__type_name);
 	return this->__type_name != other.__type_name;
 }
 
 bool type_info::before(const type_info& other) const
 {
+	//_SysDebug("type_info::before - '%s' < '%s'", this->__type_name, other.__type_name);
 	return this->__type_name < other.__type_name;
 }
 
@@ -41,10 +47,76 @@ type_info::type_info(const type_info& rhs):
 }
 type_info& type_info::operator=(const type_info& rhs)
 {
+	_SysDebug("type_info::operator=, was %s now %s", __type_name, rhs.__type_name);
 	__type_name = rhs.__type_name;
 	return *this;
 }
 
+bool type_info::is_class() const
+{
+	if( typeid(*this) == typeid(::__cxxabiv1::__class_type_info) )
+		return true;
+	if( is_subclass() )
+		return true;
+	return false;
+}
 
-};	// namespace std
+bool type_info::is_subclass() const
+{
+	if( typeid(*this) == typeid(::__cxxabiv1::__si_class_type_info) )
+		return true;
+	if( typeid(*this) == typeid(::__cxxabiv1::__vmi_class_type_info) )
+		return true;
+	return false;
+}
+
+// Acess-defined
+bool type_info::__is_child(const type_info &poss_child, size_t &offset) const
+{
+	_SysDebug("typeids = this:%s , poss_child:%s", typeid(*this).name(), typeid(poss_child).name());
+
+	// Check #1: Child is same type
+	if( poss_child == *this ) {
+		offset = 0;
+		return true;
+	}
+	
+	// Check #2: This type must be a class
+	if( !this->is_class() ) {
+		return false;
+	}
+	// Check #3: Child class must be a subclass
+	if( !poss_child.is_subclass() ) {
+		return false;
+	}
+	
+	if( typeid(poss_child) == typeid(::__cxxabiv1::__si_class_type_info) ) {
+		auto &si_poss_child = reinterpret_cast<const ::__cxxabiv1::__si_class_type_info&>(poss_child);
+		// Single inheritance
+		_SysDebug("type_info::__is_child - Single inheritance");
+		return __is_child( *si_poss_child.__base_type, offset );
+	}
+	else if( typeid(poss_child) == typeid(::__cxxabiv1::__vmi_class_type_info) ) {
+		// Multiple inheritance
+		_SysDebug("TODO: type_info::__is_child - Multiple inheritance");
+		abort();
+		for(;;);
+	}
+	else {
+		// Oops!
+		_SysDebug("ERROR: type_info::__is_child - Reported subclass type, but not a subclass %s",
+			typeid(poss_child).name()
+			);
+		abort();
+		for(;;);
+	}
+}
 
+
+// NOTE: Not defined by the C++ ABI, but is similar to one defined by GCC (__cxa_type_match
+//bool __acess_type_match(std::typeinfo& possibly_derived, const std::typeinfo& base_type)
+//{
+//	return false;
+//}
+
+};	// namespace std
diff --git a/Usermode/Libraries/libc++_extras.so_src/Makefile b/Usermode/Libraries/libc++_extras.so_src/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..f129b612ce26cf65f9bcf6b4beebb564e83bb494
--- /dev/null
+++ b/Usermode/Libraries/libc++_extras.so_src/Makefile
@@ -0,0 +1,19 @@
+# Acess2 Basic C Library
+# Makefile
+
+-include ../Makefile.cfg
+
+CPPFLAGS += 
+CXXFLAGS += -Wall -Werror -Wextra -std=c++11
+CFLAGS   += -Wall -Werror -Wextra
+ASFLAGS  +=
+LDFLAGS  += 
+LIBS     += -lc++
+
+include ../Makefile.tpl
+
+%.native: %.cpp
+	$(NCXX) $< -o $@ -Wall -std=c++11 -I include_exp/
+
+TEST_cprintf.native: include_exp/cxxextras_printf
+
diff --git a/Usermode/Libraries/libc++_extras.so_src/TEST_cprintf.cpp b/Usermode/Libraries/libc++_extras.so_src/TEST_cprintf.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b77b00400ddc98412c10a9cfda299bd929c59e14
--- /dev/null
+++ b/Usermode/Libraries/libc++_extras.so_src/TEST_cprintf.cpp
@@ -0,0 +1,29 @@
+/*
+ */
+#include <cxxextras_printf>
+#include <cstdio>
+#include <stdexcept>
+
+void my_puts(const char *str, size_t len)
+{
+	fwrite(str, 1, len, stdout);
+}
+
+#define ASSERT_EXCEPTION(expr, Exception) do{bool _ok=false; try { expr; } catch(const Exception& e){_ok=true;}if(!_ok)throw ::std::runtime_error("Didn't throw "#Exception);}while(0)
+
+int main()
+{
+	printf("Success\n");
+	cprintf(my_puts, "%s %i %+#-010x\n", "hello_world", 1337, 0x1234565);
+	
+	//printf("Too Few\n");
+	//ASSERT_EXCEPTION( ::cxxextras::cprintf(my_puts, "%s %i %+#-010x\n"), ::cxxextras::cprintf_toofewargs );
+	//printf("Too Many\n");
+	//ASSERT_EXCEPTION( ::cxxextras::cprintf(my_puts, "%s\n", "tst", 12345), ::cxxextras::cprintf_toomanyargs );
+	//
+	//printf("Bad Format\n");
+	//ASSERT_EXCEPTION( ::cxxextras::cprintf(my_puts, "%-\n"), ::cxxextras::cprintf_badformat );
+	
+	return 0;
+}
+
diff --git a/Usermode/Libraries/libc++_extras.so_src/include_exp/cxxextras_printf b/Usermode/Libraries/libc++_extras.so_src/include_exp/cxxextras_printf
new file mode 100644
index 0000000000000000000000000000000000000000..7d05e21938356c9757fdc7b02c5085580d878202
--- /dev/null
+++ b/Usermode/Libraries/libc++_extras.so_src/include_exp/cxxextras_printf
@@ -0,0 +1,473 @@
+/*
+ */
+#ifndef _LIBCXXEXTRAS_PRINTF_
+#define _LIBCXXEXTRAS_PRINTF_
+
+#include <cstddef>
+#include <cstdlib>
+#include <cstdio>
+#include <functional>
+
+namespace cxxextras {
+
+class cprintf_toomanyargs:
+	public ::std::exception
+{
+};
+class cprintf_toofewargs:
+	public ::std::exception
+{
+};
+class cprintf_badformat:
+	public ::std::exception
+{
+	const char *m_reason;
+public:
+	cprintf_badformat(const char *reason):
+		m_reason(reason)
+	{
+	}
+	const char* what() const noexcept override {
+		return m_reason;
+	}
+};
+
+namespace _bits {
+
+enum e_cprintf_type
+{
+	TYPE_AUTO,
+	TYPE_BOOLEAN,
+	TYPE_BINARY,
+	TYPE_OCT,
+	TYPE_INT,
+	TYPE_INTU,
+	TYPE_INTS,
+	TYPE_HEXLC,
+	TYPE_HEXUC,
+	TYPE_STRING,
+};
+
+struct PrintfFlags
+{
+	unsigned int width;
+	unsigned int precision;
+	unsigned int flags;
+	
+	struct Left {};
+	constexpr PrintfFlags(PrintfFlags x, Left  _): width(x.width), precision(x.precision), flags(x.flags | 1) {}
+	struct Sign {};
+	constexpr PrintfFlags(PrintfFlags x, Sign  _): width(x.width), precision(x.precision), flags(x.flags | 2) {}
+	struct Space {};
+	constexpr PrintfFlags(PrintfFlags x, Space _): width(x.width), precision(x.precision), flags(x.flags | 4) {}
+	struct Alt {};
+	constexpr PrintfFlags(PrintfFlags x, Alt   _): width(x.width), precision(x.precision), flags(x.flags | 8) {}
+	struct Zero {};
+	constexpr PrintfFlags(PrintfFlags x, Zero  _): width(x.width), precision(x.precision), flags(x.flags | 16) {}
+	
+	struct ArgWidth {};
+	constexpr PrintfFlags(PrintfFlags x, ArgWidth _, unsigned int v): width(v), precision(x.precision), flags(x.flags) {}
+	struct ArgPrec {};
+	constexpr PrintfFlags(PrintfFlags x, ArgPrec  _, unsigned int v): width(x.width), precision(v), flags(x.flags) {}
+	
+	struct FAuto {};
+	constexpr PrintfFlags(PrintfFlags x, FAuto   _): width(x.width), precision(x.precision), flags(x.flags | 0x000) {}
+	struct FString {};
+	constexpr PrintfFlags(PrintfFlags x, FString _): width(x.width), precision(x.precision), flags(x.flags | 0x100) {}
+	struct FBool {};
+	constexpr PrintfFlags(PrintfFlags x, FBool   _): width(x.width), precision(x.precision), flags(x.flags | 0x200) {}
+	struct FBinary {};
+	constexpr PrintfFlags(PrintfFlags x, FBinary _): width(x.width), precision(x.precision), flags(x.flags | 0x300) {}
+	struct FOct {};
+	constexpr PrintfFlags(PrintfFlags x, FOct   _): width(x.width), precision(x.precision), flags(x.flags | 0x400) {}
+	struct FUDec {};
+	constexpr PrintfFlags(PrintfFlags x, FUDec  _): width(x.width), precision(x.precision), flags(x.flags | 0x500) {}
+	struct FSDec {};
+	constexpr PrintfFlags(PrintfFlags x, FSDec  _): width(x.width), precision(x.precision), flags(x.flags | 0x600) {}
+	struct FHexL {};
+	constexpr PrintfFlags(PrintfFlags x, FHexL  _): width(x.width), precision(x.precision), flags(x.flags | 0x700) {}
+	struct FHexU {};
+	constexpr PrintfFlags(PrintfFlags x, FHexU  _): width(x.width), precision(x.precision), flags(x.flags | 0x800) {}
+	
+	constexpr PrintfFlags():
+		width(0), precision(0), flags(0)
+	{
+	}
+};
+struct s_cprintf_fmt
+{
+	bool isValid = false;
+	unsigned int precision = 0;
+	unsigned int minLength = 0;
+	bool	padLeft = false;
+	bool	showSign = false;
+	bool	showSpace = false;
+	bool	altFormat = false;
+	bool	padZero = false;
+	enum e_cprintf_type	type;
+};
+;
+
+constexpr bool isdigit_s(const char ch) {
+	return '0' <= ch && ch <= '9';
+}
+constexpr unsigned todigit(const char ch) {
+	return ch - '0';
+}
+	
+
+};	// namespace _bits
+
+typedef ::std::function<void(const char*,size_t)>	cprintf_cb;
+template <typename Arg> size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags &fmt, Arg arg);
+template <typename Arg> constexpr bool cprintf_val_chk(const _bits::PrintfFlags fmt, Arg arg);
+
+constexpr bool cprintf_val_chk(const _bits::PrintfFlags fmt, const char* arg) {
+	return true;
+}
+size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags& fmt, const char* arg) {
+	unsigned int len;
+	for(len = 0; arg[len]; len ++)
+		;
+	puts(arg, len);
+	return len;
+}
+constexpr bool cprintf_val_chk(const _bits::PrintfFlags& fmt, int arg) {
+	return true;
+}
+size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags& fmt, int arg) {
+	size_t len = ::std::snprintf(nullptr, 0, "%i", arg);
+	char buf[len+1];
+	::std::snprintf(buf, len+1, "%i", arg);
+	puts(buf, len);
+	return len;
+}
+constexpr bool cprintf_val_chk(const _bits::PrintfFlags& fmt, unsigned int arg) {
+	return true;
+}
+size_t cprintf_val(cprintf_cb puts, const _bits::PrintfFlags& fmt, unsigned int arg) {
+	size_t len = ::std::snprintf(nullptr, 0, "%u", arg);
+	char buf[len+1];
+	::std::snprintf(buf, len+1, "%u", arg);
+	puts(buf, len);
+	return len;
+}
+
+namespace _bits
+{
+namespace _printf
+{
+	template <unsigned N>
+	class _str
+	{
+		const char	m_buf[N];
+		unsigned m_ofs;
+	public:
+		constexpr _str(const char buf[N]):
+			m_buf(buf),
+			m_ofs(0)
+		{
+		}
+		constexpr _str(const char buf[N], unsigned ofs):
+			m_buf(buf),
+			m_ofs(ofs)
+		{
+		}
+		constexpr _str<N> operator+(const unsigned o) {
+			return _str(m_buf, m_ofs+o);
+		}
+		constexpr char operator*() {
+			return m_buf[m_ofs];
+		}
+	};
+	
+	template <typename... Args>
+	constexpr bool val(const char* fmt, Args... args);
+	
+	template <typename Fmt>
+	constexpr bool val_fmt_done(const char * fmt, PrintfFlags item, Fmt fmtcode)
+	{
+		return false ? false : throw "Too few arguments";
+	}
+	template <typename Fmt, typename Arg, typename... Args>
+	constexpr bool val_fmt_done(const char* fmt, PrintfFlags item, Fmt fmtcode, Arg arg, Args... args)
+	{
+		return cprintf_val_chk(item, arg) && val(fmt+1, args...);
+	}
+	// --- Format code
+	template <typename ...Args>
+	constexpr bool val_fmt_fmt(const char * fmt, PrintfFlags item, Args... args)
+	{
+		return *fmt == '\0' ? throw "ERROR: NUL byte in format specifier"
+		     : *fmt == '?' ? val_fmt_done(fmt, item, PrintfFlags::FAuto(),  args...)
+		     : *fmt == 's' ? val_fmt_done(fmt, item, PrintfFlags::FString(),args...)
+		     : *fmt == 'B' ? val_fmt_done(fmt, item, PrintfFlags::FBool(),  args...)
+		     : *fmt == 'b' ? val_fmt_done(fmt, item, PrintfFlags::FBinary(),args...)
+		     : *fmt == 'o' ? val_fmt_done(fmt, item, PrintfFlags::FOct(),   args...)
+		     : *fmt == 'i' ? val_fmt_done(fmt, item, PrintfFlags::FSDec(),  args...)
+		     : *fmt == 'u' ? val_fmt_done(fmt, item, PrintfFlags::FUDec(),  args...)
+		     : *fmt == 'x' ? val_fmt_done(fmt, item, PrintfFlags::FHexL(),  args...)
+		     : *fmt == 'X' ? val_fmt_done(fmt, item, PrintfFlags::FHexU(),  args...)
+		     : throw cprintf_badformat("Unknown character in format string");
+	}
+	// --- Size modifier (not implemented, not needed?)
+	template <typename ...Args>
+	constexpr bool val_fmt_size(const char * fmt, PrintfFlags item, Args... args)
+	{
+		// TODO: Size characters?
+		return val_fmt_fmt(fmt, item, args...);
+	}
+	// --- Precision
+	template <typename ...Args>
+	constexpr bool val_fmt_prec_val(const char * fmt, unsigned int value, PrintfFlags item, Args... args)
+	{
+		return _bits::isdigit_s(*fmt) ? val_fmt_prec_val(fmt+1, value*10+_bits::todigit(*fmt), item, args...)
+		     : val_fmt_size(fmt, PrintfFlags(item, PrintfFlags::ArgPrec(), value), args...);
+	}
+	template <typename ...Args>
+	constexpr bool val_fmt_prec_arg(const char *fmt, PrintfFlags item, unsigned int size, Args... args)
+	{
+		return val_fmt_prec_opt(fmt, PrintfFlags(item, PrintfFlags::ArgPrec(), size), args...);
+	}
+	template <typename Arg, typename ...Args>
+	constexpr bool val_fmt_prec_arg(const char *fmt, PrintfFlags item, Arg size, Args... args)
+	{
+		//static_assert(false, "Invalid type for precision modifier, must be 'unsigned int'");
+		return false ? false : throw "Invalid type for precision modifier, must be 'unsigned int'";
+	}
+	constexpr bool val_fmt_prec_arg(const char *fmt, PrintfFlags item)
+	{
+		//static_assert(false, "Too few arguments when getting precision modifier");
+		return false ? false : throw "Too few arguments when getting precision modifier";
+	}
+	template <typename ...Args>
+	constexpr bool val_fmt_prec(const char * fmt, PrintfFlags item, Args... args)
+	{
+		return _bits::isdigit_s(*fmt) ? val_fmt_prec_val(fmt+1, _bits::todigit(*fmt), item, args...)
+		     : *fmt == '*' ? val_fmt_prec_arg(fmt+1, item, args...)
+		     : val_fmt_size(fmt, item, args...);
+	}
+	template <typename ...Args>
+	constexpr bool val_fmt_prec_opt(const char * fmt, PrintfFlags item, Args... args)
+	{
+		return *fmt == '.' ? val_fmt_prec(fmt+1, item, args...)
+		     : val_fmt_size(fmt, item, args...);
+	}
+	// --- Field Width ---	
+	template <typename ...Args>
+	constexpr bool val_fmt_width_val(const char* fmt, unsigned int size, PrintfFlags item, Args... args)
+	{
+		return  _bits::isdigit_s(*fmt) ? val_fmt_width_val(fmt+1, size*10+_bits::todigit(*fmt), item, args...)
+		      : val_fmt_prec_opt(fmt, PrintfFlags(item, PrintfFlags::ArgWidth(), size), args...);
+	}
+	template <typename ...Args>
+	constexpr bool val_fmt_width_arg(const char *fmt, PrintfFlags item, unsigned int size, Args... args)
+	{
+		return val_fmt_prec_opt(fmt, PrintfFlags(item, PrintfFlags::ArgWidth(), size), args...);
+	}
+	template <typename Arg, typename ...Args>
+	constexpr bool val_fmt_width_arg(const char *fmt, PrintfFlags item, Arg size, Args... args)
+	{
+		//static_assert(false, "Invalid type for width modifier, must be 'unsigned int'");
+		return false ? false : throw "Invalid type for width modifier, must be 'unsigned int'";
+	}
+	constexpr bool val_fmt_width_arg(const char *fmt, PrintfFlags item)
+	{
+		//static_assert(false, "Too few arguments when reading width for width modifier");
+		return false ? false : throw "Too few arguments when reading width for width modifier";
+	}
+	template <typename ...Args>
+	constexpr bool val_fmt_width(const char * fmt, PrintfFlags item, Args... args)
+	{
+		return _bits::isdigit_s(*fmt) ? val_fmt_width_val(fmt+1, _bits::todigit(*fmt), item, args...)
+		     : *fmt == '*' ? val_fmt_width_arg(fmt+1, item, args...)
+		     : val_fmt_prec_opt(fmt, item, args...);
+	}
+	// --- Flags
+	template <typename ...Args>
+	constexpr bool val_fmt_flags(const char * fmt, PrintfFlags item, Args... args)
+	{
+		return
+		  *fmt == '-' ? val_fmt_flags(fmt+1, PrintfFlags(item, PrintfFlags::Left()) , args...)
+		: *fmt == '+' ? val_fmt_flags(fmt+1, PrintfFlags(item, PrintfFlags::Sign()) , args...)
+		: *fmt == ' ' ? val_fmt_flags(fmt+1, PrintfFlags(item, PrintfFlags::Space()), args...)
+		: *fmt == '#' ? val_fmt_flags(fmt+1, PrintfFlags(item, PrintfFlags::Alt())  , args...)
+		: *fmt == '0' ? val_fmt_flags(fmt+1, PrintfFlags(item, PrintfFlags::Zero()) , args...)
+		: val_fmt_width(fmt, item, args...);
+	}
+	// --- Literal '%'
+	template <typename ...Args>
+	constexpr bool val_fmt_start(const char* fmt, Args... args)
+	{
+		return *fmt == '%' ? val(fmt+1, args...) : val_fmt_flags(fmt, PrintfFlags(), args...);
+	}
+	template <typename... Args>
+	constexpr bool val(const char* fmt, Args... args)
+	{
+		return *fmt == '\0' ? true
+		     : *fmt == '%' ? val_fmt_start(fmt+1, args...)
+		     : val(fmt+1, args...);
+	}
+	
+	size_t run(cprintf_cb puts, const char *fmt);
+	template <typename... Args> size_t run(cprintf_cb puts, const char * fmt, Args... args);
+
+	// --- Print formatted value
+	template <typename Fmt>
+	constexpr size_t run_fmt_done(cprintf_cb puts, const char * fmt, PrintfFlags item, Fmt fmtcode)
+	{
+		throw cprintf_toofewargs();
+	}
+	template <typename Fmt, typename Arg, typename... Args>
+	size_t run_fmt_done(cprintf_cb puts, const char * fmt, PrintfFlags item, Fmt fmtcode, Arg val,  Args... args)
+	{
+		if( !puts )
+			return run(puts, fmt+1, args...);
+		else
+			return ::cxxextras::cprintf_val(puts, PrintfFlags(item, fmtcode), val) + run(puts, fmt+1, args...);
+	}
+	// --- Format code
+	template <typename ...Args>
+	size_t run_fmt_fmt(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
+	{
+		return *fmt == '\0' ? throw "ERROR: NUL byte in format specifier"
+		     : *fmt == '?' ? run_fmt_done(puts, fmt, item, PrintfFlags::FAuto(),  args...)
+		     : *fmt == 's' ? run_fmt_done(puts, fmt, item, PrintfFlags::FString(),args...)
+		     : *fmt == 'B' ? run_fmt_done(puts, fmt, item, PrintfFlags::FBool(),  args...)
+		     : *fmt == 'b' ? run_fmt_done(puts, fmt, item, PrintfFlags::FBinary(),args...)
+		     : *fmt == 'o' ? run_fmt_done(puts, fmt, item, PrintfFlags::FOct(),   args...)
+		     : *fmt == 'i' ? run_fmt_done(puts, fmt, item, PrintfFlags::FSDec(),  args...)
+		     : *fmt == 'u' ? run_fmt_done(puts, fmt, item, PrintfFlags::FUDec(),  args...)
+		     : *fmt == 'x' ? run_fmt_done(puts, fmt, item, PrintfFlags::FHexL(),  args...)
+		     : *fmt == 'X' ? run_fmt_done(puts, fmt, item, PrintfFlags::FHexU(),  args...)
+		     : throw cprintf_badformat("Unknown character in format string");
+	}
+	// --- Size modifier (not implemented, not needed?)
+	template <typename ...Args>
+	size_t run_fmt_size(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
+	{
+		// TODO: Size characters?
+		return run_fmt_fmt(puts, fmt, item, args...);
+	}
+	// --- Precision
+	template <typename ...Args>
+	size_t run_fmt_prec_val(cprintf_cb puts, const char * fmt, unsigned int val, PrintfFlags item, Args... args)
+	{
+		return _bits::isdigit_s(*fmt) ? run_fmt_prec_val(puts, fmt+1, val*10+_bits::todigit(*fmt), item, args...)
+		     : run_fmt_size(puts, fmt, PrintfFlags(item, PrintfFlags::ArgPrec(), val), args...);
+	}
+	template <typename ...Args>
+	size_t run_fmt_prec_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, unsigned int size, Args... args)
+	{
+		return run_fmt_prec_opt(puts, fmt, PrintfFlags(item, PrintfFlags::ArgPrec(), size), args...);
+	}
+	template <typename Arg, typename ...Args>
+	size_t run_fmt_prec_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, Arg size, Args... args)
+	{
+		throw cprintf_badformat("Invalid type for printf precision modifier");
+	}
+	size_t run_fmt_prec_arg(cprintf_cb puts, const char *fmt, PrintfFlags item)
+	{
+		throw cprintf_toofewargs();
+	}
+	template <typename ...Args>
+	size_t run_fmt_prec(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
+	{
+		return _bits::isdigit_s(*fmt) ? run_fmt_prec_val(puts, fmt+1, _bits::todigit(*fmt), item, args...)
+		     : *fmt == '*' ? run_fmt_prec_arg(puts, fmt+1, item, args...)
+		     : run_fmt_size(puts, fmt, item, args...);
+	}
+	template <typename ...Args>
+	size_t run_fmt_prec_opt(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
+	{
+		return *fmt == '.' ? run_fmt_prec(puts, fmt+1, item, args...)
+		     : run_fmt_size(puts, fmt, item, args...);
+	}
+	// --- Field Width ---	
+	template <typename ...Args>
+	size_t run_fmt_width_val(cprintf_cb puts, const char* fmt, unsigned int val, PrintfFlags item, Args... args)
+	{
+		return  _bits::isdigit_s(*fmt) ? run_fmt_width_val(puts, fmt+1, val*10+_bits::todigit(*fmt), item, args...)
+		      : run_fmt_prec_opt(puts, fmt, PrintfFlags(item, PrintfFlags::ArgWidth(), val), args...);
+	}
+	template <typename ...Args>
+	size_t run_fmt_width_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, unsigned int size, Args... args)
+	{
+		return run_fmt_prec_opt(puts, fmt, PrintfFlags(item, PrintfFlags::ArgWidth(), size), args...);
+	}
+	template <typename Arg, typename ...Args>
+	size_t run_fmt_width_arg(cprintf_cb puts, const char *fmt, PrintfFlags item, Arg size, Args... args)
+	{
+		throw cprintf_badformat("Invalid type for printf width modifier");
+	}
+	size_t run_fmt_width_arg(cprintf_cb puts, const char *fmt, PrintfFlags item)
+	{
+		throw cprintf_toofewargs();
+	}
+	template <typename ...Args>
+	size_t run_fmt_width(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
+	{
+		return _bits::isdigit_s(*fmt) ? run_fmt_width_val(puts, fmt+1, _bits::todigit(*fmt), item, args...)
+		     : *fmt == '*' ? run_fmt_width_arg(puts, fmt+1, item, args...)
+		     : run_fmt_prec_opt(puts, fmt, item, args...);
+	}
+	// --- Flags
+	template <typename ...Args>
+	size_t run_fmt_flags(cprintf_cb puts, const char * fmt, PrintfFlags item, Args... args)
+	{
+		return
+		  *fmt == '-' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Left()) , args...)
+		: *fmt == '+' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Sign()) , args...)
+		: *fmt == ' ' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Space()), args...)
+		: *fmt == '#' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Alt())  , args...)
+		: *fmt == '0' ? run_fmt_flags(puts, fmt+1, PrintfFlags(item, PrintfFlags::Zero()) , args...)
+		: run_fmt_width(puts, fmt, item, args...);
+	}
+	// --- Literal '%'
+	template <typename ...Args>
+	size_t run_fmt_start(cprintf_cb puts, const char * fmt, Args... args)
+	{
+		return *fmt == '%'
+			? (puts("%", 1), 1 + run(puts, fmt+1, args...))
+			: run_fmt_flags(puts, fmt, PrintfFlags(), args...);
+	}
+	// --- Root
+	template <typename... Args>
+	size_t run(cprintf_cb puts, const char * fmt, Args... args)
+	{
+		int ofs;
+		for( ofs = 0; fmt[ofs] != '\0' && fmt[ofs] != '%'; ofs ++ )
+			;
+		if( fmt[ofs] != '%' )
+			throw cprintf_toomanyargs();
+		if(ofs > 0 && puts) puts(fmt, ofs);
+		return ofs + run_fmt_start(puts, fmt+ofs+1, args...);
+	}
+	size_t run(cprintf_cb puts, const char *fmt)
+	{
+		int ofs;
+		for( ofs = 0; fmt[ofs] != '\0' && fmt[ofs] != '%'; ofs ++ )
+			;
+		if(ofs > 0 && puts) puts(fmt, ofs);
+		return ofs + (fmt[ofs] == '%' ? run_fmt_start(puts, fmt+ofs+1) : 0);
+	}
+
+}
+
+};	// namespace _bits
+
+#define cprintf(puts, fmt, ...)	static_assert(::cxxextras::_bits::_printf::val(fmt, __VA_ARGS__),"");::cxxextras::_bits::_printf::run(puts, fmt, __VA_ARGS__)
+
+//template <typename ... Args>
+//size_t cprintf(cprintf_cb puts, const char *fmt, Args... args)
+//{
+//	_bits::_printf::run(nullptr, fmt, args...);
+//	return _bits::_printf::run(puts, fmt, args...);
+//}
+
+};
+
+#endif
+
+// vim: ft=cpp
+
diff --git a/Usermode/Libraries/libc.so_src/Makefile b/Usermode/Libraries/libc.so_src/Makefile
index c615b6e96ed3907caeda14f0c174ec7764bcbfb0..64749b3b8c03eb5af66bb364fc605fc290f047e4 100644
--- a/Usermode/Libraries/libc.so_src/Makefile
+++ b/Usermode/Libraries/libc.so_src/Makefile
@@ -4,9 +4,11 @@
 -include ../Makefile.cfg
 
 CPPFLAGS += 
-CFLAGS   += -Wall -Werror -Wextra
+CFLAGS   += -nostdlib -Wall -Werror -Wextra
 ASFLAGS  +=
-LDFLAGS  += -Map map.txt
+LDFLAGS  += -nostdlib
+PRELINK  := $(CRTI) $(CRTBEGINS) $(CRT0S)
+LIBS     += $(LIBGCC_PATH) $(CRTENDS) $(CRTN)
 
 INCFILES := stdio.h stdlib.h
 
@@ -19,7 +21,7 @@ OBJ += arch/$(ARCHDIR).ao
 DEPFILES := $(OBJ:%.o=%.d)
 BIN = libc.so
 ifeq ($(ARCHDIR),native)
- OBJ := $(filter-out heap.o,$(OBJ))
+ OBJ := $(filter-out heap.o,$(OBJ)) heap_native.o
  #LDFLAGS += -l c
  BIN = libc_acess.so
 endif
@@ -27,8 +29,8 @@ endif
 include ../Makefile.tpl
 
 EXP_%.txt: TEST_%.native
-	./$< > $@
-	rm $<
+	@./$< > $@
+	@rm $<
 EXP_strtoi.txt:
 	echo -n "" > $@
 
diff --git a/Usermode/Libraries/libc.so_src/TEST_string.c b/Usermode/Libraries/libc.so_src/TEST_string.c
new file mode 100644
index 0000000000000000000000000000000000000000..4ecca307d684819ece57ff7b12de56fe12834f64
--- /dev/null
+++ b/Usermode/Libraries/libc.so_src/TEST_string.c
@@ -0,0 +1,51 @@
+/*
+ */
+#include <stdio.h>
+#include <string.h>
+
+#define ASSERT(cnd)	printf("ASSERT: "#cnd" == %s\n", ((cnd) ? "pass" : "FAIL"))
+
+int main()
+{
+	ASSERT(strcmp("hello", "world") < 0);
+	ASSERT(strcmp("hello", "hello") == 0);
+	ASSERT(strcmp("wello", "hello") > 0);
+	ASSERT(strcmp("\xff", "\1") > 0);
+	ASSERT(strcmp("\1", "\xff") < 0);
+	ASSERT(strcmp("Hello", "hello") < 0);
+	
+	ASSERT(strncmp("hello world", "hello", 5) == 0);
+	
+	ASSERT(strcasecmp("hello", "world") < 0);
+	ASSERT(strcasecmp("hello", "hello") == 0);
+	ASSERT(strcasecmp("wello", "hello") > 0);
+	ASSERT(strcasecmp("\xff", "\1") > 0);
+	ASSERT(strcasecmp("\1", "\xff") < 0);
+	ASSERT(strcasecmp("Hello", "hello") == 0);
+	ASSERT(strcasecmp("Hello", "Hello") == 0);
+	ASSERT(strcasecmp("hellO", "Hello") == 0);
+	
+	char buf[13];
+	memset(buf, 127, sizeof(buf));
+	ASSERT(buf[0] == 127);	ASSERT(buf[4] == 127);
+	strncpy(buf, "hello", 4);
+	ASSERT(buf[3] == 'l');	ASSERT(buf[4] == 127);
+	strncpy(buf, "hello", 8);
+	ASSERT(buf[4] == 'o');	ASSERT(buf[5] == '\0'); ASSERT(buf[7] == '\0'); ASSERT(buf[8] == 127);
+	
+	memset(buf, 0, 13);
+	ASSERT(buf[0] == 0);	ASSERT(buf[12] == 0);
+	
+	ASSERT(memchr("\xffhello", 'x', 6) == NULL);
+	
+	const char *teststr_foo = "foo";
+	ASSERT(strchr(teststr_foo, 'f') == teststr_foo+0);
+	ASSERT(strchr(teststr_foo, 'o') == teststr_foo+1);
+	ASSERT(strchr(teststr_foo, '\0') == teststr_foo+3);
+	ASSERT(strchr(teststr_foo, 'X') == NULL);
+	ASSERT(strrchr(teststr_foo, 'f') == teststr_foo+0);
+	ASSERT(strrchr(teststr_foo, 'o') == teststr_foo+2);
+	ASSERT(strrchr(teststr_foo, '\0') == teststr_foo+3);
+	ASSERT(strrchr(teststr_foo, 'X') == NULL);
+}
+
diff --git a/Usermode/Libraries/libc.so_src/TEST_strtoi.c b/Usermode/Libraries/libc.so_src/TEST_strtoi.c
index 6d89224eaa83b92c9231a8b57d4d8a69657ea106..e06580872615fe727e3548c7cd1971a455791bb4 100644
--- a/Usermode/Libraries/libc.so_src/TEST_strtoi.c
+++ b/Usermode/Libraries/libc.so_src/TEST_strtoi.c
@@ -7,15 +7,71 @@
  */
 #include <stdio.h>
 #include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>	// strerror
 
-#define TST(t, class, base, val, exp, fmt) do {\
-	t ret = strto##class(#val, NULL, base); \
+#define STR_(v)	#v
+#define STR(v)	STR_(v)
+#define TST(t, class, base, val, exp, fmt, ofs, exp_errno) do {\
+	const char *in = val;\
+	char *end;\
+	errno = 0;\
+	t ret = strto##class(in, &end, base); \
 	if( ret != exp ) \
-		printf("FAIL strto"#class"('"#val"') != "#val" (act 0x"fmt")\n", ret);\
+		fprintf(stderr, "FAIL strto"#class"('%s') != "#exp" (act "fmt")\n", in, ret);\
+	if( end != in+ofs ) \
+		fprintf(stderr, "FAIL strto"#class"('%s') returned wrong end: %p (+%zi) instead of %p (+%zi)\n",\
+			in,end,end-in,in+ofs,(size_t)ofs);\
+	if( exp_errno != errno ) \
+		fprintf(stderr, "FAIL strto"#class"('%s') returned wrong errno, exp '%s', got '%s'\n",\
+			in, strerror(exp_errno), strerror(errno));\
 }while(0)
 
+#define PRIMEBUF(fmt, val)	buf_len = snprintf(buf, sizeof(buf), fmt, val)
+
 int main(int argc, char *argv[])
 {
-	TST(unsigned long, ul, 0, 0x10ec, 0x10ec, "%lx");
-	TST(unsigned long long, ull, 0, 0xffeed10ec, 0xffeed10ec, "%llx");
+	char buf[64];
+	size_t	buf_len;
+	
+	// Success cases
+	TST(unsigned long, ul, 0, "0x10ec", 0x10ec, "%lx", 2+4, 0);
+	TST(unsigned long long, ull, 0, "0xffeed10ec", 0xffeed10ec, "%llx", 2+9, 0);
+	TST(unsigned long long, ull, 0, "01234567", 01234567, "%llo", 8, 0);
+	TST(unsigned long long, ull, 0, "1234567", 1234567, "%lld", 7, 0);
+	TST(long long, ll, 0, "-1", -1, "%lld", 2, 0);	// -1
+	TST(long long, ll, 0, "100113", 100113, "%lld", strlen(in), 0);
+	TST(long long, ll, 0, "0x101", 0x101, "0x%llx", strlen(in), 0);
+	
+	// Invalid strings
+	TST(unsigned long long, ull, 0, "0x",  0, "%llx", 1, 0);	// Single 0
+	TST(unsigned long long, ull, 0, "0xg", 0, "%llx", 1, 0);	// Single 0
+	TST(unsigned long long, ull, 0, "-a", 0, "%llx", 0, 0);	// Nothing
+	TST(long long, ll, 0, "-a", 0, "%lld", 0, 0);	// Nothing
+	TST(long long, ll, 0, "-1aaatg", -1, "%lld", 2, 0);	// -1 (with traling junk)
+	TST(long long, ll, 0, "-+1aaatg", 0, "%lld", 0, 0);	// Nothing
+	TST(long long, ll, 0, "-  1", 0, "%lld", 0, 0);	// Nothing
+	TST(long long, ll, 0, "01278  1", 0127, "%lld", 4, 0);	// 0127 with junk
+	
+	// Range edges
+	PRIMEBUF("0x%llx", ULLONG_MAX);
+	TST(unsigned long long, ull, 0, buf, ULLONG_MAX, "0x%llx", buf_len, 0);
+	PRIMEBUF("%llu", ULLONG_MAX);
+	TST(unsigned long long, ull, 0, buf, ULLONG_MAX, "%llu", buf_len, 0);
+	PRIMEBUF("-%llu", (long long)LONG_MAX);
+	TST(long, l, 0, buf, -LONG_MAX, "%ld", buf_len, 0);
+	
+	// Out of range
+	// - When the range limit is hit, valid characters should still be consumed (just not used)
+	TST(unsigned long long, ull, 0, "0x10000FFFF0000FFFF", ULLONG_MAX, "%llx", strlen(in), ERANGE);
+	TST(unsigned long, ul, 0, "0x10000FFFF0000FFFF", ULONG_MAX, "%lx", strlen(in), ERANGE);
+	TST(long, l, 0, "0x10000FFFF0000FFFF", LONG_MAX, "%ld", strlen(in), ERANGE);
+	TST(long, l, 0, "-0x10000FFFF0000FFFF", LONG_MIN, "%ld", strlen(in), ERANGE);
+	if( LONG_MIN < -LONG_MAX )
+	{
+		// Ensure that if -LONG_MIN is greater than LONG_MAX, that converting it leaves a range error
+		PRIMEBUF("%ld", LONG_MIN);
+		TST(long, l, 0, buf+1, LONG_MAX, "%ld", buf_len-1, ERANGE);
+	}
 }
diff --git a/Usermode/Libraries/libc.so_src/ctype.c b/Usermode/Libraries/libc.so_src/ctype.c
index d5a5e97ae601d8f0009813e9357cd4f30a2a407b..18a199c647621270b9e8eabc886479f4ee5d43df 100644
--- a/Usermode/Libraries/libc.so_src/ctype.c
+++ b/Usermode/Libraries/libc.so_src/ctype.c
@@ -22,23 +22,40 @@ int isalnum(int ch) {
 	return isalpha(ch) || isdigit(ch);
 }
 
-int toupper(int ch) {
-	if('a'<=ch && ch <='z')
-		return ch - 'a' + 'A';
-	return ch;
+int isxdigit(int ch) {
+	if('0'<=ch&&ch<='9')	return 1;
+	if('a'<=ch&&ch<='f')	return 1;
+	if('F'<=ch&&ch<='F')	return 1;
+	return 0;
 }
-int tolower(int ch) {
-	if('A'<=ch && ch <='Z')
-		return ch - 'A' + 'a';
-	return ch;
+
+int isupper(int ch) {
+	if('A'<=ch && ch <='Z')	return 1;
+	return 0;
+}
+
+int islower(int ch) {
+	if('a'<=ch && ch <='z')	return 1;
+	return 0;
+}
+
+int ispunct(int ch) {
+	if( isprint(ch) && !isspace(ch) && !isalnum(ch) )
+		return 1;
+	return 0;
 }
 
 int isprint(int ch ) {
-	if( ch < ' ' )	return 0;
-	if( ch > 'z' )	return 0;
+	if( ' ' <= ch && ch <= 'z' )	return 1;
 	return 1;
 }
 
+int isgraph(int ch) {
+	// Anything but space
+	if( ' ' < ch && ch <= 'z' )	return 1;
+	return 0;
+}
+
 int isspace(int ch) {
 	if(ch == ' ')	return 1;
 	if(ch == '\t')	return 1;
@@ -47,12 +64,17 @@ int isspace(int ch) {
 	return 0;
 }
 
-int isxdigit(int ch) {
-	if('0'<=ch&&ch<='9')	return 1;
-	if('a'<=ch&&ch<='f')	return 1;
-	if('F'<=ch&&ch<='F')	return 1;
-	return 0;
+int toupper(int ch) {
+	if('a'<=ch && ch <='z')
+		return ch - 'a' + 'A';
+	return ch;
 }
+int tolower(int ch) {
+	if('A'<=ch && ch <='Z')
+		return ch - 'A' + 'a';
+	return ch;
+}
+
 
 // C99
 int isblank(int ch) {
diff --git a/Usermode/Libraries/libc.so_src/errno.c b/Usermode/Libraries/libc.so_src/errno.c
index 6f9545749e8f246715e0e0e8a48da970c0b3e143..295183c4310c578594bc3809067432e7618d3f7f 100644
--- a/Usermode/Libraries/libc.so_src/errno.c
+++ b/Usermode/Libraries/libc.so_src/errno.c
@@ -21,13 +21,16 @@ EXPORT char *strerror(int errnum)
 	switch((enum libc_eErrorNumbers)errnum)
 	{
 	case EOK:	return "Success";
+	case ERANGE:	return "Value out of range";
+	case EDOM:	return "Value out of domain";
+	case EILSEQ:	return "Illegal character sequence";
+
 	case ENOSYS:	return "Invalid instruction/syscall";
 	case EINVAL:	return "Bad argument(s)";
 	case EBADF:	return "Invalid file";
 	case ENOMEM:	return "No free memory";
 	case EACCES:	return "Not permitted";
 	case EBUSY:	return "Resource is busy";
-	case ERANGE:	return "Value out of range";
 	case ENOTFOUND:	return "Item not found";
 	case EROFS:	return "Read only filesystem";
 	case ENOTIMPL:	return "Not implimented";
@@ -46,8 +49,15 @@ EXPORT char *strerror(int errnum)
 	case ENOTTY:	return "Not a TTY";
 	case EAGAIN:	return "Try again";
 	case EFBIG:	return "File too big";
+	case E2BIG:	return "Value too big";
 	case EALREADY:	return "Operation was no-op";
+	case ENOSPC:	return "No space left on the device";
+
 	case EAFNOSUPPORT:	return "Address family not supported";
+	case EADDRINUSE:	return "Address already in use";
+	case ETIMEDOUT:	return "Operation timed out";
+	case EOPNOTSUPP:	return "Operation not supported on socket";
+
 	case EINTERNAL:	return "Internal error";
 	}
 	_SysDebug("strerror: errnum=%i unk", errnum);
diff --git a/Usermode/Libraries/libc.so_src/heap.c b/Usermode/Libraries/libc.so_src/heap.c
index d6d9a40361e134db10ed997762f44bc2d123563b..71e3d6dc0c7cb754ed015aa66fc24d0bc0d7a568 100644
--- a/Usermode/Libraries/libc.so_src/heap.c
+++ b/Usermode/Libraries/libc.so_src/heap.c
@@ -8,6 +8,8 @@
 #include <acess/sys.h>
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
+#include <stdbool.h>
 #include "lib.h"
 
 #if 0
@@ -71,6 +73,7 @@ static const heap_head	_heap_zero_allocation;
 EXPORT void	*malloc(size_t bytes);
 void	*_malloc(size_t bytes, void *owner);
 EXPORT void	*calloc(size_t bytes, size_t count);
+bool	_libc_free(void *mem);
 EXPORT void	free(void *mem);
 EXPORT void	*realloc(void *mem, size_t bytes);
 EXPORT void	*sbrk(int increment);
@@ -221,26 +224,31 @@ EXPORT void *calloc(size_t __nmemb, size_t __size)
  \param mem	Pointer - Memory to free
 */
 EXPORT void free(void *mem)
+{
+	if( !_libc_free(mem) ) {
+		Heap_Validate(1);
+		exit(0);
+	}
+}
+
+// Exported for libc++
+EXPORT bool _libc_free(void *mem)
 {
 	heap_head	*head = (heap_head*)mem - 1;
 
 	// Free of NULL or the zero allocation does nothing
 	if(!mem || mem == _heap_zero_allocation.data)
-		return ;
+		return true;
 	
 	// Sanity check the head address
 	if(head->magic != MAGIC) {
 		if( head->magic != MAGIC_FREE ) {
-			_SysDebug("Double free of %p", mem);
-			Heap_Validate(1);
-			exit(0);
+			_SysDebug("Double free of %p by %p", mem, __builtin_return_address(0));
 		}
 		else {
-			_SysDebug("Free of invalid pointer %p", mem);
-			Heap_Validate(1);
-			exit(0);
+			_SysDebug("Free of invalid pointer %p by ", mem, __builtin_return_address(0));
 		}
-		return;
+		return false;
 	}
 	
 	head->magic = MAGIC_FREE;
@@ -265,8 +273,9 @@ EXPORT void free(void *mem)
 		heap_foot *prevFoot = PREV_FOOT(head);
 		if( prevFoot->magic != MAGIC )
 		{
+			_SysDebug("Heap corruption, previous foot magic invalid");
 			Heap_Validate(1);
-			exit(1);
+			return false;
 		}
 		
 		heap_head *prevHead = prevFoot->header;
@@ -281,6 +290,8 @@ EXPORT void free(void *mem)
 			prevFoot->header = NULL;
 		}
 	}
+	
+	return true;
 }
 
 /**
@@ -308,7 +319,8 @@ EXPORT void *realloc(void *oldPos, size_t bytes)
 	
 	// Check for free space after the block
 	heap_head *nexthead = NEXT_HEAD(head);
-	if( nexthead && nexthead->magic == MAGIC_FREE && head->size + nexthead->size >= reqd_size )
+	assert( nexthead <= _heap_end );
+	if( nexthead != _heap_end && nexthead->magic == MAGIC_FREE && head->size + nexthead->size >= reqd_size )
 	{
 		// Split next block
 		if( head->size + nexthead->size > reqd_size )
@@ -337,12 +349,12 @@ EXPORT void *realloc(void *oldPos, size_t bytes)
 	void *ret = _malloc(bytes, __builtin_return_address(0));
 	if(ret == NULL)
 		return NULL;
+	heap_head *newhead = (heap_head*)ret - 1;
 	
-	//Copy Old Data
+	// Copy Old Data
+	assert( head->size < newhead->size );
 	size_t copy_size = head->size-sizeof(heap_head)-sizeof(heap_foot);
-	if( copy_size > bytes )
-		copy_size = bytes;
-	memcpy(ret, oldPos, bytes);
+	memcpy(ret, oldPos, copy_size);
 	free(oldPos);
 	
 	//Return
diff --git a/Usermode/Libraries/libc.so_src/heap_native.c b/Usermode/Libraries/libc.so_src/heap_native.c
new file mode 100644
index 0000000000000000000000000000000000000000..0f4d9b3b66d2a2750733103b7f8d8cf0b11c41e6
--- /dev/null
+++ b/Usermode/Libraries/libc.so_src/heap_native.c
@@ -0,0 +1,8 @@
+#include <stdlib.h>
+#include <stdbool.h>
+
+bool _libc_free(void *addr)
+{
+	free(addr);
+	return true;
+}
diff --git a/Usermode/Libraries/libc.so_src/include_exp/ctype.h b/Usermode/Libraries/libc.so_src/include_exp/ctype.h
index 75e4ce88aa17139643da0ecb99ed6d7eb1123590..69067e87fa99e6ab6efea1daab987c5c05d04df2 100644
--- a/Usermode/Libraries/libc.so_src/include_exp/ctype.h
+++ b/Usermode/Libraries/libc.so_src/include_exp/ctype.h
@@ -14,21 +14,27 @@ extern "C" {
 
 extern int isalpha(int ch);
 extern int isdigit(int ch);
-
 extern int isalnum(int ch);
+extern int isxdigit(int ch);
 
-extern int toupper(int ch);
-extern int tolower(int ch);
+extern int islower(int ch);
+extern int isupper(int ch);
+extern int ispunct(int ch);
 
 extern int isprint(int ch);
+extern int isgraph(int ch);
 
 extern int isspace(int ch);
 
-extern int isxdigit(int ch);
+extern int iscntrl(int ch);
 
 // C99
 extern int isblank(int ch);
 
+// Conversions
+extern int toupper(int ch);
+extern int tolower(int ch);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/Usermode/Libraries/libc.so_src/include_exp/errno.enum.h b/Usermode/Libraries/libc.so_src/include_exp/errno.enum.h
index f943654712f8e80a03b81c4663f90e3120727537..d7b105365e14bccf3c0614f22ad7206872c3c1ae 100755
--- a/Usermode/Libraries/libc.so_src/include_exp/errno.enum.h
+++ b/Usermode/Libraries/libc.so_src/include_exp/errno.enum.h
@@ -1,13 +1,16 @@
 
 enum libc_eErrorNumbers {
 	EOK,
+	EDOM,	// (C99) Value out of domain
+	EILSEQ,	// (C99) Illegal multi-byte sequence
+	ERANGE,	// (C99) Value out of range
+	
 	ENOSYS,	// Invalid Instruction
 	EINVAL,	// Invalid Paramater
 	EBADF,	// Bad FD
 	ENOMEM,	// No free memory
 	EACCES,	// Not permitted
 	EBUSY,	// Resource is busy
-	ERANGE,	// Value out of range
 	ENOTFOUND,	// Item not found
 	EROFS,	// Read only
 	ENOTIMPL,	// Not implemented
@@ -27,11 +30,16 @@ enum libc_eErrorNumbers {
 
 	EAGAIN,	// Try again
 	EALREADY,	// Operation was a NOP
+	ENOSPC,	// (POSIX) No space left on device
 	
 	EFBIG,	// File too large
+	E2BIG,	// Argument list too large
 
 	// psockets
 	EAFNOSUPPORT,	
+	EADDRINUSE,	// (POSIX.1) Specified addres is already in use
+	ETIMEDOUT,
+	EOPNOTSUPP,	// (POSIX.1) Operation not supported on socket
 	
 	EINTERNAL	// Internal Error
 };
diff --git a/Usermode/Libraries/libc.so_src/include_exp/inttypes.h b/Usermode/Libraries/libc.so_src/include_exp/inttypes.h
index 0200aa6aa58691b2e009544204ef44eda564b4d4..05e6707016160ce05ac88f0689a77cb04b66ce82 100644
--- a/Usermode/Libraries/libc.so_src/include_exp/inttypes.h
+++ b/Usermode/Libraries/libc.so_src/include_exp/inttypes.h
@@ -11,18 +11,25 @@
 #define _INTTYPES_H_
 
 #include <stdint.h>
+#include <limits.h>
 
-#define PRId64	"lld"
-#define PRIdLEAST64	"lld"
-#define PRIdFAST64	"lld"
+#if INT64_MAX == LONG_MAX
+# define _PRI64	"l"
+#else
+# define _PRI64	"ll"
+#endif
+
+#define PRId64	_PRI64"d"
+#define PRIdLEAST64	_PRI64"d"
+#define PRIdFAST64	_PRI64"d"
 #define PRIdMAX
 #define PRIdPTR
-#define PRIi64	"lli"
+#define PRIi64	_PRI64"i"
 #define PRIiLEAST64
 #define PRIiFAST64
 #define PRIiMAX
 #define PRIiPTR
 
-#define PRIx64	"llx"
+#define PRIx64	_PRI64"i"
 
 #endif
diff --git a/Usermode/Libraries/libc.so_src/include_exp/stdio.h b/Usermode/Libraries/libc.so_src/include_exp/stdio.h
index ed1fce584d1ce5bebcafc60f6af45dd3d661e526..b2cb8574368c11328f76a7bba7cdb4ebcf904941 100644
--- a/Usermode/Libraries/libc.so_src/include_exp/stdio.h
+++ b/Usermode/Libraries/libc.so_src/include_exp/stdio.h
@@ -94,7 +94,7 @@ extern FILE	*open_memstream(char **bufferptr, size_t *lengthptr);
 extern FILE	*fdopen(int fd, const char *modes);
 extern FILE	*tmpfile(void);
 extern int	fclose(FILE *fp);
-extern void	fflush(FILE *fp);
+extern int	fflush(FILE *fp);
 extern off_t	ftell(FILE *fp);
 extern off_t	ftello(FILE *fp);
 extern int	fseek(FILE *fp, long int amt, int whence);
diff --git a/Usermode/Libraries/libc.so_src/include_exp/stdlib.h b/Usermode/Libraries/libc.so_src/include_exp/stdlib.h
index 07fc81f4c8d7756e978ac8c3574da9fe9e9b29ee..fa923eef6e7244cc3fe896d50852165e76605e0c 100644
--- a/Usermode/Libraries/libc.so_src/include_exp/stdlib.h
+++ b/Usermode/Libraries/libc.so_src/include_exp/stdlib.h
@@ -112,6 +112,8 @@ extern int	rand_p(unsigned int *seedp);
 # define SEEK_END	(-1)
 #endif
 
+#define MB_CUR_MAX	5	// (C99) Max number of bytes in a single multibyte character (UTF8=5)
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/Usermode/Libraries/libc.so_src/include_exp/string.h b/Usermode/Libraries/libc.so_src/include_exp/string.h
index 7ef1bc7aef4e37847c0bc4a34f84d262cc9ab8bb..028ed27e5bbedcc4a3f2585abdb048a4326a5f06 100644
--- a/Usermode/Libraries/libc.so_src/include_exp/string.h
+++ b/Usermode/Libraries/libc.so_src/include_exp/string.h
@@ -29,6 +29,7 @@ extern char	*strrchr(const char *str, int character);
 extern char	*strstr(const char *str1, const char *str2);
 extern size_t	strcspn(const char *haystack, const char *reject);
 extern size_t	strspn(const char *haystack, const char *accept);
+extern char	*strpbrk(const char *haystack, const char *accept);
 
 extern char	*strtok(char *str, const char *delim);
 extern char	*strtok_r(char *str, const char *delim, char **saveptr);
diff --git a/Usermode/Libraries/libc.so_src/signals.c b/Usermode/Libraries/libc.so_src/signals.c
index 5e32fc8612ef88ac2ebcf903caac3448b1691164..532f09b732f57fe4ba9907218478b579df1a1301 100644
--- a/Usermode/Libraries/libc.so_src/signals.c
+++ b/Usermode/Libraries/libc.so_src/signals.c
@@ -41,6 +41,7 @@ int raise(int signal)
 void abort(void)
 {
 	// raise(SIGABRT);
+	_SysDebug("abort() - %p", __builtin_return_address(0));
 	_exit(-1);
 }
 
diff --git a/Usermode/Libraries/libc.so_src/stdio.c b/Usermode/Libraries/libc.so_src/stdio.c
index 1bb90cd99221b512ca8d1cf38ff7fd0f2162e538..55fd52134750e57d62f5aeaa04d273a03f8fa54d 100644
--- a/Usermode/Libraries/libc.so_src/stdio.c
+++ b/Usermode/Libraries/libc.so_src/stdio.c
@@ -14,6 +14,9 @@
 
 #define DEBUG_BUILD	0
 
+#define LOG_WARN(f,...)	_SysDebug("WARN: %s: "f, __func__ ,## __VA_ARGS__)
+#define LOG_NOTICE(f,...)	_SysDebug("NOTE: %s: "f, __func__ ,## __VA_ARGS__)
+
 // === CONSTANTS ===
 #define	_stdin	0
 #define	_stdout	1
@@ -27,9 +30,9 @@ struct sFILE	*get_file_struct();
 
 // === GLOBALS ===
 struct sFILE	_iob[STDIO_MAX_STREAMS];	// IO Buffer
-struct sFILE	*stdin;	// Standard Input
-struct sFILE	*stdout;	// Standard Output
-struct sFILE	*stderr;	// Standard Error
+struct sFILE	*stdin = &_iob[0];	// Standard Input
+struct sFILE	*stdout = &_iob[1];	// Standard Output
+struct sFILE	*stderr = &_iob[2];	// Standard Error
 ///\note Initialised in SoMain
 static const int STDIN_BUFSIZ = 512;
 static const int STDOUT_BUFSIZ = 512;
@@ -38,19 +41,16 @@ static const int STDOUT_BUFSIZ = 512;
 void _stdio_init(void)
 {
 	// Init FileIO Pointers
-	stdin = &_iob[0];
 	stdin->FD = 0;
 	stdin->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_READ|FILE_FLAG_LINEBUFFERED|FILE_FLAG_OURBUFFER;
 	stdin->Buffer = malloc(STDIN_BUFSIZ);
 	stdin->BufferSpace = STDIN_BUFSIZ;
 
-	stdout = &_iob[1];
 	stdout->FD = 1;
 	stdout->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE|FILE_FLAG_LINEBUFFERED|FILE_FLAG_OURBUFFER;
 	stdout->Buffer = malloc(STDOUT_BUFSIZ);
 	stdout->BufferSpace = STDOUT_BUFSIZ;
 	
-	stderr = &_iob[2];
 	stderr->FD = 2;
 	stderr->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE;
 }
@@ -374,22 +374,23 @@ int _fflush_int(FILE *fp)
 	return ret;
 }
 
-EXPORT void fflush(FILE *fp)
+EXPORT int fflush(FILE *fp)
 {
 	if( !fp || fp->FD == FD_NOTOPEN )
-		return ;
+		return EBADF;
 	
 	// Nothing to do for memory files
 	if( fp->FD == FD_MEMFILE )
-		return ;
+		return 0;
 	// Memory streams, update pointers
 	if( fp->FD == FD_MEMSTREAM ) {
 		*fp->BufPtr = fp->Buffer;
 		*fp->LenPtr = fp->BufferPos;
-		return ;
+		return 0;
 	}
 	
 	_fflush_int(fp);
+	return 0;
 }
 
 EXPORT void clearerr(FILE *fp)
@@ -681,19 +682,28 @@ EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)
 {
 	size_t	ret;
 	
-	if(!fp || fp->FD == -1)
+	if(!fp || fp->FD == -1) {
+		LOG_WARN("bad fp %p", fp);
 		return -1;
+	}
 	if( size == 0 || num == 0 )
 		return 0;
 	
 	if( _GetFileMode(fp) != FILE_FLAG_MODE_READ ) {
 		errno = 0;
+		LOG_WARN("not open for read");
+		if( fp == stdin ) {
+			LOG_WARN("BUGCHECK FAIL: stdin was not open for read");
+			exit(129);
+		}
 		return -1;
 	}
 
 	// Don't read if EOF is set
-	if( fp->Flags & FILE_FLAG_EOF )
+	if( fp->Flags & FILE_FLAG_EOF ) {
+		LOG_NOTICE("EOF");
 		return 0;
+	}
 
 	
 	if( fp->FD == FD_MEMFILE ) {
@@ -701,6 +711,7 @@ EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)
 	}
 	else if( fp->FD == FD_MEMSTREAM ) {
 		//return _fread_memstream(ptr, size, num, fp);
+		LOG_WARN("Reading from a mem stream");
 		errno = EBADF;
 		return 0;
 	}
@@ -744,6 +755,8 @@ EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)
 				extra, size, num
 				);
 		}
+		LOG_NOTICE("Incomplete read %i/%i bytes (object size %i)",
+			ret, bytes, size);
 	}
 	
 	return ret / size;
@@ -782,13 +795,12 @@ EXPORT char *fgets(char *s, int size, FILE *fp)
  */
 EXPORT int fputc(int c, FILE *fp)
 {
-	char	ch = c;
+	unsigned char	ch = c;
 	return fwrite(&ch, 1, 1, fp);
 }
 
 EXPORT int putchar(int c)
 {
-	c &= 0xFF;
 	return fputc(c, stdout);
 }
 
@@ -798,7 +810,7 @@ EXPORT int putchar(int c)
  */
 EXPORT int fgetc(FILE *fp)
 {
-	char	ret = 0;
+	unsigned char	ret = 0;
 	if( fread(&ret, 1, 1, fp) != 1 )
 		return -1;
 	return ret;
@@ -812,7 +824,6 @@ EXPORT int getchar(void)
 
 EXPORT int puts(const char *str)
 {
-	
 	if(!str)	return 0;
 	 int	len = strlen(str);
 	
diff --git a/Usermode/Libraries/libc.so_src/string.c b/Usermode/Libraries/libc.so_src/string.c
index aa79ec961676057343d1fcd833541f6641631d30..d61d51dde0d2b742b69c06733c166d6e464b7150 100644
--- a/Usermode/Libraries/libc.so_src/string.c
+++ b/Usermode/Libraries/libc.so_src/string.c
@@ -2,7 +2,7 @@
  * AcessOS Basic C Library
  * string.c
  */
-#include <acess/sys.h>
+//#include <acess/sys.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <ctype.h>
@@ -13,20 +13,19 @@
  * \fn EXPORT int strcmp(const char *s1, const char *s2)
  * \brief Compare two strings
  */
-EXPORT int strcmp(const char *s1, const char *s2)
+EXPORT int strcmp(const char *_s1, const char *_s2)
 {
-	while(*s1 && *s1 == *s2) {
-		s1++; s2++;
-	}
-	return (int)*s1 - (int)*s2;
+	return strncmp(_s1, _s2, SIZE_MAX);
 }
 
 /**
- * \fn EXPORT int strncmp(const char *s1, const char *s2)
- * \brief Compare two strings
+ * \fn EXPORT int strncmp(const char *s1, const char *s2, size_t n)
+ * \brief Compare two strings, stopping after n characters
  */
-EXPORT int strncmp(const char *s1, const char *s2, size_t n)
+EXPORT int strncmp(const char *_s1, const char *_s2, size_t n)
 {
+	const unsigned char*	s1 = (const unsigned char*)_s1;
+	const unsigned char*	s2 = (const unsigned char*)_s2;
 	while(n && *s1 && *s1 == *s2)
 	{
 		s1++; s2++;
@@ -38,23 +37,31 @@ EXPORT int strncmp(const char *s1, const char *s2, size_t n)
 		return (int)*s1 - (int)*s2;
 }
 
-EXPORT int strcasecmp(const char *s1, const char *s2)
+EXPORT int strcasecmp(const char *_s1, const char *_s2)
 {
-	 int	rv;
-	while( (rv = toupper(*s1) - toupper(*s2)) == 0 && *s1 != '\0' && *s2 != '\0' ) {
-		s1++; s2++;
-	}
-	return rv;
+	return strncasecmp(_s1, _s2, SIZE_MAX);
 }
 
-EXPORT int strncasecmp(const char *s1, const char *s2, size_t n)
+EXPORT int strncasecmp(const char *_s1, const char *_s2, size_t n)
 {
-	 int	rv = 0;
-	if( n == 0 )	return 0;
-	while(n -- && (rv = toupper(*s1) - toupper(*s2)) == 0 && *s1 != '\0' && *s2 != '\0') {
-		s1++; s2++;
+	const unsigned char*	s1 = (const unsigned char*)_s1;
+	const unsigned char*	s2 = (const unsigned char*)_s2;
+	while( n-- && *s1 && *s2 )
+	{
+		if( *s1 != *s2 )
+		{
+			int rv;
+			rv = toupper(*s1) - toupper(*s2);
+			if(rv != 0)
+				return rv;
+			rv = tolower(*s1) - tolower(*s2);
+			if(rv != 0)
+				return rv;
+		}
+		s1 ++;
+		s2 ++;
 	}
-	return rv;
+	return 0;
 }
 
 /**
@@ -80,8 +87,13 @@ EXPORT char *strcpy(char *dst, const char *src)
 EXPORT char *strncpy(char *dst, const char *src, size_t num)
 {
 	char *to = dst;
-	while(*src && num--)	*to++ = *src++;
-	*to = '\0';
+	while(num --)
+	{
+		if(*src)
+			*to++ = *src++;
+		else
+			*to++ = '\0';
+	}
 	return dst;
 }
 
@@ -118,9 +130,10 @@ EXPORT char *strncat(char *dst, const char *src, size_t n)
  */
 EXPORT size_t strlen(const char *str)
 {
-	size_t	retval;
-	for(retval = 0; *str != '\0'; str++, retval++);
-	return retval;
+	size_t	len = 0;
+	while(str[len] != '\0')
+		len ++;
+	return len;
 }
 
 /**
@@ -130,8 +143,9 @@ EXPORT size_t strlen(const char *str)
  */
 EXPORT size_t strnlen(const char *str, size_t maxlen)
 {
-	size_t	len;
-	for( len = 0; maxlen -- && *str; str ++, len ++ );
+	size_t	len = 0;
+	while( len < maxlen && str[len] != '\0' )
+		len ++;
 	return len;
 }
 
@@ -171,11 +185,12 @@ EXPORT char *strndup(const char *str, size_t maxlen)
  * \fn EXPORT char *strchr(char *str, int character)
  * \brief Locate a character in a string
  */
-EXPORT char *strchr(const char *str, int character)
+EXPORT char *strchr(const char *_str, int character)
 {
+	const unsigned char* str = (const unsigned char*)_str;
 	for(;*str;str++)
 	{
-		if(*str == character)
+		if( *str == character )
 			return (char*)str;
 	}
 	return NULL;
@@ -185,11 +200,10 @@ EXPORT char *strchr(const char *str, int character)
  * \fn EXPORT char *strrchr(char *str, int character)
  * \brief Locate the last occurance of a character in a string
  */
-EXPORT char *strrchr(const char *str, int character)
+EXPORT char *strrchr(const char *_str, int character)
 {
-	 int	i;
-	i = strlen(str)-1;
-	while(i--)
+	const unsigned char* str = (const unsigned char*)_str;
+	for( size_t i = strlen(_str); i--; )
 	{
 		if(str[i] == character)
 			return (void*)&str[i];
@@ -277,6 +291,8 @@ EXPORT void *memcpy(void *__dest, const void *__src, size_t count)
 	return __dest;
 }
 
+// TODO: memccpy (POSIX defined)
+
 /**
  * \fn EXPORT void *memmove(void *dest, const void *src, size_t count)
  * \brief Copy data in memory, avoiding overlap problems
@@ -316,7 +332,7 @@ EXPORT int memcmp(const void *mem1, const void *mem2, size_t count)
 	while(count--)
 	{
 		if( *p1 != *p2 )
-			return *p1 - *p2;
+			return (int)*p1 - (int)*p2;
 		p1 ++;
 		p2 ++;
 	}
@@ -332,11 +348,12 @@ EXPORT int memcmp(const void *mem1, const void *mem2, size_t count)
  */
 EXPORT void *memchr(const void *ptr, int value, size_t num)
 {
+	const unsigned char* buf = ptr;
 	while(num--)
 	{
-		if( *(const unsigned char*)ptr == (unsigned char)value )
-			return (void*)ptr;
-		ptr ++;
+		if( *buf == (unsigned char)value )
+			return (void*)buf;
+		buf ++;
 	}
 	return NULL;
 }
@@ -344,12 +361,13 @@ EXPORT void *memchr(const void *ptr, int value, size_t num)
 EXPORT size_t strcspn(const char *haystack, const char *reject)
 {
 	size_t	ret = 0;
-	 int	i;
 	while( *haystack )
 	{
-		for( i = 0; reject[i] && reject[i] == *haystack; i ++ );
-
-		if( reject[i] ) return ret;
+		for( int i = 0; reject[i]; i ++ )
+		{
+			if( reject[i] == *haystack )
+				return ret;
+		}
 		ret ++;
 	}
 	return ret;
@@ -358,17 +376,32 @@ EXPORT size_t strcspn(const char *haystack, const char *reject)
 EXPORT size_t strspn(const char *haystack, const char *accept)
 {
 	size_t	ret = 0;
-	 int	i;
 	while( *haystack )
 	{
-		for( i = 0; accept[i] && accept[i] == *haystack; i ++ );
-
-		if( !accept[i] )	return ret;
+		for( int i = 0; accept[i]; i ++ )
+		{
+			if( accept[i] != *haystack )
+				return ret;
+		}
 		ret ++;
 	}
 	return ret;
 }
 
+EXPORT char *strpbrk(const char *haystack, const char *accept)
+{
+	while( *haystack )
+	{
+		for( int i = 0; accept[i]; i ++ )
+		{
+			if( accept[i] == *haystack )
+				return (char*)haystack;
+		}
+		haystack ++;
+	}
+	return NULL;
+}
+
 char *strtok(char *str, const char *delim)
 {
 	static char *__saveptr;
diff --git a/Usermode/Libraries/libc.so_src/strtoi.c b/Usermode/Libraries/libc.so_src/strtoi.c
index de7f72545ef01aaab2b812f6a09b72ae8a139ef4..84bf0005919edbacef6a2dc0967f07fffe40d88c 100644
--- a/Usermode/Libraries/libc.so_src/strtoi.c
+++ b/Usermode/Libraries/libc.so_src/strtoi.c
@@ -12,7 +12,7 @@
 
 unsigned long long strtoull(const char *str, char **end, int base)
 {
-	long long	ret = 0;
+	unsigned long long	ret = 0;
 	
 	if( !str || base < 0 || base > 36 || base == 1 ) {
 		if(end)
@@ -27,7 +27,7 @@ unsigned long long strtoull(const char *str, char **end, int base)
 	
 	// Handle base detection for hex
 	if( base == 0 || base == 16 ) {
-		if( *str == '0' && str[1] == 'x' ) {
+		if( *str == '0' && (str[1] == 'x' || str[1] == 'X') && isxdigit(str[2]) ) {
 			str += 2;
 			base = 16;
 		}
@@ -43,6 +43,10 @@ unsigned long long strtoull(const char *str, char **end, int base)
 	if( base == 0 )
 		base = 10;
 
+	// Value before getting within 1 digit of ULLONG_MAX
+	// - Used to avoid overflow in more accurate check
+	unsigned long long	max_before_ullong_max = ULLONG_MAX / base;
+	unsigned int	space_above = ULLONG_MAX - max_before_ullong_max * base;
 	while( *str )
 	{
 		 int	next = -1;
@@ -58,8 +62,31 @@ unsigned long long strtoull(const char *str, char **end, int base)
 			if( 'a' <= *str && *str <= 'a'+base-10-1 )
 				next = *str - 'a' + 10;
 		}
+		//_SysDebug("strtoull - ret=0x%llx,next=%i,str='%s'", ret, next, str);
 		if( next < 0 )
 			break;
+		
+		// If we're already out of range, keep eating
+		if( ret == ULLONG_MAX ) {
+			errno = ERANGE;
+			str ++;
+			// Keep eating until first unrecognised character
+			continue;
+		}
+	
+		// Rough then accurate check against max value
+		if( ret >= max_before_ullong_max )
+		{
+			//_SysDebug("strtoull - 0x%llx>0x%llx", ret, max_before_ullong_max);
+			if( (ret - max_before_ullong_max) * base + next > space_above ) {
+				//_SysDebug("strtoull - %u*%u+%u (%u) > %u",
+				//	(unsigned int)(ret - max_before_ullong_max), base, next, space_above);
+				ret = ULLONG_MAX;
+				errno = ERANGE;
+				str ++;
+				continue;
+			}
+		}
 		ret *= base;
 		ret += next;
 		str ++;
@@ -85,7 +112,6 @@ unsigned long strtoul(const char *ptr, char **end, int base)
 long long strtoll(const char *str, char **end, int base)
 {
 	 int	neg = 0;
-	unsigned long long	ret;
 
 	if( !str ) {
 		errno = EINVAL;
@@ -96,17 +122,37 @@ long long strtoll(const char *str, char **end, int base)
 		str++;
 	
 	// Check for negative (or positive) sign
-	if(*str == '-' || *str == '+') {
+	if(*str == '-' || *str == '+')
+	{
+		//_SysDebug("strtoll - str[0:1] = '%.2s'", str);
+		if( !isdigit(str[1]) ) {
+			// Non-digit, invalid string
+			if(end)	*end = (char*)str;
+			return 0;
+		}
 		neg = (*str == '-');
 		str++;
 	}
 
-	ret = strtoull(str, end, base);	
+	unsigned long long ret = strtoull(str, end, base);	
+	//_SysDebug("strtoll - neg=%i,ret=%llu", neg, ret);
 
-	if( neg )
+	if( neg ) {
+		// Abuses unsigned integer overflow
+		if( ret + LLONG_MIN < ret ) {
+			errno = ERANGE;
+			return LLONG_MIN;
+		}
 		return -ret;
+	}
 	else
+	{
+		if( ret > LLONG_MAX ) {
+			errno = ERANGE;
+			return LLONG_MAX;
+		}
 		return ret;
+	}
 }
 
 long strtol(const char *str, char **end, int base)
diff --git a/Usermode/Libraries/libc.so_src/stub.c b/Usermode/Libraries/libc.so_src/stub.c
index a1c67ffb26ab48447e24465a70cb4199981d23f4..922943e1e71a7d4a120e583f85ca0e07c5e58d1c 100644
--- a/Usermode/Libraries/libc.so_src/stub.c
+++ b/Usermode/Libraries/libc.so_src/stub.c
@@ -45,21 +45,8 @@ tCPUID	gCPU_Features;
  */
 int SoMain(UNUSED(uintptr_t, BaseAddress), UNUSED(int, argc), UNUSED(char **, argv), char **envp)
 {
-	// Init for env.c
 	environ = envp;
 
-	#if 0	
-	{
-		 int	i = 0;
-		char	**tmp;
-		_SysDebug("envp = %p", envp);
-		for(tmp = envp; *tmp; tmp++,i++)
-		{
-			_SysDebug("envp[%i] = '%s'", i, *tmp);
-		}
-	}
-	#endif
-
 	_stdio_init();	
 	
 	#if USE_CPUID
@@ -76,7 +63,7 @@ int SoMain(UNUSED(uintptr_t, BaseAddress), UNUSED(int, argc), UNUSED(char **, ar
 	// Set Error handler
 	_SysSetFaultHandler(ErrorHandler);
 	
-	return 1;
+	return 0;
 }
 
 int ErrorHandler(int Fault)
diff --git a/Usermode/Libraries/libc.so_src/time.c b/Usermode/Libraries/libc.so_src/time.c
index 185af88f0a0c4019b462e31d525e7e9880b0e172..611ae9b0de3b566f07ceb01eca67ddac29674d6d 100644
--- a/Usermode/Libraries/libc.so_src/time.c
+++ b/Usermode/Libraries/libc.so_src/time.c
@@ -145,11 +145,18 @@ size_t strftime(char*restrict s, size_t maxsize, const char*restrict format, con
 		if( *format == 0 )
 			break;
 		format ++;
+		
+		// If EOS is hit on a '%', break early
+		if( *format == 0 )
+			break;
 		switch(*format++)
 		{
-		case 0:	format--;	break;
-		case '%':	ofs += _puts(s, maxsize, ofs, format-1, 1);	break;
-		case 'd':	// The day of the month as a decimal number (range 01 to 31).
+		// Literal '%', 
+		case '%':
+			ofs += _puts(s, maxsize, ofs, format-1, 1);
+			break;
+		// The day of the month as a decimal number (range 01 to 31).
+		case 'd':
 			{
 			char tmp[2] = {'0','0'};
 			tmp[0] += (timeptr->tm_mday / 10) % 10;
@@ -157,6 +164,24 @@ size_t strftime(char*restrict s, size_t maxsize, const char*restrict format, con
 			ofs += _puts(s, maxsize, ofs, tmp, 2);
 			}
 			break;
+		// Two-digit 24 hour
+		case 'H':
+			{
+			char tmp[2] = {'0','0'};
+			tmp[0] += (timeptr->tm_hour / 10) % 10;
+			tmp[1] +=  timeptr->tm_hour % 10;
+			ofs += _puts(s, maxsize, ofs, tmp, 2);
+			}
+			break;
+		// Two-digit minutes
+		case 'M':
+			{
+			char tmp[2] = {'0','0'};
+			tmp[0] += (timeptr->tm_min / 10) % 10;
+			tmp[1] +=  timeptr->tm_min % 10;
+			ofs += _puts(s, maxsize, ofs, tmp, 2);
+			}
+			break;
 		default:
 			_SysDebug("TODO: strftime('...%%%c...')", format[-1]);
 			break;
diff --git a/Usermode/Libraries/libc.so_src/timeconv.c b/Usermode/Libraries/libc.so_src/timeconv.c
index 6fed585ab39f2239c82ff53a9fe2e1bec013729b..00eb13f3205c3bb1e89514fb94c84659c9bd51d7 100644
--- a/Usermode/Libraries/libc.so_src/timeconv.c
+++ b/Usermode/Libraries/libc.so_src/timeconv.c
@@ -114,7 +114,7 @@ int64_t get_days_since_y2k(int64_t ts, int *h, int *m, int *s)
 	ts -= n_leap;
 	#endif
 	
-	int64_t	days = ts / 24*60*60;
+	int64_t	days = ts / (24*60*60);
 	int64_t seconds = ts % (24*60*60);
 	*s = (is_ls ? 60 : seconds % 60);
 	*m = (seconds/60 % 24);
diff --git a/Usermode/Libraries/libiconv.so_src/Makefile b/Usermode/Libraries/libiconv.so_src/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..50f257ac5322653b853e3b2b119f2da6d5befc4b
--- /dev/null
+++ b/Usermode/Libraries/libiconv.so_src/Makefile
@@ -0,0 +1,15 @@
+# Acess 2 "libiconv"
+#
+
+include ../Makefile.cfg
+
+CPPFLAGS +=
+CFLAGS   += -Wall
+LDFLAGS  += -lc -soname libiconv.so
+
+OBJ = iconv.o
+BIN = libiconv.so
+
+include ../Makefile.tpl
+
+
diff --git a/Usermode/Libraries/libiconv.so_src/iconv.c b/Usermode/Libraries/libiconv.so_src/iconv.c
new file mode 100644
index 0000000000000000000000000000000000000000..d339555caeb9660b272976d019c47c0c9e6fc870
--- /dev/null
+++ b/Usermode/Libraries/libiconv.so_src/iconv.c
@@ -0,0 +1,27 @@
+/*
+ */
+#include <iconv.h>
+#include <acess/sys.h>
+
+// === CODE ===
+int SoMain(void)
+{
+	return 0;
+}
+
+iconv_t iconv_open(const char *to, const char *from)
+{
+	return NULL;
+}
+
+size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
+{
+	_SysDebug("WTF are you using iconv for?");
+	return 0;
+}
+
+int iconv_close(iconv_t cd)
+{
+	return 0;
+}
+
diff --git a/Usermode/Libraries/libiconv.so_src/include_exp/iconv.h b/Usermode/Libraries/libiconv.so_src/include_exp/iconv.h
new file mode 100644
index 0000000000000000000000000000000000000000..817abeed70f6540c6002107a74d9e8b8017b76cb
--- /dev/null
+++ b/Usermode/Libraries/libiconv.so_src/include_exp/iconv.h
@@ -0,0 +1,20 @@
+/*
+ * Acess2 libiconv
+ * - By John Hodge (thePowersGang)
+ *
+ * iconv.h
+ * - External header
+ */
+#ifndef _ICONV_H_
+#define _ICONV_H_
+
+#include <stddef.h>
+
+typedef void	*iconv_t;
+
+extern iconv_t	iconv_open(const char *to, const char *from);
+extern size_t	iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
+extern int	iconv_close(iconv_t cd);
+
+#endif
+
diff --git a/Usermode/Libraries/libintl.so_src/Makefile b/Usermode/Libraries/libintl.so_src/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..398ffc4ea1ee0b0eb2414cc45ac550367bc0d9dc
--- /dev/null
+++ b/Usermode/Libraries/libintl.so_src/Makefile
@@ -0,0 +1,16 @@
+# Acess 2 "libintl"
+#
+
+include ../Makefile.cfg
+
+CPPFLAGS +=
+CFLAGS   += -Wall
+LDFLAGS  += -lc
+
+OBJ = main.o gettext.o
+BIN = libintl.so
+
+include ../Makefile.tpl
+
+
+
diff --git a/Usermode/Libraries/libintl.so_src/gettext.c b/Usermode/Libraries/libintl.so_src/gettext.c
new file mode 100644
index 0000000000000000000000000000000000000000..3b10d411393090233a0b8e93094618ab807b8407
--- /dev/null
+++ b/Usermode/Libraries/libintl.so_src/gettext.c
@@ -0,0 +1,35 @@
+/*
+ */
+#include <stddef.h>
+#include <libintl.h>
+
+// === CODE ===
+char *gettext(const char *msg)
+{
+	return dcgettext(NULL, msg, 0);
+}
+char *dgettext(const char *domain, const char *msg)
+{
+	return dcgettext(domain, msg, 0);
+}
+char *dcgettext(const char *domain, const char *msg, int category)
+{
+	return (char*)msg;
+}
+
+char *ngettext(const char *msg, const char *msgp, unsigned long int n)
+{
+	return dcngettext(NULL, msg, msgp, n, 0);
+}
+char *dngettext(const char *domain, const char *msg, const char *msgp, unsigned long int n)
+{
+	return dcngettext(domain, msg, msgp, n, 0);
+}
+char *dcngettext(const char *domain, const char *msg, const char *msgp, unsigned long int n, int category)
+{
+	if( n == 1 )
+		return (char*)msg;
+	else
+		return (char*)msgp;
+}
+
diff --git a/Usermode/Libraries/libintl.so_src/include_exp/libintl.h b/Usermode/Libraries/libintl.so_src/include_exp/libintl.h
new file mode 100644
index 0000000000000000000000000000000000000000..54c0993809134cd7e13d5f31b334fc95cc6c03ff
--- /dev/null
+++ b/Usermode/Libraries/libintl.so_src/include_exp/libintl.h
@@ -0,0 +1,13 @@
+/*
+ */
+#ifndef _LIBINTL_H_
+#define _LIBINTL_H_
+
+extern char	*gettext(const char *msg);
+extern char	*dgettext(const char *domain, const char *msg);
+extern char	*dcgettext(const char *domain, const char *msg, int category);
+extern char	*ngettext(const char *msg, const char *msgp, unsigned long int n);
+extern char	*dngettext(const char *domain, const char *msg, const char *msgp, unsigned long int n);
+extern char	*dcngettext(const char *domain, const char *msg, const char *msgp, unsigned long int n, int category);
+
+#endif
diff --git a/Usermode/Libraries/libintl.so_src/main.c b/Usermode/Libraries/libintl.so_src/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..30dead738e1dc9fab6339151b2c2ee5028033556
--- /dev/null
+++ b/Usermode/Libraries/libintl.so_src/main.c
@@ -0,0 +1,7 @@
+/*
+ */
+
+int SoMain(void)
+{
+	return 0;
+}
diff --git a/Usermode/Libraries/libm.so_src/include_exp/math.h b/Usermode/Libraries/libm.so_src/include_exp/math.h
index d6d988f22c2deba6104df32e5b0d831083e155b4..a86001217923beb5097896521a208b2ed10e0e85 100644
--- a/Usermode/Libraries/libm.so_src/include_exp/math.h
+++ b/Usermode/Libraries/libm.so_src/include_exp/math.h
@@ -12,6 +12,13 @@
 extern "C" {
 #endif
 
+typedef float	float_t;
+typedef double	double_t;
+
+
+#define INFINITY	(*(float*)((uint32_t[]){0x78000000}))
+#define NAN	(*(float*)((uint32_t[]){0x78000001}))
+
 extern double	pow(double x, double y);
 extern double	exp(double x);
 extern double	log(double val);
diff --git a/Usermode/Libraries/libnet.so_src/Makefile b/Usermode/Libraries/libnet.so_src/Makefile
index 29abaaa0e95d8b5223426b8d364fb89b4f6bd7ee..10d5b9620d45e8c01fc8fb6b77b5bc4f173a1640 100644
--- a/Usermode/Libraries/libnet.so_src/Makefile
+++ b/Usermode/Libraries/libnet.so_src/Makefile
@@ -7,6 +7,9 @@ CFLAGS   += -Wall
 LDFLAGS  += -lc -soname libnet.so
 
 OBJ = main.o address.o socket.o
+OBJ += hostnames.o dns.o
 BIN = libnet.so
 
+UTESTS = dns
+
 include ../Makefile.tpl
diff --git a/Usermode/Libraries/libnet.so_src/TEST_dns.c b/Usermode/Libraries/libnet.so_src/TEST_dns.c
new file mode 100644
index 0000000000000000000000000000000000000000..26b36bf50481273e33c8fca0febdd67c0f92d572
--- /dev/null
+++ b/Usermode/Libraries/libnet.so_src/TEST_dns.c
@@ -0,0 +1,52 @@
+/*
+ */
+#include "include/dns.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+
+extern int DNS_int_ParseResponse(const void* packet, size_t return_len, void *info, handle_record_t* handle_record_t);
+
+// Complex response from "Q ssh.ucc.asn.au A IN"
+const uint8_t test_packet_1[] = {
+	0xac, 0x00, 0x80, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x03, 0x73, 0x73, 0x68, 0x03, 0x75, 0x63, 0x63, 0x03, 0x61, 0x73, 0x6e, 0x02, 0x61, 0x75, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x77, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x78, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x79, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x61, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x7a, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x62, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x75, 0xc0, 0x18, 0xc0, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x01, 0x76, 0xc0, 0x18, 0xc0, 0x2c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x25, 0xd1, 0xc0, 0x05, 0xc0, 0x3c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x25, 0xd1, 0xc2, 0x05, 0xc0, 0x4c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x25, 0xd1, 0xc4, 0x05, 0xc0, 0x5c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x3a, 0x41, 0xfe, 0x49, 0xc0, 0x6c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x25, 0xd1, 0xc6, 0x05, 0xc0, 0x7c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0x3a, 0x41, 0xfd, 0x49, 0xc0, 0x8c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0xd3, 0x1d, 0x85, 0x20, 0xc0, 0x9c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xe6, 0xd6, 0x00, 0x04, 0xca, 0x0c, 0x1f, 0x8d, 
+	};
+
+#define TEST_REL_INT(exp, rel, have) do { \
+	int a = (exp);\
+	int b = (have);\
+	if( !(a rel b) ) { \
+		fprintf(stderr, "TEST_REL_INT("#exp" "#rel" "#exp") FAILED l=%i r=%i", \
+			a, b); \
+		return 1; \
+	} \
+} while(0)
+	
+int test_response_parse_1_cb(void *info, const char *name, enum eTypes type, enum eClass class, unsigned int ttl, size_t rdlength, const void *rdata)
+{
+	int* stagep = info;
+	switch( *stagep )
+	{
+	case 0:
+		TEST_REL_INT(0, ==, strcmp(name, "au."));
+		break;
+	}
+	(*stagep) += 1;
+	return 0;
+}
+int test_response_parse_1(void)
+{
+	int stage = 0;
+	TEST_REL_INT(0, ==, DNS_int_ParseResponse(test_packet_1, sizeof(test_packet_1), &stage, test_response_parse_1_cb) );
+	return 0;
+}
+
+int main(void)
+{
+	 int rv = 0;
+	// - Name Encode
+	// - Name Decode
+	// - Response parsing
+	rv |= test_response_parse_1();
+	return rv;
+}
diff --git a/Usermode/Libraries/libnet.so_src/address.c b/Usermode/Libraries/libnet.so_src/address.c
index 66a28b1b45b22fe08b75d11dcc7c7ff1914a05a0..d103e64b04fee12d7951049fee38b6215e6a484e 100644
--- a/Usermode/Libraries/libnet.so_src/address.c
+++ b/Usermode/Libraries/libnet.so_src/address.c
@@ -7,7 +7,8 @@
  */
 #include <net.h>
 #include <stdint.h>
-#include <stdio.h>
+#include <stdio.h>	// sprintf
+#include <stdlib.h>
 #define DEBUG	0
 
 static inline uint32_t htonl(uint32_t v)
@@ -37,43 +38,50 @@ static inline uint16_t htons(uint16_t v)
  */
 static int Net_ParseIPv4Addr(const char *String, uint8_t *Addr)
 {
-	 int	i = 0;
 	 int	j;
-	 int	val;
+	const char *pos = String;
 	
-	for( j = 0; String[i] && j < 4; j ++ )
+	for( j = 0; *pos && j < 4; j ++ )
 	{
-		val = 0;
-		for( ; String[i] && String[i] != '.'; i++ )
-		{
-			if('0' > String[i] || String[i] > '9') {
-				#if DEBUG
-				printf("0<c<9 expected, '%c' found\n", String[i]);
-				#endif
-				return 0;
-			}
-			val = val*10 + String[i] - '0';
+		char	*end;
+		unsigned long val = strtoul(pos, &end, 10);
+		if( *end && *end != '.' ) {
+			#if DEBUG
+			_SysDebug("%s: Unexpected character, '%c' found", __func__, *end);
+			#endif
+			return 0;
+		}
+		if( *pos == '.' ) {
+			#if DEBUG
+			_SysDebug("%s: Two dots in a row", __func__);
+			#endif
+			return 0;
 		}
 		if(val > 255) {
 			#if DEBUG
-			printf("val > 255 (%i)\n", val);
+			_SysDebug("%s: val > 255 (%i)", __func__, val);
 			#endif
 			return 0;
 		}
+		#if DEBUG
+		_SysDebug("%s: Comp '%.*s' = %lu", __func__, end - pos, pos, val);
+		#endif
 		Addr[j] = val;
 		
-		if(String[i] == '.')
-			i ++;
+		pos = end;
+		
+		if(*pos == '.')
+			pos ++;
 	}
 	if( j != 4 ) {
 		#if DEBUG
-		printf("4 parts expected, %i found\n", j);
+		_SysDebug("%s: 4 parts expected, %i found", __func__, j);
 		#endif
 		return 0;
 	}
-	if(String[i] != '\0') {
+	if(*pos != '\0') {
 		#if DEBUG
-		printf("EOS != '\\0', '%c'\n", String[i]);
+		_SysDebug("%s: EOS != '\\0', '%c'", __func__, *pos);
 		#endif
 		return 0;
 	}
diff --git a/Usermode/Libraries/libnet.so_src/dns.c b/Usermode/Libraries/libnet.so_src/dns.c
new file mode 100644
index 0000000000000000000000000000000000000000..3bb9c35575c767834d28031d4e8e7b234f4fd52e
--- /dev/null
+++ b/Usermode/Libraries/libnet.so_src/dns.c
@@ -0,0 +1,338 @@
+/*
+ * Acess2 Networking Toolkit
+ * By John Hodge (thePowersGang)
+ * 
+ * dns.c
+ * - Hostname<->Address resolution
+ */
+#include <stddef.h>	// size_t / NULL
+#include <stdint.h>	// uint*_t
+#include <string.h>	// memcpy, strchr
+#include <assert.h>
+#include <acess/sys.h>	// for _SysSelect
+#include <acess/fd_set.h>	// FD_SET
+#include <net.h>
+#include "include/dns.h"
+
+// === PROTOTYPES ===
+//int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eTypes type, enum eClass class, handle_record_t* handle_record, void *info);
+int DNS_int_ParseResponse(const void* packet, size_t return_len, void *info, handle_record_t* handle_record_t);
+size_t	DNS_EncodeName(void *buf, const char *dotted_name);
+int DNS_DecodeName(char dotted_name[256], const void *buf, size_t ofs, size_t space);
+int DNS_int_ParseRR(const void *buf, size_t ofs, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p);
+
+static uint16_t	get16(const void *buf);
+static uint32_t	get32(const void *buf);
+static size_t put16(void *buf, uint16_t val);
+
+
+// === CODE ===
+int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eTypes type, enum eClass class, handle_record_t* handle_record, void *info)
+{
+	int namelen = DNS_EncodeName(NULL, name);
+	assert(namelen < 256);
+	size_t	pos = 0;
+	char	packet[ 512 ];
+	assert( (6*2) + (namelen + 2*2) < 512 );
+	// - Header
+	pos += put16(packet + pos, 0xAC00);	// Identifier (arbitary)
+	pos += put16(packet + pos, (0 << 0) | (0 << 1) | (1 << 8) );	// Op : Query, Standard, Recursion
+	pos += put16(packet + pos, 1);	// QDCount
+	pos += put16(packet + pos, 0);	// ANCount
+	pos += put16(packet + pos, 0);	// NSCount
+	pos += put16(packet + pos, 0);	// ARCount
+	// - Question
+	pos += DNS_EncodeName(packet + pos, name);
+	pos += put16(packet + pos, type);	// QType
+	pos += put16(packet + pos, class);	// QClass
+	
+	assert(pos <= sizeof(packet));
+	
+	// Send and wait for reply
+	// - Lock
+	//  > TODO: Lock DNS queries
+	// - Send
+	int sock = Net_OpenSocket_UDP(ServerAType, ServerAddr, 53, 0);
+	if( sock < 0 ) {
+		// Connection failed
+		_SysDebug("DNS_Query - UDP open failed");
+		// TODO: Correctly report this failure with a useful error code
+		return 1;
+	}
+	int rv = Net_UDP_SendTo(sock, 53, ServerAType, ServerAddr, pos, packet);
+	if( rv != pos ) {
+		_SysDebug("DNS_Query - Write failed");
+		// TODO: Error reporting
+		_SysClose(sock);
+		return 1;
+	}
+	// - Wait
+	{
+		 int	nfd = sock + 1;
+		fd_set	fds;
+		FD_ZERO(&fds);
+		FD_SET(sock, &fds);
+		int64_t	timeout = 2000;	// Give it two seconds, should be long enough
+		rv = _SysSelect(nfd, &fds, NULL, NULL, &timeout, 0);
+		if( rv == 0 ) {
+			// Timeout with no reply, give up
+			_SysDebug("DNS_Query - Timeout");
+			_SysClose(sock);
+			return 1;
+		}
+		if( rv < 0 ) {
+			// Oops, select failed
+			_SysDebug("DNS_Query - Select failure");
+			_SysClose(sock);
+			return 1;
+		}
+	}
+	int return_len = Net_UDP_RecvFrom(sock, NULL, NULL, NULL, sizeof(packet), packet);
+	if( return_len <= 0 ) {
+		// TODO: Error reporting
+		_SysDebug("DNS_Query - Read failure");
+		_SysClose(sock);
+		return 1;
+	}
+	_SysClose(sock);
+	// - Release
+	//  > TODO: Lock DNS queries
+	
+	// For each response in the answer (and additional) sections, call the passed callback
+	return DNS_int_ParseResponse(packet, return_len, info, handle_record);
+}
+
+int DNS_int_ParseResponse(const void* buf, size_t return_len, void *info, handle_record_t* handle_record)
+{
+	const uint8_t* packet = buf;
+	char	rr_name[256];
+	unsigned int id = get16(packet + 0);
+	if( id != 0xAC00 ) {
+		_SysDebug("DNS_Query - Packet ID mismatch");
+		return 2;
+	}
+	unsigned int flags = get16(packet + 2);
+	unsigned int qd_count = get16(packet + 4);
+	unsigned int an_count = get16(packet + 6);
+	unsigned int ns_count = get16(packet + 8);
+	unsigned int ar_count = get16(packet + 10);
+	size_t pos = 6*2;
+	// TODO: Can I safely assert / fail if qd_count is non-zero?
+	// - Questions, ignored
+	for( unsigned int i = 0; i < qd_count; i ++ ) {
+		int rv = DNS_DecodeName(rr_name, packet, pos, return_len);
+		if( rv < 0 ) {
+			_SysDebug("DNS_Query - Parse error in QD");
+			return 1;
+		}
+		pos += rv + 2*2;
+	}
+	// - Answers, pass on to handler
+	for( unsigned int i = 0; i < an_count; i ++ )
+	{
+		enum eTypes	type;
+		enum eClass	class;
+		uint32_t	ttl;
+		size_t	rdlength;
+		int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, &type, &class, &ttl, &rdlength);
+		if( rv < 0 ) {
+			_SysDebug("DNS_Query - Parse error in AN");
+			return 1;
+		}
+		pos += rv;
+		
+		handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos - rdlength);
+	}
+	// Authority Records (should all be NS records)
+	for( unsigned int i = 0; i < ns_count; i ++ )
+	{
+		size_t	rdlength;
+		int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, NULL, NULL, NULL, &rdlength);
+		if( rv < 0 ) {
+			_SysDebug("DNS_Query - Parse error in NS");
+			return 1;
+		}
+		pos += rv;
+	}
+	// - Additional records, pass to handler
+	for( unsigned int i = 0; i < ar_count; i ++ )
+	{
+		enum eTypes	type;
+		enum eClass	class;
+		uint32_t	ttl;
+		size_t	rdlength;
+		int rv = DNS_int_ParseRR(packet, pos, return_len, rr_name, &type, &class, &ttl, &rdlength);
+		if( rv < 0 ) {
+			_SysDebug("DNS_Query - Parse error in AR");
+			return 1;
+		}
+		pos += rv;
+		
+		handle_record(info, rr_name, type, class, ttl, rdlength, packet + pos - rdlength);
+	}
+	
+	return 0;
+}
+
+/// Encode a dotted name as a DNS name
+size_t	DNS_EncodeName(void *buf, const char *dotted_name)
+{
+	size_t	ret = 0;
+	const char *str = dotted_name;
+	uint8_t	*buf8 = buf;
+	while( *str )
+	{
+		const char *next = strchr(str, '.');
+		size_t seg_len = (next ? next - str : strlen(str));
+		if( seg_len > 63 ) {
+			// Oops, too long (truncate)
+			seg_len = 63;
+		}
+		if( seg_len == 0 && next != NULL ) {
+			// '..' encountered, invalid (skip)
+			str = next+1;
+			continue ;
+		}
+		
+		if( buf8 )
+		{
+			buf8[ret] = seg_len;
+			memcpy(buf8+ret+1, str, seg_len);
+		}
+		ret += 1 + seg_len;
+		
+		if( next == NULL ) {
+			// No trailing '.', assume it's there? Yes, need to be NUL terminated
+			if(buf8)	buf8[ret] = 0;
+			ret ++;
+			break;
+		}
+		else {
+			str = next + 1;
+		}
+	}
+	return ret;
+}
+
+// Decode a name (including trailing . for root)
+int DNS_DecodeName(char dotted_name[256], const void *buf, size_t ofs, size_t space)
+{
+	int consumed = 0;
+	int out_pos = 0;
+	const uint8_t *buf8 = (const uint8_t*)buf + ofs;
+	for( ;; )
+	{
+		if( ofs + consumed + 1 > space ) {
+			_SysDebug("DNS_DecodeName - Len byte OOR space=%i", space);
+			return -1;
+		}
+		uint8_t	seg_len = *buf8;
+		buf8 ++;
+		consumed ++;
+		// Done
+		if( seg_len == 0 )
+			break;
+		if( (seg_len & 0xC0) == 0xC0 )
+		{
+			// Backreference, the rest of the name is a backref
+			char tmp[256];
+			int ref_ofs = get16(buf8 - 1) & 0x3FFF;
+			consumed += 1, buf8 += 1;	// Only one, previous inc still applies
+			_SysDebug("DNS_DecodeName - Nested at %i", ref_ofs);
+			if( DNS_DecodeName(tmp, buf, ref_ofs, space) < 0 )
+				return -1;
+			memcpy(dotted_name+out_pos, tmp, strlen(tmp));
+			out_pos += strlen(tmp);
+			break;
+		}
+		// Protocol violation (segment too long)
+		if( seg_len >= 64 ) {
+			_SysDebug("DNS_DecodeName - Seg too long %i", seg_len);
+			return -1;
+		}
+		// Protocol violation (overflowed end of buffer)
+		if( ofs + consumed + seg_len > space ) {
+			_SysDebug("DNS_DecodeName - Seg OOR %i+%i>%i", consumed, seg_len, space);
+			return -1;
+		}
+		// Protocol violation (name was too long)
+		if( out_pos + seg_len + 1 > 255 ) {
+			_SysDebug("DNS_DecodeName - Dotted name too long %i+%i+1 > %i",
+				out_pos, seg_len, 255);
+			return -1;
+		}
+		
+		_SysDebug("DNS_DecodeName : Seg %i '%.*s'", seg_len, seg_len, buf8);
+		
+		// Read segment
+		memcpy(dotted_name + out_pos, buf8, seg_len);
+		buf8 += seg_len;
+		consumed += seg_len;
+		out_pos += seg_len;
+		
+		// Place '.'
+		dotted_name[out_pos] = '.';
+		out_pos ++;
+	}
+	dotted_name[out_pos] = '\0';
+	_SysDebug("DNS_DecodeName - '%s', consumed = %i", dotted_name, consumed);
+	return consumed;
+}
+
+// Parse a Resource Record
+int DNS_int_ParseRR(const void *buf, size_t ofs, size_t space, char* name_p, enum eTypes* type_p, enum eClass* class_p, uint32_t* ttl_p, size_t* rdlength_p)
+{
+	const uint8_t	*buf8 = buf;
+	size_t	consumed = 0;
+	
+	// 1. Name
+	int rv = DNS_DecodeName(name_p, buf, ofs, space);
+	if(rv < 0)	return -1;
+	
+	ofs += rv, consumed += rv;
+	
+	if( type_p )
+		*type_p = get16(buf8 + ofs);
+	ofs += 2, consumed += 2;
+	
+	if( class_p )
+		*class_p = get16(buf8 + ofs);
+	ofs += 2, consumed += 2;
+	
+	if( ttl_p )
+		*ttl_p = get32(buf + ofs);
+	ofs += 4, consumed += 4;
+	
+	size_t rdlength = get16(buf + ofs);
+	if( rdlength_p )
+		*rdlength_p = rdlength;
+	ofs += 2, consumed += 2;
+	
+	_SysDebug("DNS_int_ParseRR - name='%s', rdlength=%i", name_p, rdlength);
+	
+	return consumed + rdlength;
+}
+
+static uint16_t get16(const void *buf) {
+	const uint8_t* buf8 = buf;
+	uint16_t rv = 0;
+	rv |= (uint16_t)buf8[0] << 8;
+	rv |= (uint16_t)buf8[1] << 0;
+	return rv;
+}
+static uint32_t get32(const void *buf) {
+	const uint8_t* buf8 = buf;
+	uint32_t rv = 0;
+	rv |= (uint32_t)buf8[0] << 24;
+	rv |= (uint32_t)buf8[1] << 16;
+	rv |= (uint32_t)buf8[2] << 8;
+	rv |= (uint32_t)buf8[3] << 0;
+	return rv;
+}
+static size_t put16(void *buf, uint16_t val) {
+	uint8_t* buf8 = buf;
+	buf8[0] = val >> 8;
+	buf8[1] = val & 0xFF;
+	return 2;
+}
+
diff --git a/Usermode/Libraries/libnet.so_src/hostnames.c b/Usermode/Libraries/libnet.so_src/hostnames.c
new file mode 100644
index 0000000000000000000000000000000000000000..c3c962681164ac02dbc769c9718792cc107a6424
--- /dev/null
+++ b/Usermode/Libraries/libnet.so_src/hostnames.c
@@ -0,0 +1,164 @@
+/*
+ * Acess2 Networking Toolkit
+ * By John Hodge (thePowersGang)
+ * 
+ * dns.c
+ * - Hostname<->Address resolution
+ */
+#include <net.h>
+#include "include/dns.h"
+#include <string.h>
+#include <stdlib.h>	// malloc (for loading config)
+#include <stdbool.h>
+#include <acess/sys.h>	// _SysDebug
+
+// === TYPES ===
+struct sDNSServer
+{
+	 int	AddrType;
+	char	AddrData[16];
+};
+
+struct sHostEntry
+{
+	 int	AddrType;
+	char	AddrData[16];
+	char	*Names[];
+};
+
+struct sLookupAnyInfo
+{
+	 int	expected_type;
+	void	*dest_ptr;
+	bool	have_result;
+};
+struct sDNSCallbackInfo
+{
+	void	*cb_info;
+	tNet_LookupAddrs_Callback	*callback;
+	enum eTypes	desired_type;
+	enum eClass	desired_class;
+	bool	got_value;
+};
+
+// === PROTOTYPES ===
+ int	int_lookupany_callback(void *info_v, int AddrType, const void *Addr);
+void	int_DNS_callback(void *info, const char *name, enum eTypes type, enum eClass class, unsigned int ttl, size_t rdlength, const void *rdata);
+
+// === GLOBALS ===
+ int	giNumDNSServers;
+struct sDNSServer	*gaDNSServers;
+ int	giNumHostEntries;
+struct sHostEntry	*gaHostEntries;
+
+// === CODE ===
+int Net_Lookup_AnyAddr(const char *Name, int AddrType, void *Addr)
+{
+	struct sLookupAnyInfo	cb_info = {
+		.expected_type = AddrType,
+		.dest_ptr = Addr,
+		.have_result = false,
+		};
+	return Net_Lookup_Addrs(Name, &cb_info, int_lookupany_callback);
+}
+int int_lookupany_callback(void *info_v, int AddrType, const void *Addr)
+{
+	struct sLookupAnyInfo	*info = info_v;
+	if( AddrType == info->expected_type && info->have_result == false )
+	{
+		memcpy(info->dest_ptr, Addr, Net_GetAddressSize(AddrType));
+		
+		info->have_result = true;
+		return 1;
+	}
+	return 0;
+}
+
+int Net_Lookup_Addrs(const char *Name, void *cb_info, tNet_LookupAddrs_Callback *callback)
+{
+	_SysDebug("Net_Lookup_Addrs(Name='%s')", Name);
+	// 1. Load (if not loaded) the DNS config from "/Acess/Conf/dns"
+	// - "* <ip> <ip>" for DNS server(s)
+	// - "127.0.0.1 localhost localhost.localdomain"
+	if( !gaDNSServers )
+	{
+		giNumDNSServers = 1;
+		gaDNSServers = malloc( 1 * sizeof(gaDNSServers[0]) );
+		gaDNSServers[0].AddrType = Net_ParseAddress("192.168.1.1", gaDNSServers[0].AddrData);
+	}
+	
+	// 2. Check the hosts list
+	for( int i = 0; i < giNumHostEntries; i ++ )
+	{
+		const struct sHostEntry* he = &gaHostEntries[i];
+		for( const char * const *namep = (const char**)he->Names; *namep; namep ++ )
+		{
+			if( strcasecmp(Name, *namep) == 0 )
+			{
+				if( callback(cb_info, he->AddrType, he->AddrData) != 0 )
+					return 0;
+			}
+		}
+	}
+	// 3. Contact DNS server specified in config
+	for( int i = 0; i < giNumDNSServers; i ++ )
+	{
+		const struct sDNSServer	*s = &gaDNSServers[i];
+		struct sDNSCallbackInfo	info = {
+			.cb_info = cb_info,
+			.callback = callback,
+			.desired_type = TYPE_A,
+			.desired_class = CLASS_IN,
+			.got_value = false,
+			};
+		if( ! DNS_Query(s->AddrType, s->AddrData, Name, info.desired_type, info.desired_class, int_DNS_callback, &info) )
+		{
+			if( info.got_value )
+			{
+				return 0;
+			}
+			else
+			{
+				// NXDomain, I guess
+				return 1;
+			}
+		}
+	}
+	return 1;
+}
+
+void int_DNS_callback(void *info_v, const char *name, enum eTypes type, enum eClass class, unsigned int ttl, size_t rdlength, const void *rdata)
+{
+	struct sDNSCallbackInfo	*info = info_v;
+	_SysDebug("int_DNS_callback(name='%s', type=%i, class=%i)", name, type, class);
+	
+	// Check type matches (if pattern was provided)
+	if( info->desired_type != QTYPE_STAR && type != info->desired_type )
+		return ;
+	if( info->desired_class != QCLASS_STAR && class != info->desired_class )
+		return ;
+	
+	switch( type )
+	{
+	case TYPE_A:
+		if( rdlength != 4 )
+			return ;
+		info->callback( info->cb_info, 4, rdata );
+		break;
+	//case TYPE_AAAA:
+	//	if( rdlength != 16 )
+	//		return ;
+	//	info->callback( info->cb_info, 6, rdata );
+	//	break;
+	default:
+		// Ignore anything not A/AAAA
+		break;
+	}
+	info->got_value = true;
+}
+
+int Net_Lookup_Name(int AddrType, const void *Addr, char *Dest[256])
+{
+	return 1;
+}
+
diff --git a/Usermode/Libraries/libnet.so_src/include/dns.h b/Usermode/Libraries/libnet.so_src/include/dns.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc7a58b4f220440e9c6d5bb355240f9a944a2327
--- /dev/null
+++ b/Usermode/Libraries/libnet.so_src/include/dns.h
@@ -0,0 +1,48 @@
+/*
+ * Acess2 Networking Toolkit
+ * By John Hodge (thePowersGang)
+ * 
+ * dns.h
+ * - DNS Protocol Interface
+ */
+#ifndef _DNS_H_
+#define _DNS_H_
+
+#include <stddef.h>
+
+enum eTypes
+{
+	TYPE_A = 1,
+	TYPE_NS = 2,
+	TYPE_CNAME = 5,
+	TYPE_SOA = 6,
+	TYPE_NULL = 10,
+	TYPE_PTR = 12,
+	TYPE_HINFO = 13,
+	TYPE_MX = 15,
+	TYPE_TXT = 16,
+	QTYPE_STAR = 255,
+};
+
+enum eClass
+{
+	CLASS_IN = 1,
+	CLASS_CH = 3,	// "Chaos"
+	QCLASS_STAR = 255,
+};
+
+/**
+ * \brief Handler for a DNS record obtained by DNS_Query
+ * \param info	Value passed as the last argument to DNS_Query
+ * \param name	NUL-terminated name associated with the returned record
+ * \param type	Record type (may not be equal to requested)
+ * \param class	Record class (may not be equal to requested)
+ * \param rdlength	Length of data pointed to by 'rdata'
+ * \param rdata	Record data
+ */
+typedef void	handle_record_t(void *info, const char *name, enum eTypes type, enum eClass class, unsigned int ttl, size_t rdlength, const void *rdata);
+
+int DNS_Query(int ServerAType, const void *ServerAddr, const char *name, enum eTypes type, enum eClass class, handle_record_t* handle_record, void *info);
+
+#endif
+
diff --git a/Usermode/Libraries/libnet.so_src/include_exp/net.h b/Usermode/Libraries/libnet.so_src/include_exp/net.h
index 5fe4ba1058688e83365e6dc5df026a0108ce471d..3d33da0337c7a700ed2b1d470bae068145309b48 100644
--- a/Usermode/Libraries/libnet.so_src/include_exp/net.h
+++ b/Usermode/Libraries/libnet.so_src/include_exp/net.h
@@ -6,6 +6,8 @@
 #ifndef __LIBNET_H_
 #define __LIBNET_H_
 
+#include <stddef.h>
+
 enum {
 	NET_ADDRTYPE_NULL = 0,
 	NET_ADDRTYPE_IPV4 = 4,
@@ -53,8 +55,43 @@ extern char	*Net_GetInterface(int AddrType, void *Addr);
  * Opens a file using /Devices/ip/routes/@<AddrType>:<Addr>/<SocketName>
  * 
  */
-extern int	Net_OpenSocket(int AddrType, void *Addr, const char *SocketName);
+extern int	Net_OpenSocket(int AddrType, const void *Addr, const char *SocketName);
+
+extern int	Net_OpenSocket_TCPC(int AddrType, const void *Addr, int Port);
+
+extern int	Net_OpenSocket_UDP(int AddrType, const void *Addr, int RAddr, int LAddr);
+extern int Net_UDP_SendTo  (int FD, int Port, int AddrType, const void *Addr, size_t Length, const void *Data);
+extern int Net_UDP_RecvFrom(int FD, int* Port, int* AddrType, void *Addr, size_t Length, void *Data);
 
-extern int	Net_OpenSocket_TCPC(int AddrType, void *Addr, int Port);
+
+/**
+ * \name Hostnames
+ * \brief Handling of hostname resolution
+ * \{
+ */
+
+/**
+ * \brief Returns an address for the specified hostname
+ * \note Picks randomly if multiple addresses are present
+ */
+extern int	Net_Lookup_AnyAddr(const char *Name, int AddrType, void *Addr);
+
+/**
+ * \brief Callback for Net_Lookup_Addrs, returns non-zero when lookup should terminate
+ */
+typedef int tNet_LookupAddrs_Callback(void *info, int AddrType, const void *Addr);
+
+/**
+ * \brief Enumerate addresses for a host, calling the provided function for each
+ */
+extern int	Net_Lookup_Addrs(const char *Name, void *info, tNet_LookupAddrs_Callback* callback);
+
+/**
+ */
+extern int	Net_Lookup_Name(int AddrType, const void *Addr, char *Dest[256]);
+
+/**
+ * \}
+ */
 
 #endif
diff --git a/Usermode/Libraries/libnet.so_src/socket.c b/Usermode/Libraries/libnet.so_src/socket.c
index 85f7e54f28944711b7f026a280625bf3ccd94db7..825dc6fe274a2011bd56f61d01748205799db0d5 100644
--- a/Usermode/Libraries/libnet.so_src/socket.c
+++ b/Usermode/Libraries/libnet.so_src/socket.c
@@ -8,17 +8,28 @@
 #include <net.h>
 #include <stdio.h>
 #include <stdint.h>
+#include <string.h>	// memcpy
 #include <acess/sys.h>
 
-int Net_OpenSocket(int AddrType, void *Addr, const char *Filename)
+enum {
+	UDP_IOCTL_GETSETLPORT = 4,
+	UDP_IOCTL_GETSETRPORT,
+	UDP_IOCTL_GETSETRMASK,
+	UDP_IOCTL_SETRADDR,
+	UDP_IOCTL_SENDTO,
+	UDP_IOCTL_RECVFROM,
+};
+
+int Net_OpenSocket(int AddrType, const void *Addr, const char *Filename)
 {
 	 int	addrLen = Net_GetAddressSize(AddrType);
-	 int	i;
-	uint8_t	*addrBuffer = Addr;
 	char	hexAddr[addrLen*2+1];
 	
-	for( i = 0; i < addrLen; i ++ )
-		sprintf(hexAddr+i*2, "%02x", addrBuffer[i]);
+	{
+		const uint8_t	*addrBuffer = Addr;
+		for( unsigned int i = 0; i < addrLen; i ++ )
+			sprintf(hexAddr+i*2, "%02x", addrBuffer[i]);
+	}
 	
 	if(Filename)
 	{
@@ -37,14 +48,82 @@ int Net_OpenSocket(int AddrType, void *Addr, const char *Filename)
 	}
 }
 
-int Net_OpenSocket_TCPC(int AddrType, void *Addr, int Port)
+int Net_OpenSocket_TCPC(int AddrType, const void *Addr, int Port)
 {
 	int fd = Net_OpenSocket(AddrType, Addr, "tcpc");
 	if( fd == -1 )	return -1;
 	
-	_SysIOCtl(fd, 5, &Port);	// Remote Port
-        _SysIOCtl(fd, 6, Addr);	// Remote address
-	_SysIOCtl(fd, 7, NULL);	// connect
+	if( _SysIOCtl(fd, 5, &Port) < 0 )	// Remote Port
+		goto err;
+	if( _SysIOCtl(fd, 6, (void*)Addr) < 0 )	// Remote address (kernel shouldn't modify)
+		goto err;
+	if( _SysIOCtl(fd, 7, NULL) < 0)	// connect
+		goto err;
+	return fd;
+err:
+	_SysClose(fd);
+	return -1;
+}
+
+int Net_OpenSocket_UDP(int AddrType, const void *Addr, int RPort, int LPort)
+{
+	int fd = Net_OpenSocket(AddrType, Addr, "udp");
+	if( fd == -1 )	return -1;
+	
+	if( _SysIOCtl(fd, UDP_IOCTL_GETSETLPORT, &LPort) < 0 )
+		goto err;
+	if( _SysIOCtl(fd, UDP_IOCTL_GETSETRPORT, &RPort) < 0 )
+		goto err;
+	int maskbits = Net_GetAddressSize(AddrType) * 8;
+	if( _SysIOCtl(fd, UDP_IOCTL_GETSETRMASK, &maskbits) < 0 )
+		goto err;
+	if( _SysIOCtl(fd, UDP_IOCTL_SETRADDR, (void*)Addr) < 0 )	// Remote address (kernel shouldn't modify)
+		goto err;
 	return fd;
+err:
+	_SysClose(fd);
+	return -1;
+}
+
+int Net_UDP_SendTo(int FD, int Port, int AddrType, const void *Addr, size_t Length, const void *Data)
+{
+	struct {
+		uint16_t port;
+		uint16_t addr_type;
+		char	addr[16];
+	} ep;
+	ep.port = Port;
+	ep.addr_type = AddrType;
+	memcpy(ep.addr, Addr, Net_GetAddressSize(AddrType));
+	struct {
+		const void *ep;
+		const void *buf;
+		uint16_t len;
+	} info = { .ep = &ep, .buf = Data, .len = Length };
+	
+	return _SysIOCtl(FD, UDP_IOCTL_SENDTO, &info);
+}
+
+int Net_UDP_RecvFrom(int FD, int* Port, int* AddrType, void *Addr, size_t Length, void *Data)
+{
+	struct {
+		uint16_t port;
+		uint16_t addr_type;
+		char	addr[16];
+	} ep;
+	struct {
+		void *ep;
+		void *buf;
+		uint16_t len;
+	} info = { .ep = &ep, .buf = Data, .len = Length };
+	
+	int rv = _SysIOCtl(FD, UDP_IOCTL_RECVFROM, &info);
+	if( rv > 0 )
+	{
+		if(Port)	*Port = ep.port;
+		if(AddrType)	*AddrType = ep.addr_type;
+		if(Addr)	memcpy(Addr, ep.addr, Net_GetAddressSize(ep.addr_type));
+	}
+	return rv;
 }
 
diff --git a/Usermode/Libraries/libposix.so_src/Makefile b/Usermode/Libraries/libposix.so_src/Makefile
index dcbf8f5392b0addcda4a1171b7bed2edf99d9b94..eb2fd2f2df5a77d7b01f8b2a46da31f7a8de80d3 100644
--- a/Usermode/Libraries/libposix.so_src/Makefile
+++ b/Usermode/Libraries/libposix.so_src/Makefile
@@ -6,12 +6,14 @@
 CPPFLAGS += 
 CFLAGS   += -Wextra
 ASFLAGS  +=
-LDFLAGS  += -soname libposix.so -Map map.txt -lc
+LDFLAGS  += -nostdlib
+PRELINK  += $(CRTI) $(CRTBEGINS) $(CRT0S)
+LIBS     += -lc $(CRTENDS) $(CRTN)
 
 OBJ  = main.o unistd.o dirent.o stat.o utmpx.o termios.o
 OBJ += pwd.o syslog.o sys_time.o sys_ioctl.o sys_resource.o
 OBJ += fcntl.o clocks.o sys_wait.o unistd_crypt.o
-OBJ += grp.o pty.o mktemp.o utime.o
+OBJ += grp.o pty.o mktemp.o utime.o getopt.o
 DEPFILES := $(OBJ:%.o=%.d)
 BIN = libposix.so
 
diff --git a/Usermode/Libraries/libposix.so_src/getopt.c b/Usermode/Libraries/libposix.so_src/getopt.c
new file mode 100644
index 0000000000000000000000000000000000000000..ab6186bba5def8d7827fafebcdaf242fd38cb7a5
--- /dev/null
+++ b/Usermode/Libraries/libposix.so_src/getopt.c
@@ -0,0 +1,21 @@
+/*
+ * Acess2 POSIX Emulation Layer
+ * - By John Hodge (thePowersGang)
+ *
+ * getopt.c
+ * - getopt() command line parsing code
+ */
+#include <getopt.h>
+
+// === GLOBALS ===
+char*	optarg;
+ int	opterr;
+ int	optind;
+ int	optopt;
+
+// === CODE ===
+int getopt(int argc, char * const argv[], const char *optstring)
+{
+	return -1;
+}
+
diff --git a/Usermode/Libraries/libposix.so_src/include_exp/endian.h b/Usermode/Libraries/libposix.so_src/include_exp/endian.h
new file mode 100644
index 0000000000000000000000000000000000000000..a70ec0d737852070bfae67d61d9e4e725ae85e27
--- /dev/null
+++ b/Usermode/Libraries/libposix.so_src/include_exp/endian.h
@@ -0,0 +1,11 @@
+/*
+ */
+#ifndef _LIBPOSIX_ENDIAN_H_
+#define _LIBPOSIX_ENDIAN_H_
+
+#define __LITTLE_ENDIAN	0
+#define __BIG_ENDIAN	0
+#define __BYTE_ORDER	__LITTLE_ENDIAN
+
+#endif
+
diff --git a/Usermode/Libraries/libposix.so_src/include_exp/getopt.h b/Usermode/Libraries/libposix.so_src/include_exp/getopt.h
new file mode 100644
index 0000000000000000000000000000000000000000..b6db119ef9445edb4922fba9dff62a7f2088188a
--- /dev/null
+++ b/Usermode/Libraries/libposix.so_src/include_exp/getopt.h
@@ -0,0 +1,27 @@
+/*
+ * Acess2 POSIX Emulation Layer
+ * - By John Hodge (thePowersGang)
+ *
+ * getopt.h
+ * - getopt() command line parsing code
+ */
+#ifndef _LIBPOSIX_GETOPT_H_
+#define _LIBPOSIX_GETOPT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char*	optarg;
+extern int	opterr;
+extern int	optind;
+extern int	optopt;
+
+extern int	getopt(int argc, char * const argv[], const char *optstring);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/Usermode/Libraries/libposix.so_src/include_exp/glob.h b/Usermode/Libraries/libposix.so_src/include_exp/glob.h
new file mode 100644
index 0000000000000000000000000000000000000000..ff96b8e600f755972074a0078681c9b9750a6270
--- /dev/null
+++ b/Usermode/Libraries/libposix.so_src/include_exp/glob.h
@@ -0,0 +1,13 @@
+/*
+ * Acess2 POSIX Emulation Library
+ * - By John Hodge (thePowersGang)
+ *
+ * glob.h
+ * - Globbing code
+ */
+#ifndef _LIBPOSIX__GLOB_H_
+#define _LIBPOSIX__GLOB_H_
+
+
+
+#endif
diff --git a/Usermode/Libraries/libposix.so_src/include_exp/regex.h b/Usermode/Libraries/libposix.so_src/include_exp/regex.h
new file mode 100644
index 0000000000000000000000000000000000000000..ebbe6b631e4b9d1a8a871f09958a6829b527e8dd
--- /dev/null
+++ b/Usermode/Libraries/libposix.so_src/include_exp/regex.h
@@ -0,0 +1,41 @@
+/*
+ * Acess2 POSIX Emulation Library
+ * - By John Hodge (thePowersGang)
+ *
+ * regex.h
+ * - POSIX regular expression support
+ */
+#ifndef _LIBPOSIX_REGEX_H_
+#define _LIBPOSIX_REGEX_H_
+
+typedef struct {
+	void *unused;
+} regex_t;
+
+typedef size_t	regoff_t;
+
+typedef struct {
+	regoff_t rm_so;
+	regoff_t rm_eo;
+} regmatch_t;
+
+extern int regcomp(regex_t *preg, const char *regex, int cflags);
+extern int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
+extern size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
+extern void regfree(regex_t *preg);
+
+enum {
+	REG_BADBR = 1,
+	REG_BADPAT,
+	REG_BADRPT,
+};
+
+#define REG_EXTENDED	0x1
+#define REG_ICASE	0x2
+#define REG_NOSUB	0x4
+#define REG_NEWLINE	0x8
+
+
+#endif
+
+
diff --git a/Usermode/Libraries/libposix.so_src/include_exp/strings.h b/Usermode/Libraries/libposix.so_src/include_exp/strings.h
new file mode 100644
index 0000000000000000000000000000000000000000..9c4114d988135744e02bdcf1d717dfe45f7e2fc8
--- /dev/null
+++ b/Usermode/Libraries/libposix.so_src/include_exp/strings.h
@@ -0,0 +1,14 @@
+/*
+ * Acess2 POSIX Emulation Library
+ * - By John Hodge (thePowersGang)
+ *
+ * strings.h
+ * - BSD's verison of string.h
+ */
+#ifndef _LIBPOSIX_STRINGS_H_
+#define _LIBPOSIX_STRINGS_H_
+
+
+
+#endif
+
diff --git a/Usermode/Libraries/libposix.so_src/include_exp/unistd.h b/Usermode/Libraries/libposix.so_src/include_exp/unistd.h
index 6519b3b77d978fbd73a2f4c6166437fa17e774d3..10c4779faa76baaa42eacc29745e75bfaa8aabad 100644
--- a/Usermode/Libraries/libposix.so_src/include_exp/unistd.h
+++ b/Usermode/Libraries/libposix.so_src/include_exp/unistd.h
@@ -71,6 +71,10 @@ extern int	chmod(const char *path, mode_t mode);
 
 extern int	unlink(const char *pathname);
 
+#define F_OK	00
+#define R_OK	04
+#define W_OK	02
+#define X_OK	01
 extern int	access(const char *pathname, int mode);
 
 extern pid_t	setsid(void);
@@ -93,6 +97,7 @@ extern unsigned int	alarm(unsigned int seconds);
 extern char	*crypt(const char *key, const char *salt);
 
 // - pty.c
+extern int	isatty(int fd);
 extern char	*ttyname(int fd);
 extern int	ttyname_r(int fd, char *buf, size_t buflen);
 
diff --git a/Usermode/Libraries/libposix.so_src/mktemp.c b/Usermode/Libraries/libposix.so_src/mktemp.c
index 863b9256b360577789e6d39dd2a8979b78923ed8..2cc5f2ad55f40dc4ed65c5b7657f985a8912cb3b 100644
--- a/Usermode/Libraries/libposix.so_src/mktemp.c
+++ b/Usermode/Libraries/libposix.so_src/mktemp.c
@@ -7,6 +7,7 @@
  */
 #include <unistd.h>	// mktemp
 #include <stdlib.h>	// mkstemp
+#include <stdio.h>
 #include <string.h>	// str*
 #include <errno.h>
 
@@ -25,7 +26,7 @@ int mkstemp(char *template)
 	
 	for( int i = 0; i < 1000000; i ++ )
 	{
-		sprintf(template+tpl_len-6, "%06d", i);
+		snprintf(template+tpl_len-6, 6+1, "%06d", i);
 		int fd = open(template, O_EXCL|O_CREAT, 0600);
 		if(fd == -1)	continue ;
 	
diff --git a/Usermode/Libraries/libposix.so_src/unistd.c b/Usermode/Libraries/libposix.so_src/unistd.c
index 3af7bcbc34f15cf7f54e38051a5f3112c3a83c4a..dca29630e7e2f5b5e7a60175e681cd77df76e6df 100644
--- a/Usermode/Libraries/libposix.so_src/unistd.c
+++ b/Usermode/Libraries/libposix.so_src/unistd.c
@@ -292,3 +292,21 @@ int ttyname_r(int fd, char *buf, size_t buflen)
 
 	return ENOTIMPL;
 }
+
+int isatty(int fd)
+{
+	if( fd < 0 ) {
+		errno = EBADF;
+		return 0;
+	}
+	
+	 int	type = _SysIOCtl(fd, DRV_IOCTL_TYPE, NULL);
+	if( type == -1 )
+		return 0;
+	if( type != DRV_TYPE_TERMINAL ) {
+		errno = ENOTTY;
+		// NOTE: Pre POSIX 2001, EINVAL was returned
+		return 0;
+	}
+	return 1;
+}
diff --git a/Usermode/Libraries/libpsocket.so_src/Makefile b/Usermode/Libraries/libpsocket.so_src/Makefile
index 0ad326e5b4f18d29dc85def41d3144cde31a85e0..65f7380bea07373d2d5a1541861e85d077081add 100644
--- a/Usermode/Libraries/libpsocket.so_src/Makefile
+++ b/Usermode/Libraries/libpsocket.so_src/Makefile
@@ -4,7 +4,8 @@ include ../Makefile.cfg
 
 CPPFLAGS +=
 CFLAGS   += -Wall
-LDFLAGS  += -lc -soname libpsocket.so -lnet
+LDFLAGS  += -lc -soname libpsocket.so
+LIBS     += -lnet
 
 OBJ = main.o getaddrinfo.o socket.o pton.o byteordering.o
 BIN = libpsocket.so
diff --git a/Usermode/Libraries/libpsocket.so_src/getaddrinfo.c b/Usermode/Libraries/libpsocket.so_src/getaddrinfo.c
index 5c21c06ea555621970653f23049a3e832a75a6c9..e1a0e424a4266a297d44e08e594600cc262bdd5d 100644
--- a/Usermode/Libraries/libpsocket.so_src/getaddrinfo.c
+++ b/Usermode/Libraries/libpsocket.so_src/getaddrinfo.c
@@ -13,6 +13,16 @@
 #include <stdlib.h>	// strtol
 #include <acess/sys.h>
 
+// === TYPES ===
+struct sLookupInfo {
+	struct addrinfo	**ret_p;
+};
+
+// === PROTOTYPES ===
+struct addrinfo *int_new_addrinfo(int af, const void *addrdata);
+int int_getaddinfo_lookupcb(void *info, int addr_type, const void *addr);
+
+// === GLOBALS ===
 static const struct {
 	const char *Name;
 	 int	SockType;
@@ -52,28 +62,20 @@ int getaddrinfo(const char *node, const char *service, const struct addrinfo *hi
 	{
 		// 1. Check if the node is an IP address
 		{
-			 int	type;
 			char	addrdata[16];
-			type = Net_ParseAddress(node, addrdata);
+			int type = Net_ParseAddress(node, addrdata);
 			switch(type)
 			{
-			case 0:
+			case NET_ADDRTYPE_NULL:
 				break;
-			case 4:	// IPv4
-				ret = malloc(sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
-				ret->ai_family = AF_INET;
-				ret->ai_socktype = 0;
-				ret->ai_protocol = 0;
-				ret->ai_addrlen = sizeof(struct in_addr);
-				ret->ai_addr = (void*)( ret + 1 );
-				ret->ai_canonname = 0;
-				ret->ai_next = 0;
-				((struct sockaddr_in*)ret->ai_addr)->sin_family = AF_INET;
-				((struct sockaddr_in*)ret->ai_addr)->sin_port = 0;
-				memcpy( &((struct sockaddr_in*)ret->ai_addr)->sin_addr, addrdata, 4 );
+			case NET_ADDRTYPE_IPV4:
+				ret = int_new_addrinfo(AF_INET, addrdata);
+				break;
+			case NET_ADDRTYPE_IPV6:
+				ret = int_new_addrinfo(AF_INET6, addrdata);
 				break;
 			default:
-				_SysDebug("getaddrinfo: Unknown address family %i", type);
+				_SysDebug("getaddrinfo: Unknown address type %i", type);
 				return 1;
 			}
 		}
@@ -82,15 +84,19 @@ int getaddrinfo(const char *node, const char *service, const struct addrinfo *hi
 		// - No luck with above, and hints->ai_flags doesn't have AI_NUMERICHOST set
 		if( !ret && !(hints->ai_flags & AI_NUMERICHOST) )
 		{
-			_SysDebug("getaddrinfo: TODO DNS Lookups");
-			// TODO: DNS Lookups
-			// ? /Acess/Conf/Nameservers
-			// ? /Acess/Conf/Hosts
-			//count = Net_LookupDNS(node, service, NULL);
-			//
+			// Just does a basic A record lookup
+			// TODO: Support SRV records
+			// TODO: Ensure that CNAMEs are handled correctly
+			struct sLookupInfo info = {
+				.ret_p = &ret,
+				};
+			if( Net_Lookup_Addrs(node, &info, int_getaddinfo_lookupcb) ) {
+				// Lookup failed, quick return
+				return EAI_NONAME;
+			}
 		}
 		
-		// 3. No Match, chuck sad
+		// 3. No Match, return sad
 		if( !ret )
 		{
 			return EAI_NONAME;
@@ -182,9 +188,81 @@ int getaddrinfo(const char *node, const char *service, const struct addrinfo *hi
 	return 0;
 }
 
+/**
+ * \brief Allocate a new zeroed addrinfo for the specified address
+ */
+struct addrinfo *int_new_addrinfo(int af, const void *addrdata)
+{
+	size_t	addrlen = 0;
+	switch(af)
+	{
+	case AF_INET:
+		addrlen = sizeof(struct sockaddr_in);
+		break;
+	case AF_INET6:
+		addrlen = sizeof(struct sockaddr_in6);
+		break;
+	default:
+		_SysDebug("int_new_addrinfo: ERROR - Unknown AF %i", af);
+		return NULL;
+	}
+	struct addrinfo* ret = malloc(sizeof(struct addrinfo) + addrlen);
+	ret->ai_family = af;
+	ret->ai_socktype = 0;
+	ret->ai_protocol = 0;
+	ret->ai_addrlen = addrlen;
+	ret->ai_addr = (void*)( ret + 1 );
+	ret->ai_canonname = 0;
+	ret->ai_next = 0;
+	switch(af)
+	{
+	case AF_INET:
+		((struct sockaddr_in*)ret->ai_addr)->sin_family = AF_INET;
+		((struct sockaddr_in*)ret->ai_addr)->sin_port = 0;
+		memcpy( &((struct sockaddr_in*)ret->ai_addr)->sin_addr, addrdata, 4 );
+		break;
+	case AF_INET6:
+		((struct sockaddr_in6*)ret->ai_addr)->sin6_family = AF_INET6;
+		((struct sockaddr_in6*)ret->ai_addr)->sin6_port = 0;
+		memcpy( &((struct sockaddr_in6*)ret->ai_addr)->sin6_addr, addrdata, 16 );
+		break;
+	default:
+		_SysDebug("int_new_addrinfo: BUGCHECK - Unhandled AF %i", af);
+		return NULL;
+	}
+	return ret;
+}
+
+// Callback for getaddrinfo's call to Net_Lookup_Addrs
+int int_getaddinfo_lookupcb(void *info_v, int addr_type, const void *addr)
+{
+	struct sLookupInfo *info = info_v;
+	struct addrinfo	*ent;
+	switch( addr_type )
+	{
+	case NET_ADDRTYPE_IPV4:
+		ent = int_new_addrinfo(AF_INET, addr);
+		break;
+	case NET_ADDRTYPE_IPV6:
+		ent = int_new_addrinfo(AF_INET6, addr);
+		break;
+	default:
+		// Huh... unknown address type, just ignore it
+		return 0;
+	}
+	ent->ai_next = *info->ret_p;
+	*info->ret_p = ent;
+	return 0;
+}
+
 void freeaddrinfo(struct addrinfo *res)
 {
-	
+	while( res )
+	{
+		struct addrinfo *next = res->ai_next;
+		free(res);
+		res = next;
+	}
 }
 
 int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags)
diff --git a/Usermode/Libraries/libpthread.so_src/Makefile b/Usermode/Libraries/libpthread.so_src/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..0870c0cfbefa24182ae15af3d268f7074c7ff110
--- /dev/null
+++ b/Usermode/Libraries/libpthread.so_src/Makefile
@@ -0,0 +1,12 @@
+# Acess 2 - POSIX Threads
+
+include ../Makefile.cfg
+
+CPPFLAGS +=
+CFLAGS   += -Wall
+LDFLAGS  += -lc -soname libpthread.so
+
+OBJ = main.o thread.o
+BIN = libpthread.so
+
+include ../Makefile.tpl
diff --git a/Usermode/Libraries/libpthread.so_src/include_exp/pthread.h b/Usermode/Libraries/libpthread.so_src/include_exp/pthread.h
new file mode 100644
index 0000000000000000000000000000000000000000..376fb0e2b69c0cb35b28756a7bbba9042e0ce82a
--- /dev/null
+++ b/Usermode/Libraries/libpthread.so_src/include_exp/pthread.h
@@ -0,0 +1,95 @@
+/*
+ * Acess2 libpthread
+ * - By John Hodge (thePowersGang)
+ *
+ * pthread.h
+ * - Core POSIX threads header
+ */
+#ifndef _LIBPTHREAT_PTHREAD_H_
+#define _LIBPTHREAT_PTHREAD_H_
+
+// XXX: Hack - libgcc doesn't seem to be auto-detecting the presence of this header
+#include "sched.h"
+
+//! \name pthread core
+//! \{
+typedef struct pthread_attr_s	pthread_attr_t;
+
+struct pthread_s
+{
+	void*	retval;
+	unsigned int	kernel_handle;
+};
+typedef struct pthread_s	pthread_t;
+
+extern int	pthread_create(pthread_t *threadptr, const pthread_attr_t * attrs, void* (*fcn)(void*), void* arg);
+extern int	pthread_detach(pthread_t thread);
+extern int	pthread_join(pthread_t thread, void **retvalptr);
+extern int	pthread_cancel(pthread_t thread);
+extern int	pthread_equal(pthread_t t1, pthread_t t2);
+extern pthread_t	pthread_self(void);
+extern void	pthread_exit(void* retval);
+//! }
+
+
+//! \name pthread_once
+//! \{
+struct pthread_once_s
+{
+	int	flag;
+};
+#define PTHREAD_ONCE_INIT	((struct pthread_once_s){.flag=0})
+typedef struct pthread_once_s	pthread_once_t;
+extern int pthread_once(pthread_once_t *once_contol, void (*init_routine)(void));
+//! \}
+
+//! \name pthread mutexes
+//! \{
+#define PTHREAD_MUTEX_NORMAL	0
+#define PTHREAD_MUTEX_RECURSIVE	1
+struct pthread_mutexattr_s
+{
+	int	type;
+};
+typedef struct pthread_mutexattr_s	pthread_mutexattr_t;
+extern int pthread_mutexattr_init(pthread_mutexattr_t *attrs);
+extern int pthread_mutexattr_settype(pthread_mutexattr_t *attrs, int type);
+extern int pthread_mutexattr_destroy(pthread_mutexattr_t *attrs);
+
+struct pthread_mutex_s
+{
+	void*	futex;
+};
+#define PTHREAD_MUTEX_INITIALIZER	((struct pthread_mutex_s){0})
+typedef struct pthread_mutex_s	pthread_mutex_t;
+extern int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t *attrs);
+extern int pthread_mutex_lock(pthread_mutex_t *lock);
+extern int pthread_mutex_trylock(pthread_mutex_t *lock);
+extern int pthread_mutex_unlock(pthread_mutex_t *lock);
+extern int pthread_mutex_destroy(pthread_mutex_t *lock);
+//! \}
+
+//! \name pthread TLS keys
+//! \{
+//typedef struct pthread_key_s	pthread_key_t;
+typedef unsigned int	pthread_key_t;
+extern int pthread_key_create(pthread_key_t *keyptr, void (*fcn)(void*));
+extern int pthread_key_delete(pthread_key_t key);
+extern int pthread_setspecific(pthread_key_t key, const void* data);
+extern void* pthread_getspecific(pthread_key_t key);
+//! \}
+
+//! \name pthread condvars
+//! \{
+typedef struct pthread_cond_s	pthread_cond_t;
+typedef struct pthread_condattr_s	pthread_condattr_t;
+extern int pthread_cond_init(pthread_cond_t *condptr, const pthread_condattr_t* attrs);
+extern int pthread_cond_wait(pthread_cond_t *condptr, pthread_mutex_t *mutex);
+extern int pthread_cond_timedwait(pthread_cond_t *condptr, pthread_mutex_t *mutex, const struct timespec *timeout);
+extern int pthread_cond_signal(pthread_cond_t *condptr);
+extern int pthread_cond_broadcast(pthread_cond_t *condptr);
+extern int pthread_cond_destroy(pthread_cond_t *condptr);
+//! \}
+
+#endif
+
diff --git a/Usermode/Libraries/libpthread.so_src/include_exp/sched.h b/Usermode/Libraries/libpthread.so_src/include_exp/sched.h
new file mode 100644
index 0000000000000000000000000000000000000000..8366ba22d4d9d68196a50f2e08d30bd299359bc3
--- /dev/null
+++ b/Usermode/Libraries/libpthread.so_src/include_exp/sched.h
@@ -0,0 +1,15 @@
+/*
+ * Acess2 libpthread
+ * - By John Hodge (thePowersGang)
+ *
+ * sched.h
+ * - Execution Scheduling (POSIX Realtime Extensions)
+ */
+#ifndef _LIBPTHREAT_SCHED_H_
+#define _LIBPTHREAT_SCHED_H_
+
+// *grumble* libgcc wants a yield
+extern int	sched_yield(void);
+
+#endif
+
diff --git a/Usermode/Libraries/libpthread.so_src/main.c b/Usermode/Libraries/libpthread.so_src/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Usermode/Libraries/libpthread.so_src/thread.c b/Usermode/Libraries/libpthread.so_src/thread.c
new file mode 100644
index 0000000000000000000000000000000000000000..4b5f51fee5388794c2da727186f4bc2e47bcfe58
--- /dev/null
+++ b/Usermode/Libraries/libpthread.so_src/thread.c
@@ -0,0 +1,44 @@
+/*
+ * Acess2 libpthread
+ * - By John Hodge (thePowersGang)
+ *
+ * thread.c
+ * - Thread management for pthreads
+ */
+#include <pthread.h>
+#include <assert.h>
+
+int pthread_create(pthread_t *threadptr, const pthread_attr_t * attrs, void* (*fcn)(void*), void* arg)
+{
+	assert(!"TODO: pthread_create");
+	return 0;
+}
+int pthread_detach(pthread_t thread)
+{
+	assert(!"TODO: pthread_detach");
+	return 0;
+}
+int pthread_join(pthread_t thread, void **retvalptr)
+{
+	assert(!"TODO: pthread_join");
+	return 0;
+}
+int pthread_cancel(pthread_t thread)
+{
+	assert(!"TODO: pthread_cancel");
+	return 0;
+}
+int pthread_equal(pthread_t t1, pthread_t t2)
+{
+	assert(!"TODO: pthread_equal");
+	return 0;
+}
+pthread_t pthread_self(void)
+{
+	assert(!"TODO: pthread_self");
+	return (pthread_t){0};
+}
+void pthread_exit(void* retval)
+{
+	assert(!"TODO: pthread_create");
+}
diff --git a/Usermode/Libraries/libresolv.so_src/Makefile b/Usermode/Libraries/libresolv.so_src/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..21eb942d6567d093d5a54d7d0739b44f0596e9d3
--- /dev/null
+++ b/Usermode/Libraries/libresolv.so_src/Makefile
@@ -0,0 +1,16 @@
+# Acess2 BSD "resolv" library
+# Makefile
+
+-include ../Makefile.cfg
+
+CPPFLAGS += 
+CFLAGS   += -Wextra
+ASFLAGS  +=
+LDFLAGS  += 
+LIBS     += -lnet -lpsocket
+
+OBJ  = resolv.o
+BIN = libresolv.so
+
+include ../Makefile.tpl
+
diff --git a/Usermode/Libraries/libresolv.so_src/include_exp/arpa/nameser.h b/Usermode/Libraries/libresolv.so_src/include_exp/arpa/nameser.h
new file mode 100644
index 0000000000000000000000000000000000000000..5f2532b5b3553c7aea06cd72d28d326f42f29dd0
--- /dev/null
+++ b/Usermode/Libraries/libresolv.so_src/include_exp/arpa/nameser.h
@@ -0,0 +1,16 @@
+/*
+ * Acess2 POSIX Sockets Emulation
+ * - By John Hodge (thePowersGang)
+ *
+ * arpa/nameser.h
+ * - BSD name resolution (TODO: move to libresolv)
+ *
+ * NOTE: I have no fucking idea who/what defines this header, it's wanted by glib
+ */
+#ifndef _LIBPSOCKET__ARPA_NAMESER_H_
+#define _LIBPSOCKET__ARPA_NAMESER_H_
+
+#define C_IN	0x1
+
+#endif
+
diff --git a/Usermode/Libraries/libresolv.so_src/include_exp/resolv.h b/Usermode/Libraries/libresolv.so_src/include_exp/resolv.h
new file mode 100644
index 0000000000000000000000000000000000000000..66d965731b0aa085df97727b43f7fb897cb5d71b
--- /dev/null
+++ b/Usermode/Libraries/libresolv.so_src/include_exp/resolv.h
@@ -0,0 +1,20 @@
+/*
+ */
+#ifndef _LIBRESOLV__RESOLV_H_
+#define _LIBRESOLV__RESOLV_H_
+
+extern int res_init(void);
+
+extern int res_query(const char *dname, int class, int type, unsigned char *answer, int anslen);
+
+extern int res_search(const char *dname, int class, int type, unsigned char *answer, int anslen);
+
+extern int res_querydomain(const char *name, const char *domain, int class, int type, unsigned char *answer, int anslen);
+
+extern int res_mkquery(int op, const char *dname, int class, int type, char *data, int datalen, struct rrec *newrr, char *buf, int buflen);
+
+extern int res_send(const char *msg, int msglen, char *answer, int anslen);
+
+
+#endif
+
diff --git a/Usermode/Libraries/libresolv.so_src/resolv.c b/Usermode/Libraries/libresolv.so_src/resolv.c
new file mode 100644
index 0000000000000000000000000000000000000000..e8d121bea636a13f34c4bbdc38eef4abff18c137
--- /dev/null
+++ b/Usermode/Libraries/libresolv.so_src/resolv.c
@@ -0,0 +1,33 @@
+/*
+ */
+
+int res_init(void)
+{
+	return 1;
+}
+
+int res_query(const char *dname, int class, int type, unsigned char *answer, int anslen)
+{
+	return 1;
+}
+
+int res_search(const char *dname, int class, int type, unsigned char *answer, int anslen)
+{
+	return 1;
+}
+
+int res_querydomain(const char *name, const char *domain, int class, int type, unsigned char *answer, int anslen)
+{
+	return 1;
+}
+
+int res_mkquery(int op, const char *dname, int class, int type, char *data, int datalen, struct rrec *newrr, char *buf, int buflen)
+{
+	return 1;
+}
+
+int res_send(const char *msg, int msglen, char *answer, int anslen)
+{
+	return 1;
+}
+
diff --git a/Usermode/Libraries/libunicode.so_src/include_exp/unicode.h b/Usermode/Libraries/libunicode.so_src/include_exp/unicode.h
index ef0eb56e04153bdfe6d9637da9158baf0a7c8163..6c7e3e4a4edaff22dac3c4d1f70582c9239383da 100644
--- a/Usermode/Libraries/libunicode.so_src/include_exp/unicode.h
+++ b/Usermode/Libraries/libunicode.so_src/include_exp/unicode.h
@@ -10,6 +10,10 @@
 
 #include <stdint.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /**
  * \breif Read a single codepoint from  a UTF-8 stream
  * \return Number of bytes read
@@ -27,5 +31,102 @@ extern int	WriteUTF8(char *buf, uint32_t Val);
 
 static inline int	Unicode_IsPrinting(uint32_t Codepoint) { return 1; }
 
+#ifdef __cplusplus
+}	// extern "C"
+#endif
+
+#ifdef __cplusplus
+#include <iterator>
+
+namespace libunicode {
+
+class utf8iterator:
+	public ::std::iterator< ::std::forward_iterator_tag, uint32_t >
+{
+	const char*	m_curpos;
+	uint32_t	m_curval;
+public:
+	utf8iterator():
+		m_curpos(0)
+	{
+	}
+	utf8iterator(const char *pos):
+		m_curpos(pos)
+	{
+		(*this)++;
+	}
+	utf8iterator(const utf8iterator& other) {
+		m_curpos = other.m_curpos;
+		m_curval = other.m_curval;
+	}
+	utf8iterator& operator=(const utf8iterator& other) {
+		m_curpos = other.m_curpos;
+		m_curval = other.m_curval;
+		return *this;
+	}
+	
+	bool operator== (const utf8iterator& other) {
+		return other.m_curpos == m_curpos;
+	}
+	bool operator!= (const utf8iterator& other) {
+		return other.m_curpos != m_curpos;
+	}
+	utf8iterator& operator++() {
+		m_curpos += ::ReadUTF8(m_curpos, &m_curval);
+		return *this;
+	}
+	utf8iterator operator++(int) {
+		utf8iterator	rv(*this);
+		m_curpos += ::ReadUTF8(m_curpos, &m_curval);
+		return rv;
+	}
+	uint32_t operator*() const {
+		return m_curval;
+	}
+	uint32_t operator->() const {
+		return m_curval;
+	}
+};
+
+class utf8string
+{
+	const char* m_data;
+	size_t	m_len;
+	
+	size_t _strlen(const char*s) {
+		size_t	l = 0;
+		while(*s)	l ++;
+		return l;
+	}
+public:
+	utf8string(const char* c_str):
+		m_data(c_str),
+		m_len(_strlen(c_str))
+	{
+	}
+	utf8string(const char* c_str, size_t len):
+		m_data(c_str),
+		m_len(len)
+	{
+	}
+	utf8string(const ::std::string& str):
+		m_data(str.c_str()),
+		m_len(str.size())
+	{
+	}
+	
+	utf8iterator begin() const {
+		return utf8iterator(m_data);
+	}
+	utf8iterator end() const {
+		return utf8iterator(m_data + m_len);
+	}
+};
+
+
+};
+
+#endif
+
 #endif
 
diff --git a/Usermode/common_settings.mk b/Usermode/common_settings.mk
index 519b0fd353097e770f5e469b1844d02b454d7b26..2f6fa8f4d867beb51f4376bcddb06c4f9ec84f39 100644
--- a/Usermode/common_settings.mk
+++ b/Usermode/common_settings.mk
@@ -5,3 +5,14 @@ CPPFLAGS += -I$(ACESSUSERDIR)/include/ -DARCHDIR_is_$(ARCHDIR)
 CPPFLAGS += -I $(ACESSDIR)/Externals/Output/$(ARCHDIR)/include
 CFLAGS += -std=gnu99 -g
 LDFLAGS += -L $(ACESSDIR)/Externals/Output/$(ARCHDIR)/lib
+LDFLAGS += -L $(OUTPUTDIR)Libs
+
+CRTI := $(OUTPUTDIR)Libs/crti.o
+CRTBEGIN := $(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o 2>/dev/null)
+CRTBEGINS := $(shell $(CC) $(CFLAGS) -print-file-name=crtbeginS.o 2>/dev/null)
+CRT0 := $(OUTPUTDIR)Libs/crt0.o
+CRT0S := $(OUTPUTDIR)Libs/crt0S.o
+CRTEND := $(shell $(CC) $(CFLAGS) -print-file-name=crtend.o 2>/dev/null)
+CRTENDS := $(shell $(CC) $(CFLAGS) -print-file-name=crtendS.o 2>/dev/null)
+CRTN := $(OUTPUTDIR)Libs/crtn.o
+LIBGCC_PATH = $(shell $(CC) -print-libgcc-file-name 2>/dev/null)