diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index eb4fef3815..3fa5f51f14 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -22,6 +22,8 @@ set(SRCS    core.cpp
             hle/config_mem.cpp
             hle/coprocessor.cpp
             hle/syscall.cpp
+            hle/kernel/kernel.cpp
+            hle/kernel/thread.cpp
             hle/service/apt.cpp
             hle/service/gsp.cpp
             hle/service/hid.cpp
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj
index b56661e48b..59fc6f4fcf 100644
--- a/src/core/core.vcxproj
+++ b/src/core/core.vcxproj
@@ -156,6 +156,8 @@
     <ClCompile Include="hle\config_mem.cpp" />
     <ClCompile Include="hle\coprocessor.cpp" />
     <ClCompile Include="hle\hle.cpp" />
+    <ClCompile Include="hle\kernel\kernel.cpp" />
+    <ClCompile Include="hle\kernel\thread.cpp" />
     <ClCompile Include="hle\service\apt.cpp" />
     <ClCompile Include="hle\service\gsp.cpp" />
     <ClCompile Include="hle\service\hid.cpp" />
@@ -198,6 +200,8 @@
     <ClInclude Include="hle\coprocessor.h" />
     <ClInclude Include="hle\function_wrappers.h" />
     <ClInclude Include="hle\hle.h" />
+    <ClInclude Include="hle\kernel\kernel.h" />
+    <ClInclude Include="hle\kernel\thread.h" />
     <ClInclude Include="hle\service\apt.h" />
     <ClInclude Include="hle\service\gsp.h" />
     <ClInclude Include="hle\service\hid.h" />
diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters
index 5c947ec23f..ff988c1161 100644
--- a/src/core/core.vcxproj.filters
+++ b/src/core/core.vcxproj.filters
@@ -28,6 +28,9 @@
     <Filter Include="hle\service">
       <UniqueIdentifier>{812c5189-ca49-4704-b842-3ffad09092d3}</UniqueIdentifier>
     </Filter>
+    <Filter Include="hle\kernel">
+      <UniqueIdentifier>{f2b132eb-caff-4b04-aaae-88d24393a711}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="arm\disassembler\arm_disasm.cpp">
@@ -114,6 +117,12 @@
     <ClCompile Include="hle\config_mem.cpp">
       <Filter>hle</Filter>
     </ClCompile>
+    <ClCompile Include="hle\kernel\thread.cpp">
+      <Filter>hle\kernel</Filter>
+    </ClCompile>
+    <ClCompile Include="hle\kernel\kernel.cpp">
+      <Filter>hle\kernel</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="arm\disassembler\arm_disasm.h">
@@ -223,6 +232,12 @@
     <ClInclude Include="hle\config_mem.h">
       <Filter>hle</Filter>
     </ClInclude>
+    <ClInclude Include="hle\kernel\thread.h">
+      <Filter>hle\kernel</Filter>
+    </ClInclude>
+    <ClInclude Include="hle\kernel\kernel.h">
+      <Filter>hle\kernel</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Text Include="CMakeLists.txt" />
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
new file mode 100644
index 0000000000..fc494fe30f
--- /dev/null
+++ b/src/core/hle/kernel/kernel.cpp
@@ -0,0 +1,142 @@
+// Copyright 2014 Citra Emulator Project / PPSSPP Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.  
+
+#pragma once
+
+#include <string.h>
+
+#include "common/common.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/thread.h"
+
+KernelObjectPool g_kernel_objects;
+
+void __KernelInit() {
+    __KernelThreadingInit();
+}
+
+void __KernelShutdown() {
+    __KernelThreadingShutdown();
+}
+
+KernelObjectPool::KernelObjectPool() {
+    memset(occupied, 0, sizeof(bool) * MAX_COUNT);
+    next_id = INITIAL_NEXT_ID;
+}
+
+UID KernelObjectPool::Create(KernelObject *obj, int range_bottom, int range_top) {
+    if (range_top > MAX_COUNT) {
+        range_top = MAX_COUNT;
+    }
+    if (next_id >= range_bottom && next_id < range_top) {
+        range_bottom = next_id++;
+    }
+    for (int i = range_bottom; i < range_top; i++) {
+        if (!occupied[i]) {
+            occupied[i] = true;
+            pool[i] = obj;
+            pool[i]->uid = i + HANDLE_OFFSET;
+            return i + HANDLE_OFFSET;
+        }
+    }
+    ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use.");
+    return 0;
+}
+
+bool KernelObjectPool::IsValid(UID handle)
+{
+    int index = handle - HANDLE_OFFSET;
+    if (index < 0)
+        return false;
+    if (index >= MAX_COUNT)
+        return false;
+
+    return occupied[index];
+}
+
+void KernelObjectPool::Clear()
+{
+    for (int i = 0; i < MAX_COUNT; i++)
+    {
+        //brutally clear everything, no validation
+        if (occupied[i])
+            delete pool[i];
+        occupied[i] = false;
+    }
+    memset(pool, 0, sizeof(KernelObject*)*MAX_COUNT);
+    next_id = INITIAL_NEXT_ID;
+}
+
+KernelObject *&KernelObjectPool::operator [](UID handle)
+{
+    _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
+    return pool[handle - HANDLE_OFFSET];
+}
+
+void KernelObjectPool::List() {
+    for (int i = 0; i < MAX_COUNT; i++) {
+        if (occupied[i]) {
+            if (pool[i]) {
+                INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName(), 
+                    pool[i]->GetName());
+            }
+        }
+    }
+}
+
+int KernelObjectPool::GetCount()
+{
+    int count = 0;
+    for (int i = 0; i < MAX_COUNT; i++)
+    {
+        if (occupied[i])
+            count++;
+    }
+    return count;
+}
+
+KernelObject *KernelObjectPool::CreateByIDType(int type) {
+    // Used for save states.  This is ugly, but what other way is there?
+    switch (type) {
+    //case SCE_KERNEL_TMID_Alarm:
+    //    return __KernelAlarmObject();
+    //case SCE_KERNEL_TMID_EventFlag:
+    //    return __KernelEventFlagObject();
+    //case SCE_KERNEL_TMID_Mbox:
+    //    return __KernelMbxObject();
+    //case SCE_KERNEL_TMID_Fpl:
+    //    return __KernelMemoryFPLObject();
+    //case SCE_KERNEL_TMID_Vpl:
+    //    return __KernelMemoryVPLObject();
+    //case PPSSPP_KERNEL_TMID_PMB:
+    //    return __KernelMemoryPMBObject();
+    //case PPSSPP_KERNEL_TMID_Module:
+    //    return __KernelModuleObject();
+    //case SCE_KERNEL_TMID_Mpipe:
+    //    return __KernelMsgPipeObject();
+    //case SCE_KERNEL_TMID_Mutex:
+    //    return __KernelMutexObject();
+    //case SCE_KERNEL_TMID_LwMutex:
+    //    return __KernelLwMutexObject();
+    //case SCE_KERNEL_TMID_Semaphore:
+    //    return __KernelSemaphoreObject();
+    //case SCE_KERNEL_TMID_Callback:
+    //    return __KernelCallbackObject();
+    //case SCE_KERNEL_TMID_Thread:
+    //    return __KernelThreadObject();
+    //case SCE_KERNEL_TMID_VTimer:
+    //    return __KernelVTimerObject();
+    //case SCE_KERNEL_TMID_Tlspl:
+    //    return __KernelTlsplObject();
+    //case PPSSPP_KERNEL_TMID_File:
+    //    return __KernelFileNodeObject();
+    //case PPSSPP_KERNEL_TMID_DirList:
+    //    return __KernelDirListingObject();
+
+    default:
+        ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type);
+        return NULL;
+    }
+}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
new file mode 100644
index 0000000000..2381ca7f74
--- /dev/null
+++ b/src/core/hle/kernel/kernel.h
@@ -0,0 +1,121 @@
+// Copyright 2014 Citra Emulator Project / PPSSPP Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.  
+
+#pragma once
+
+#include "common/common_types.h"
+
+typedef u32 UID;
+
+class KernelObjectPool;
+
+class KernelObject {
+    friend class KernelObjectPool;
+    u32 uid;
+public:
+    virtual ~KernelObject() {}
+    UID GetUID() const { return uid; }
+    virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; }
+    virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; }
+    virtual int GetIDType() const = 0;
+    //virtual void GetQuickInfo(char *ptr, int size);
+};
+
+class KernelObjectPool {
+public:
+    KernelObjectPool();
+    ~KernelObjectPool() {}
+
+    // Allocates a UID within the range and inserts the object into the map.
+    UID Create(KernelObject *obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF);
+
+    static KernelObject *CreateByIDType(int type);
+
+    template <class T>
+    u32 Destroy(UID handle) {
+        u32 error;
+        if (Get<T>(handle, error)) {
+            occupied[handle - handleOffset] = false;
+            delete pool[handle - handleOffset];
+        }
+        return error;
+    };
+
+    bool IsValid(UID handle);
+
+    template <class T>
+    T* Get(UID handle, u32& outError) {
+        if (handle < handleOffset || handle >= handleOffset + maxCount || !occupied[handle - handleOffset]) {
+            // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP
+            if (handle != 0 && (u32)handle != 0x80020001) {
+                WARN_LOG(SCEKERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+            }
+            outError = T::GetMissingErrorCode();
+            return 0;
+        } else {
+            // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally,
+            // it just acted as a static case and everything worked. This means that we will never
+            // see the Wrong type object error below, but we'll just have to live with that danger.
+            T* t = static_cast<T*>(pool[handle - handleOffset]);
+            if (t == 0 || t->GetIDType() != T::GetStaticIDType()) {
+                WARN_LOG(SCEKERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
+                outError = T::GetMissingErrorCode();
+                return 0;
+            }
+            outError = SCE_KERNEL_ERROR_OK;
+            return t;
+        }
+    }
+
+    // ONLY use this when you know the handle is valid.
+    template <class T>
+    T *GetFast(UID handle) {
+        const UID realHandle = handle - handleOffset;
+        _dbg_assert_(SCEKERNEL, realHandle >= 0 && realHandle < maxCount && occupied[realHandle]);
+        return static_cast<T *>(pool[realHandle]);
+    }
+
+    template <class T, typename ArgT>
+    void Iterate(bool func(T *, ArgT), ArgT arg) {
+        int type = T::GetStaticIDType();
+        for (int i = 0; i < maxCount; i++)
+        {
+            if (!occupied[i])
+                continue;
+            T *t = static_cast<T *>(pool[i]);
+            if (t->GetIDType() == type) {
+                if (!func(t, arg))
+                    break;
+            }
+        }
+    }
+
+    bool GetIDType(UID handle, int *type) const {
+        if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || 
+            !occupied[handle - HANDLE_OFFSET]) {
+            ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+            return false;
+        }
+        KernelObject *t = pool[handle - HANDLE_OFFSET];
+        *type = t->GetIDType();
+        return true;
+    }
+
+    KernelObject *&operator [](UID handle);
+    void List();
+    void Clear();
+    int GetCount();
+
+private:
+    enum {
+        MAX_COUNT       = 0x1000,
+        HANDLE_OFFSET   = 0x100,
+        INITIAL_NEXT_ID = 0x10,
+    };
+    KernelObject *pool[MAX_COUNT];
+    bool occupied[MAX_COUNT];
+    int next_id;
+};
+
+extern KernelObjectPool g_kernel_objects;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
new file mode 100644
index 0000000000..0ed35de83f
--- /dev/null
+++ b/src/core/hle/kernel/thread.cpp
@@ -0,0 +1,228 @@
+// Copyright 2014 Citra Emulator Project / PPSSPP Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.  
+
+#include <stdio.h>
+
+#include <list>
+#include <vector>
+#include <map>
+#include <string>
+
+#include "common/common.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/thread.h"
+
+// Real CTR struct, don't change the fields.
+struct NativeThread {
+    //u32         Pointer to vtable
+    //u32         Reference count
+    //KProcess*   Process the thread belongs to (virtual address)
+    //u32         Thread id
+    //u32*        ptr = *(KThread+0x8C) - 0xB0
+    //u32*        End-address of the page for this thread allocated in the 0xFF4XX000 region. Thus, 
+    //      if the beginning of this mapped page is 0xFF401000, this ptr would be 0xFF402000.
+    //KThread*    Previous ? (virtual address)
+    //KThread*    Next ? (virtual address)
+};
+
+struct ThreadWaitInfo {
+    u32 wait_value;
+    u32 timeout_ptr;
+};
+
+class Thread : public KernelObject {
+public:
+    /*const char *GetName() { return nt.name; }*/
+    const char *GetTypeName() { return "Thread"; }
+    //void GetQuickInfo(char *ptr, int size)
+    //{
+    //    sprintf(ptr, "pc= %08x sp= %08x %s %s %s %s %s %s (wt=%i wid=%i wv= %08x )",
+    //        context.pc, context.r[13], // 13 is stack pointer
+    //        (nt.status & THREADSTATUS_RUNNING) ? "RUN" : "",
+    //        (nt.status & THREADSTATUS_READY) ? "READY" : "",
+    //        (nt.status & THREADSTATUS_WAIT) ? "WAIT" : "",
+    //        (nt.status & THREADSTATUS_SUSPEND) ? "SUSPEND" : "",
+    //        (nt.status & THREADSTATUS_DORMANT) ? "DORMANT" : "",
+    //        (nt.status & THREADSTATUS_DEAD) ? "DEAD" : "",
+    //        nt.waitType,
+    //        nt.waitID,
+    //        waitInfo.waitValue);
+    //}
+
+    //static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; }
+    //static int GetStaticIDType() { return SCE_KERNEL_TMID_Thread; }
+    //int GetIDType() const { return SCE_KERNEL_TMID_Thread; }
+
+    //bool AllocateStack(u32 &stack_size) {
+    //    FreeStack();
+
+    //    bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0;
+    //    if (nt.attr & PSP_THREAD_ATTR_KERNEL)
+    //    {
+    //        // Allocate stacks for kernel threads (idle) in kernel RAM
+    //        currentStack.start = kernelMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str());
+    //    }
+    //    else
+    //    {
+    //        currentStack.start = userMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str());
+    //    }
+    //    if (currentStack.start == (u32)-1)
+    //    {
+    //        currentStack.start = 0;
+    //        nt.initialStack = 0;
+    //        ERROR_LOG(KERNEL, "Failed to allocate stack for thread");
+    //        return false;
+    //    }
+
+    //    nt.initialStack = currentStack.start;
+    //    nt.stack_size = stack_size;
+    //    return true;
+    //}
+
+    //bool FillStack() {
+    //    // Fill the stack.
+    //    if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) {
+    //        Memory::Memset(currentStack.start, 0xFF, nt.stack_size);
+    //    }
+    //    context.r[MIPS_REG_SP] = currentStack.start + nt.stack_size;
+    //    currentStack.end = context.r[MIPS_REG_SP];
+    //    // The k0 section is 256 bytes at the top of the stack.
+    //    context.r[MIPS_REG_SP] -= 256;
+    //    context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP];
+    //    u32 k0 = context.r[MIPS_REG_K0];
+    //    Memory::Memset(k0, 0, 0x100);
+    //    Memory::Write_U32(GetUID(), k0 + 0xc0);
+    //    Memory::Write_U32(nt.initialStack, k0 + 0xc8);
+    //    Memory::Write_U32(0xffffffff, k0 + 0xf8);
+    //    Memory::Write_U32(0xffffffff, k0 + 0xfc);
+    //    // After k0 comes the arguments, which is done by sceKernelStartThread().
+
+    //    Memory::Write_U32(GetUID(), nt.initialStack);
+    //    return true;
+    //}
+
+    //void FreeStack() {
+    //    if (currentStack.start != 0) {
+    //        DEBUG_LOG(KERNEL, "Freeing thread stack %s", nt.name);
+
+    //        if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) {
+    //            Memory::Memset(nt.initialStack, 0, nt.stack_size);
+    //        }
+
+    //        if (nt.attr & PSP_THREAD_ATTR_KERNEL) {
+    //            kernelMemory.Free(currentStack.start);
+    //        }
+    //        else {
+    //            userMemory.Free(currentStack.start);
+    //        }
+    //        currentStack.start = 0;
+    //    }
+    //}
+
+    //bool PushExtendedStack(u32 size) {
+    //    u32 stack = userMemory.Alloc(size, true, (std::string("extended/") + nt.name).c_str());
+    //    if (stack == (u32)-1)
+    //        return false;
+
+    //    pushed_stacks.push_back(currentStack);
+    //    currentStack.start = stack;
+    //    currentStack.end = stack + size;
+    //    nt.initialStack = currentStack.start;
+    //    nt.stack_size = currentStack.end - currentStack.start;
+
+    //    // We still drop the threadID at the bottom and fill it, but there's no k0.
+    //    Memory::Memset(currentStack.start, 0xFF, nt.stack_size);
+    //    Memory::Write_U32(GetUID(), nt.initialStack);
+    //    return true;
+    //}
+
+    //bool PopExtendedStack() {
+    //    if (pushed_stacks.size() == 0) {
+    //        return false;
+    //    }
+    //    userMemory.Free(currentStack.start);
+    //    currentStack = pushed_stacks.back();
+    //    pushed_stacks.pop_back();
+    //    nt.initialStack = currentStack.start;
+    //    nt.stack_size = currentStack.end - currentStack.start;
+    //    return true;
+    //}
+
+    Thread() {
+        currentStack.start = 0;
+    }
+
+    // Can't use a destructor since savestates will call that too.
+    //void Cleanup() {
+    //    // Callbacks are automatically deleted when their owning thread is deleted.
+    //    for (auto it = callbacks.begin(), end = callbacks.end(); it != end; ++it)
+    //        kernelObjects.Destroy<Callback>(*it);
+
+    //    if (pushed_stacks.size() != 0)
+    //    {
+    //        WARN_LOG(KERNEL, "Thread ended within an extended stack");
+    //        for (size_t i = 0; i < pushed_stacks.size(); ++i)
+    //            userMemory.Free(pushed_stacks[i].start);
+    //    }
+    //    FreeStack();
+    //}
+
+    void setReturnValue(u32 retval);
+    void setReturnValue(u64 retval);
+    void resumeFromWait();
+    //bool isWaitingFor(WaitType type, int id);
+    //int getWaitID(WaitType type);
+    ThreadWaitInfo getWaitInfo();
+
+    // Utils
+    //inline bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; }
+    //inline bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; }
+    //inline bool isReady() const { return (nt.status & THREADSTATUS_READY) != 0; }
+    //inline bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; }
+    //inline bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; }
+
+    NativeThread nt;
+
+    ThreadWaitInfo waitInfo;
+    UID moduleId;
+
+    bool isProcessingCallbacks;
+    u32 currentMipscallId;
+    UID currentCallbackId;
+
+    ThreadContext context;
+
+    std::vector<UID> callbacks;
+
+    std::list<u32> pending_calls;
+
+    struct StackInfo {
+        u32 start;
+        u32 end;
+    };
+    // This is a stack of... stacks, since sceKernelExtendThreadStack() can recurse.
+    // These are stacks that aren't "active" right now, but will pop off once the func returns.
+    std::vector<StackInfo> pushed_stacks;
+
+    StackInfo currentStack;
+
+    // For thread end.
+    std::vector<UID> waiting_threads;
+    // Key is the callback id it was for, or if no callback, the thread id.
+    std::map<UID, u64> paused_waits;
+};
+
+void __KernelThreadingInit() {
+}
+
+void __KernelThreadingShutdown() {
+}
+
+//const char *__KernelGetThreadName(UID threadID);
+//
+//void __KernelSaveContext(ThreadContext *ctx);
+//void __KernelLoadContext(ThreadContext *ctx);
+
+//void __KernelSwitchContext(Thread *target, const char *reason);
\ No newline at end of file
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
new file mode 100644
index 0000000000..c3cdca31f4
--- /dev/null
+++ b/src/core/hle/kernel/thread.h
@@ -0,0 +1,36 @@
+// Copyright 2014 Citra Emulator Project / PPSSPP Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.  
+
+#pragma once
+
+#include "common/common_types.h"
+
+enum ThreadStatus {
+    THREADSTATUS_RUNNING = 1,
+    THREADSTATUS_READY = 2,
+    THREADSTATUS_WAIT = 4,
+    THREADSTATUS_SUSPEND = 8,
+    THREADSTATUS_DORMANT = 16,
+    THREADSTATUS_DEAD = 32,
+
+    THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND
+};
+
+struct ThreadContext {
+    void reset();
+
+    u32 reg[16];
+    u32 cpsr;
+    u32 pc;
+};
+
+void __KernelThreadingInit();
+void __KernelThreadingShutdown();
+
+//const char *__KernelGetThreadName(SceUID threadID);
+//
+//void __KernelSaveContext(ThreadContext *ctx);
+//void __KernelLoadContext(ThreadContext *ctx);
+
+//void __KernelSwitchContext(Thread *target, const char *reason);
\ No newline at end of file