summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/arm_interface.h6
-rw-r--r--src/core/arm/interpreter/arm_interpreter.cpp8
-rw-r--r--src/core/arm/interpreter/arm_interpreter.h6
-rw-r--r--src/core/core.vcxproj4
-rw-r--r--src/core/core.vcxproj.filters15
-rw-r--r--src/core/hle/function_wrappers.h7
-rw-r--r--src/core/hle/hle.cpp11
-rw-r--r--src/core/hle/hle.h4
-rw-r--r--src/core/hle/kernel/kernel.cpp154
-rw-r--r--src/core/hle/kernel/kernel.h134
-rw-r--r--src/core/hle/kernel/thread.cpp369
-rw-r--r--src/core/hle/kernel/thread.h35
-rw-r--r--src/core/hle/service/apt.cpp6
-rw-r--r--src/core/hle/service/srv.cpp8
-rw-r--r--src/core/hle/syscall.cpp63
-rw-r--r--src/core/hle/syscall.h25
-rw-r--r--src/core/loader.cpp20
-rw-r--r--src/core/mem_map.cpp3
-rw-r--r--src/core/mem_map.h7
-rw-r--r--src/core/mem_map_funcs.cpp14
21 files changed, 881 insertions, 20 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index fdf68c386..b0597db38 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -34,6 +34,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/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 9fdc7ba3c..5c382ebbd 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -64,6 +64,12 @@ public:
virtual u32 GetCPSR() const = 0;
/**
+ * Set the current CPSR register
+ * @param cpsr Value to set CPSR to
+ */
+ virtual void SetCPSR(u32 cpsr) = 0;
+
+ /**
* Returns the number of clock ticks since the last rese
* @return Returns number of clock ticks
*/
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp
index 23d96d292..c21ff0464 100644
--- a/src/core/arm/interpreter/arm_interpreter.cpp
+++ b/src/core/arm/interpreter/arm_interpreter.cpp
@@ -78,6 +78,14 @@ u32 ARM_Interpreter::GetCPSR() const {
}
/**
+ * Set the current CPSR register
+ * @param cpsr Value to set CPSR to
+ */
+void ARM_Interpreter::SetCPSR(u32 cpsr) {
+ m_state->Cpsr = cpsr;
+}
+
+/**
* Returns the number of clock ticks since the last reset
* @return Returns number of clock ticks
*/
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h
index 509025080..474ba3e45 100644
--- a/src/core/arm/interpreter/arm_interpreter.h
+++ b/src/core/arm/interpreter/arm_interpreter.h
@@ -49,6 +49,12 @@ public:
u32 GetCPSR() const;
/**
+ * Set the current CPSR register
+ * @param cpsr Value to set CPSR to
+ */
+ void SetCPSR(u32 cpsr);
+
+ /**
* Returns the number of clock ticks since the last reset
* @return Returns number of clock ticks
*/
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj
index 41af5801d..f077154ee 100644
--- a/src/core/core.vcxproj
+++ b/src/core/core.vcxproj
@@ -168,6 +168,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" />
@@ -214,6 +216,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 edf34ce2f..6aedeb54b 100644
--- a/src/core/core.vcxproj.filters
+++ b/src/core/core.vcxproj.filters
@@ -31,6 +31,9 @@
<Filter Include="arm\interpreter\mmu">
<UniqueIdentifier>{13ef9860-2ba0-47e9-a93d-b4052adab269}</UniqueIdentifier>
</Filter>
+ <Filter Include="hle\kernel">
+ <UniqueIdentifier>{8089d94b-5faa-43dc-854b-ffd2fa2e7fe3}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="arm\disassembler\arm_disasm.cpp">
@@ -153,6 +156,12 @@
<ClCompile Include="arm\interpreter\mmu\maverick.cpp">
<Filter>arm\interpreter\mmu</Filter>
</ClCompile>
+ <ClCompile Include="hle\kernel\kernel.cpp">
+ <Filter>hle\kernel</Filter>
+ </ClCompile>
+ <ClCompile Include="hle\kernel\thread.cpp">
+ <Filter>hle\kernel</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="arm\disassembler\arm_disasm.h">
@@ -274,6 +283,12 @@
<ClInclude Include="arm\interpreter\mmu\sa_mmu.h">
<Filter>arm\interpreter\mmu</Filter>
</ClInclude>
+ <ClInclude Include="hle\kernel\kernel.h">
+ <Filter>hle\kernel</Filter>
+ </ClInclude>
+ <ClInclude Include="hle\kernel\thread.h">
+ <Filter>hle\kernel</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 18b01b14b..61790e5a3 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -734,13 +734,18 @@ template<int func(void*, u32)> void WrapI_VU(){
RETURN(retval);
}
+template<int func(void*, void*, u32)> void WrapI_VVU(){
+ u32 retval = func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2));
+ RETURN(retval);
+}
+
template<int func(void*, u32, void*, int)> void WrapI_VUVI(){
u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)), PARAM(3));
RETURN(retval);
}
template<int func(void*, u32, u32, u32, u32, u32)> void WrapI_VUUUUU(){
- u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
+ u32 retval = func(NULL, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
RETURN(retval);
}
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index be151665b..452384571 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -37,6 +37,17 @@ void CallSyscall(u32 opcode) {
}
}
+void EatCycles(u32 cycles) {
+ // TODO: ImplementMe
+}
+
+void ReSchedule(const char *reason) {
+#ifdef _DEBUG
+ _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "ReSchedule: Invalid or too long reason.");
+#endif
+ // TODO: ImplementMe
+}
+
void RegisterModule(std::string name, int num_functions, const FunctionDef* func_table) {
ModuleDef module = {name, num_functions, func_table};
g_module_db.push_back(module);
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index 42f37e29c..452546e1f 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -36,6 +36,10 @@ void RegisterModule(std::string name, int num_functions, const FunctionDef *func
void CallSyscall(u32 opcode);
+void EatCycles(u32 cycles);
+
+void ReSchedule(const char *reason);
+
void Init();
void Shutdown();
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
new file mode 100644
index 000000000..f7145ddd8
--- /dev/null
+++ b/src/core/hle/kernel/kernel.cpp
@@ -0,0 +1,154 @@
+// 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/core.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;
+}
+
+Handle 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]->handle = 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(Handle 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 [](Handle 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;
+ }
+}
+
+bool __KernelLoadExec(u32 entry_point) {
+ __KernelInit();
+
+ Core::g_app_core->SetPC(entry_point);
+
+ // 0x30 is the typical main thread priority I've seen used so far
+ Handle thread_id = __KernelSetupMainThread(0x30);
+
+ return true;
+}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
new file mode 100644
index 000000000..2608eecc9
--- /dev/null
+++ b/src/core/hle/kernel/kernel.h
@@ -0,0 +1,134 @@
+// 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 s32 Handle;
+
+enum KernelIDType {
+ KERNEL_ID_TYPE_THREAD = 1,
+ KERNEL_ID_TYPE_SEMAPHORE = 2,
+ KERNEL_ID_TYPE_MUTEX = 3,
+ KERNEL_ID_TYPE_EVENT = 4,
+};
+
+enum {
+ KERNEL_MAX_NAME_LENGTH = 0x100,
+ KERNEL_DEFAULT_STACK_SIZE = 0x4000,
+};
+
+class KernelObjectPool;
+
+class KernelObject {
+ friend class KernelObjectPool;
+ u32 handle;
+public:
+ virtual ~KernelObject() {}
+ Handle GetHandle() const { return handle; }
+ virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; }
+ virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; }
+ virtual KernelIDType GetIDType() const = 0;
+};
+
+class KernelObjectPool {
+public:
+ KernelObjectPool();
+ ~KernelObjectPool() {}
+
+ // Allocates a handle within the range and inserts the object into the map.
+ Handle Create(KernelObject *obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF);
+
+ static KernelObject *CreateByIDType(int type);
+
+ template <class T>
+ u32 Destroy(Handle handle) {
+ u32 error;
+ if (Get<T>(handle, error)) {
+ occupied[handle - HANDLE_OFFSET] = false;
+ delete pool[handle - HANDLE_OFFSET];
+ }
+ return error;
+ };
+
+ bool IsValid(Handle handle);
+
+ template <class T>
+ T* Get(Handle handle, u32& outError) {
+ if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
+ // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP
+ if (handle != 0 && (u32)handle != 0x80020001) {
+ WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+ }
+ outError = 0;//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 - HANDLE_OFFSET]);
+ if (t == 0 || t->GetIDType() != T::GetStaticIDType()) {
+ WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
+ outError = 0;//T::GetMissingErrorCode();
+ return 0;
+ }
+ outError = 0;//SCE_KERNEL_ERROR_OK;
+ return t;
+ }
+ }
+
+ // ONLY use this when you know the handle is valid.
+ template <class T>
+ T *GetFast(Handle handle) {
+ const Handle realHandle = handle - HANDLE_OFFSET;
+ _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && 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 < MAX_COUNT; i++)
+ {
+ if (!occupied[i])
+ continue;
+ T *t = static_cast<T *>(pool[i]);
+ if (t->GetIDType() == type) {
+ if (!func(t, arg))
+ break;
+ }
+ }
+ }
+
+ bool GetIDType(Handle 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 [](Handle 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;
+
+bool __KernelLoadExec(u32 entry_point);
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
new file mode 100644
index 000000000..b967b3c62
--- /dev/null
+++ b/src/core/hle/kernel/thread.cpp
@@ -0,0 +1,369 @@
+// 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 "common/thread_queue_list.h"
+
+#include "core/core.h"
+#include "core/mem_map.h"
+#include "core/hle/hle.h"
+#include "core/hle/syscall.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/thread.h"
+
+// Enums
+
+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
+};
+
+enum WaitType {
+ WAITTYPE_NONE,
+ WAITTYPE_SLEEP,
+ WAITTYPE_SEMA,
+ WAITTYPE_EVENTFLAG,
+ WAITTYPE_THREADEND,
+ WAITTYPE_VBLANK,
+ WAITTYPE_MUTEX,
+ WAITTYPE_SYNCH,
+};
+
+typedef s32 Handle;
+
+class Thread : public KernelObject {
+public:
+
+ const char *GetName() { return name; }
+ const char *GetTypeName() { return "Thread"; }
+
+ static KernelIDType GetStaticIDType() { return KERNEL_ID_TYPE_THREAD; }
+ KernelIDType GetIDType() const { return KERNEL_ID_TYPE_THREAD; }
+
+ inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
+ inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
+ inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; }
+ inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
+ inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
+
+ ThreadContext context;
+
+ u32 status;
+ u32 entry_point;
+ u32 stack_top;
+ u32 stack_size;
+
+ s32 initial_priority;
+ s32 current_priority;
+
+ s32 processor_id;
+
+ WaitType wait_type;
+
+ char name[KERNEL_MAX_NAME_LENGTH+1];
+};
+
+// Lists all thread ids that aren't deleted/etc.
+std::vector<Handle> g_thread_queue;
+
+// Lists only ready thread ids.
+Common::ThreadQueueList<Handle> g_thread_ready_queue;
+
+Handle g_current_thread_handle;
+
+Thread* g_current_thread;
+
+
+inline Thread *__GetCurrentThread() {
+ return g_current_thread;
+}
+
+inline void __SetCurrentThread(Thread *t) {
+ g_current_thread = t;
+ g_current_thread_handle = t->GetHandle();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// Saves the current CPU context
+void __KernelSaveContext(ThreadContext &ctx) {
+ ctx.cpu_registers[0] = Core::g_app_core->GetReg(0);
+ ctx.cpu_registers[1] = Core::g_app_core->GetReg(1);
+ ctx.cpu_registers[2] = Core::g_app_core->GetReg(2);
+ ctx.cpu_registers[3] = Core::g_app_core->GetReg(3);
+ ctx.cpu_registers[4] = Core::g_app_core->GetReg(4);
+ ctx.cpu_registers[5] = Core::g_app_core->GetReg(5);
+ ctx.cpu_registers[6] = Core::g_app_core->GetReg(6);
+ ctx.cpu_registers[7] = Core::g_app_core->GetReg(7);
+ ctx.cpu_registers[8] = Core::g_app_core->GetReg(8);
+ ctx.cpu_registers[9] = Core::g_app_core->GetReg(9);
+ ctx.cpu_registers[10] = Core::g_app_core->GetReg(10);
+ ctx.cpu_registers[11] = Core::g_app_core->GetReg(11);
+ ctx.cpu_registers[12] = Core::g_app_core->GetReg(12);
+ ctx.sp = Core::g_app_core->GetReg(13);
+ ctx.lr = Core::g_app_core->GetReg(14);
+ ctx.pc = Core::g_app_core->GetPC();
+ ctx.cpsr = Core::g_app_core->GetCPSR();
+}
+
+/// Loads a CPU context
+void __KernelLoadContext(const ThreadContext &ctx) {
+ Core::g_app_core->SetReg(0, ctx.cpu_registers[0]);
+ Core::g_app_core->SetReg(1, ctx.cpu_registers[1]);
+ Core::g_app_core->SetReg(2, ctx.cpu_registers[2]);
+ Core::g_app_core->SetReg(3, ctx.cpu_registers[3]);
+ Core::g_app_core->SetReg(4, ctx.cpu_registers[4]);
+ Core::g_app_core->SetReg(5, ctx.cpu_registers[5]);
+ Core::g_app_core->SetReg(6, ctx.cpu_registers[6]);
+ Core::g_app_core->SetReg(7, ctx.cpu_registers[7]);
+ Core::g_app_core->SetReg(8, ctx.cpu_registers[8]);
+ Core::g_app_core->SetReg(9, ctx.cpu_registers[9]);
+ Core::g_app_core->SetReg(10, ctx.cpu_registers[10]);
+ Core::g_app_core->SetReg(11, ctx.cpu_registers[11]);
+ Core::g_app_core->SetReg(12, ctx.cpu_registers[12]);
+ Core::g_app_core->SetReg(13, ctx.sp);
+ Core::g_app_core->SetReg(14, ctx.lr);
+ //Core::g_app_core->SetReg(15, ctx.pc);
+
+ Core::g_app_core->SetPC(ctx.pc);
+ Core::g_app_core->SetCPSR(ctx.cpsr);
+}
+
+/// Resets a thread
+void __KernelResetThread(Thread *t, s32 lowest_priority) {
+ memset(&t->context, 0, sizeof(ThreadContext));
+
+ t->context.pc = t->entry_point;
+ t->context.sp = t->stack_top;
+
+ if (t->current_priority < lowest_priority) {
+ t->current_priority = t->initial_priority;
+ }
+
+ t->wait_type = WAITTYPE_NONE;
+}
+
+/// Change a thread to "ready" state
+void __KernelChangeReadyState(Thread *t, bool ready) {
+ Handle handle = t->GetHandle();
+ if (t->IsReady()) {
+ if (!ready) {
+ g_thread_ready_queue.remove(t->current_priority, handle);
+ }
+ } else if (ready) {
+ if (t->IsRunning()) {
+ g_thread_ready_queue.push_front(t->current_priority, handle);
+ } else {
+ g_thread_ready_queue.push_back(t->current_priority, handle);
+ }
+ t->status = THREADSTATUS_READY;
+ }
+}
+
+/// Changes a threads state
+void __KernelChangeThreadState(Thread *t, ThreadStatus new_status) {
+ if (!t || t->status == new_status) {
+ return;
+ }
+ __KernelChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0);
+ t->status = new_status;
+
+ if (new_status == THREADSTATUS_WAIT) {
+ if (t->wait_type == WAITTYPE_NONE) {
+ printf("ERROR: Waittype none not allowed here\n");
+ }
+ }
+}
+
+/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields)
+void __KernelCallThread(Thread *t) {
+ // Stop waiting
+ if (t->wait_type != WAITTYPE_NONE) {
+ t->wait_type = WAITTYPE_NONE;
+ }
+ __KernelChangeThreadState(t, THREADSTATUS_READY);
+}
+
+/// Creates a new thread
+Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority,
+ s32 processor_id, u32 stack_top, int stack_size) {
+
+ Thread *t = new Thread;
+
+ handle = g_kernel_objects.Create(t);
+
+ g_thread_queue.push_back(handle);
+ g_thread_ready_queue.prepare(priority);
+
+ t->status = THREADSTATUS_DORMANT;
+ t->entry_point = entry_point;
+ t->stack_top = stack_top;
+ t->stack_size = stack_size;
+ t->initial_priority = t->current_priority = priority;
+ t->processor_id = processor_id;
+ t->wait_type = WAITTYPE_NONE;
+
+ strncpy(t->name, name, KERNEL_MAX_NAME_LENGTH);
+ t->name[KERNEL_MAX_NAME_LENGTH] = '\0';
+
+ return t;
+}
+
+/// Creates a new thread - wrapper for external user
+Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, s32 processor_id,
+ u32 stack_top, int stack_size) {
+ if (name == NULL) {
+ ERROR_LOG(KERNEL, "__KernelCreateThread(): NULL name");
+ return -1;
+ }
+ if ((u32)stack_size < 0x200) {
+ ERROR_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid stack_size=0x%08X", name,
+ stack_size);
+ return -1;
+ }
+ if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
+ s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
+ WARN_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid priority=0x%08X, clamping to %08X",
+ name, priority, new_priority);
+ // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
+ // validity of this
+ priority = new_priority;
+ }
+ if (!Memory::GetPointer(entry_point)) {
+ ERROR_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid entry %08x", name, entry_point);
+ return -1;
+ }
+ Handle handle;
+ Thread *t = __KernelCreateThread(handle, name, entry_point, priority, processor_id, stack_top,
+ stack_size);
+
+ HLE::EatCycles(32000);
+
+ // This won't schedule to the new thread, but it may to one woken from eating cycles.
+ // Technically, this should not eat all at once, and reschedule in the middle, but that's hard.
+ HLE::ReSchedule("thread created");
+
+ __KernelCallThread(t);
+
+ return handle;
+}
+
+/// Switches CPU context to that of the specified thread
+void __KernelSwitchContext(Thread* t, const char *reason) {
+ Thread *cur = __GetCurrentThread();
+
+ // Save context for current thread
+ if (cur) {
+ __KernelSaveContext(cur->context);
+
+ if (cur->IsRunning()) {
+ __KernelChangeReadyState(cur, true);
+ }
+ }
+ // Load context of new thread
+ if (t) {
+ __SetCurrentThread(t);
+ __KernelChangeReadyState(t, false);
+ t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY;
+ t->wait_type = WAITTYPE_NONE;
+ __KernelLoadContext(t->context);
+ } else {
+ __SetCurrentThread(NULL);
+ }
+}
+
+/// Gets the next thread that is ready to be run by priority
+Thread *__KernelNextThread() {
+ Handle next;
+ Thread *cur = __GetCurrentThread();
+
+ if (cur && cur->IsRunning()) {
+ next = g_thread_ready_queue.pop_first_better(cur->current_priority);
+ } else {
+ next = g_thread_ready_queue.pop_first();
+ }
+ if (next < 0) {
+ return NULL;
+ }
+ return g_kernel_objects.GetFast<Thread>(next);
+}
+
+/// Sets up the primary application thread
+Handle __KernelSetupMainThread(s32 priority, int stack_size) {
+ Handle handle;
+
+ // Initialize new "main" thread
+ Thread *t = __KernelCreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
+ THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
+
+ __KernelResetThread(t, 0);
+
+ // If running another thread already, set it to "ready" state
+ Thread *cur = __GetCurrentThread();
+ if (cur && cur->IsRunning()) {
+ __KernelChangeReadyState(cur, true);
+ }
+
+ // Run new "main" thread
+ __SetCurrentThread(t);
+ t->status = THREADSTATUS_RUNNING;
+ __KernelLoadContext(t->context);
+
+ return handle;
+}
+
+/// Resumes a thread from waiting by marking it as "ready"
+void __KernelResumeThreadFromWait(Handle handle) {
+ u32 error;
+ Thread *t = g_kernel_objects.Get<Thread>(handle, error);
+ if (t) {
+ t->status &= ~THREADSTATUS_WAIT;
+ if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
+ __KernelChangeReadyState(t, true);
+ }
+ }
+}
+
+/// Puts a thread in the wait state for the given type/reason
+void __KernelWaitCurThread(WaitType wait_type, const char *reason) {
+ Thread *t = __GetCurrentThread();
+ t->wait_type = wait_type;
+ __KernelChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND)));
+}
+
+/// Reschedules to the next available thread (call after current thread is suspended)
+void __KernelReschedule(const char *reason) {
+ Thread *next = __KernelNextThread();
+ if (next > 0) {
+ __KernelSwitchContext(next, reason);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/// Wait thread - on WaitSynchronization
+void __KernelWaitThread_Synchronization() {
+ // TODO(bunnei): Just a placeholder function for now... FixMe
+ __KernelWaitCurThread(WAITTYPE_SYNCH, "waitSynchronization called");
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void __KernelThreadingInit() {
+}
+
+void __KernelThreadingShutdown() {
+}
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
new file mode 100644
index 000000000..8138be26f
--- /dev/null
+++ b/src/core/hle/kernel/thread.h
@@ -0,0 +1,35 @@
+// Copyright 2014 Citra Emulator Project / PPSSPP Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/kernel/kernel.h"
+
+class Thread;
+
+enum ThreadPriority {
+ THREADPRIO_HIGHEST = 0,
+ THREADPRIO_DEFAULT = 16,
+ THREADPRIO_LOWEST = 31,
+};
+
+enum ThreadProcessorId {
+ THREADPROCESSORID_0 = 0xFFFFFFFE,
+ THREADPROCESSORID_1 = 0xFFFFFFFD,
+ THREADPROCESSORID_ALL = 0xFFFFFFFC,
+};
+
+/// Creates a new thread - wrapper for external user
+Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority,
+ s32 processor_id, u32 stack_top, int stack_size=KERNEL_DEFAULT_STACK_SIZE);
+
+/// Sets up the primary application thread
+Handle __KernelSetupMainThread(s32 priority, int stack_size=KERNEL_DEFAULT_STACK_SIZE);
+
+void __KernelThreadingInit();
+void __KernelThreadingShutdown();
+
+/// Wait thread - on WaitSynchronization
+void __KernelWaitThread_Synchronization();
diff --git a/src/core/hle/service/apt.cpp b/src/core/hle/service/apt.cpp
index 709ac5493..ddb975607 100644
--- a/src/core/hle/service/apt.cpp
+++ b/src/core/hle/service/apt.cpp
@@ -19,7 +19,11 @@ void Initialize(Service::Interface* self) {
void GetLockHandle(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
- cmd_buff[5] = 0x00000000; // TODO: This should be an actual mutex handle
+ u32 flags = cmd_buff[1];
+
+ // TODO: This should be an actual mutex handle. Games will check that this is not non-zero
+ // (NULL), and fail if such. A faked non-zero value will at least enable further booting.
+ cmd_buff[5] = 0xDEADBEEF;
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index 071741444..303a943d4 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -16,6 +16,12 @@ void Initialize(Service::Interface* self) {
NOTICE_LOG(OSHLE, "SRV::Sync - Initialize");
}
+void GetProcSemaphore(Service::Interface* self) {
+ // Get process semaphore?
+ u32* cmd_buff = Service::GetCommandBuffer();
+ cmd_buff[3] = 0xDEADBEEF; // Return something... 0 == NULL, raises an exception
+}
+
void GetServiceHandle(Service::Interface* self) {
Syscall::Result res = 0;
u32* cmd_buff = Service::GetCommandBuffer();
@@ -39,7 +45,7 @@ void GetServiceHandle(Service::Interface* self) {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010002, Initialize, "Initialize"},
- {0x00020000, NULL, "GetProcSemaphore"},
+ {0x00020000, GetProcSemaphore, "GetProcSemaphore"},
{0x00030100, NULL, "RegisterService"},
{0x000400C0, NULL, "UnregisterService"},
{0x00050100, GetServiceHandle, "GetServiceHandle"},
diff --git a/src/core/hle/syscall.cpp b/src/core/hle/syscall.cpp
index d47df6038..0765bce7a 100644
--- a/src/core/hle/syscall.cpp
+++ b/src/core/hle/syscall.cpp
@@ -9,6 +9,9 @@
#include "core/hle/function_wrappers.h"
#include "core/hle/syscall.h"
#include "core/hle/service/service.h"
+#include "core/hle/kernel/thread.h"
+
+#include "common/symbols.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace Syscall
@@ -26,7 +29,8 @@ enum MapMemoryPermission {
};
/// Map application or GSP heap memory
-Result ControlMemory(u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
+Result ControlMemory(void* _outaddr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
+ u32* outaddr = (u32*)_outaddr;
u32 virtual_address = 0x00000000;
DEBUG_LOG(SVC, "ControlMemory called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
@@ -48,7 +52,9 @@ Result ControlMemory(u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissi
default:
ERROR_LOG(SVC, "ControlMemory unknown operation=0x%08X", operation);
}
-
+ if (NULL != outaddr) {
+ *outaddr = virtual_address;
+ }
Core::g_app_core->SetReg(1, virtual_address);
return 0;
@@ -134,16 +140,57 @@ Result GetResourceLimitCurrentValues(void* _values, Handle resource_limit, void*
return 0;
}
+Result CreateThread(void* thread, u32 thread_priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
+ std::string thread_name;
+ if (Symbols::HasSymbol(entry_point)) {
+ TSymbol symbol = Symbols::GetSymbol(entry_point);
+ thread_name = symbol.name;
+ } else {
+ char buff[100];
+ sprintf(buff, "%s", "unk-%08X", entry_point);
+ thread_name = buff;
+ }
+ DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateThread called entrypoint=0x%08X (%s), arg=0x%08X, "
+ "stacktop=0x%08X, threadpriority=0x%08X, processorid=0x%08X", entry_point,
+ thread_name.c_str(), arg, stack_top, thread_priority, processor_id);
+
+ return 0;
+}
+
+Result CreateMutex(void* _mutex, u32 initial_locked) {
+ Handle* mutex = (Handle*)_mutex;
+ DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateMutex called initial_locked=%s",
+ initial_locked ? "true" : "false");
+ return 0;
+}
+
+Result ReleaseMutex(Handle handle) {
+ DEBUG_LOG(SVC, "(UNIMPLEMENTED) ReleaseMutex called handle=0x%08X", handle);
+ return 0;
+}
+
+Result GetThreadId(void* thread_id, u32 thread) {
+ DEBUG_LOG(SVC, "(UNIMPLEMENTED) GetThreadId called thread=0x%08X", thread);
+ return 0;
+}
+
+Result QueryMemory(void *_info, void *_out, u32 addr) {
+ MemoryInfo* info = (MemoryInfo*) _info;
+ PageInfo* out = (PageInfo*) _out;
+ DEBUG_LOG(SVC, "(UNIMPLEMENTED) QueryMemory called addr=0x%08X", addr);
+ return 0;
+}
+
const HLE::FunctionDef Syscall_Table[] = {
{0x00, NULL, "Unknown"},
- {0x01, WrapI_UUUUU<ControlMemory>, "ControlMemory"},
- {0x02, NULL, "QueryMemory"},
+ {0x01, WrapI_VUUUUU<ControlMemory>, "ControlMemory"},
+ {0x02, WrapI_VVU<QueryMemory>, "QueryMemory"},
{0x03, NULL, "ExitProcess"},
{0x04, NULL, "GetProcessAffinityMask"},
{0x05, NULL, "SetProcessAffinityMask"},
{0x06, NULL, "GetProcessIdealProcessor"},
{0x07, NULL, "SetProcessIdealProcessor"},
- {0x08, NULL, "CreateThread"},
+ {0x08, WrapI_VUUUUU<CreateThread>, "CreateThread"},
{0x09, NULL, "ExitThread"},
{0x0A, NULL, "SleepThread"},
{0x0B, NULL, "GetThreadPriority"},
@@ -154,8 +201,8 @@ const HLE::FunctionDef Syscall_Table[] = {
{0x10, NULL, "SetThreadIdealProcessor"},
{0x11, NULL, "GetCurrentProcessorNumber"},
{0x12, NULL, "Run"},
- {0x13, NULL, "CreateMutex"},
- {0x14, NULL, "ReleaseMutex"},
+ {0x13, WrapI_VU<CreateMutex>, "CreateMutex"},
+ {0x14, WrapI_U<ReleaseMutex>, "ReleaseMutex"},
{0x15, NULL, "CreateSemaphore"},
{0x16, NULL, "ReleaseSemaphore"},
{0x17, NULL, "CreateEvent"},
@@ -190,7 +237,7 @@ const HLE::FunctionDef Syscall_Table[] = {
{0x34, NULL, "OpenThread"},
{0x35, NULL, "GetProcessId"},
{0x36, NULL, "GetProcessIdOfThread"},
- {0x37, NULL, "GetThreadId"},
+ {0x37, WrapI_VU<GetThreadId>, "GetThreadId"},
{0x38, WrapI_VU<GetResourceLimit>, "GetResourceLimit"},
{0x39, NULL, "GetResourceLimitLimitValues"},
{0x3A, WrapI_VUVI<GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues"},
diff --git a/src/core/hle/syscall.h b/src/core/hle/syscall.h
index 7a94e0136..17f190266 100644
--- a/src/core/hle/syscall.h
+++ b/src/core/hle/syscall.h
@@ -7,6 +7,31 @@
#include "common/common_types.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
+// SVC structures
+
+struct MemoryInfo {
+ u32 base_address;
+ u32 size;
+ u32 permission;
+ u32 state;
+};
+
+struct PageInfo {
+ u32 flags;
+};
+
+struct ThreadContext {
+ u32 cpu_registers[13];
+ u32 sp;
+ u32 lr;
+ u32 pc;
+ u32 cpsr;
+ u32 fpu_registers[32];
+ u32 fpscr;
+ u32 fpexc;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace Syscall
namespace Syscall {
diff --git a/src/core/loader.cpp b/src/core/loader.cpp
index 8756588ae..444b75feb 100644
--- a/src/core/loader.cpp
+++ b/src/core/loader.cpp
@@ -10,7 +10,7 @@
#include "core/core.h"
#include "core/file_sys/directory_file_system.h"
#include "core/elf/elf_reader.h"
-
+#include "core/hle/kernel/kernel.h"
#include "core/mem_map.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -56,7 +56,7 @@ bool Load_ELF(std::string &filename) {
elf_reader = new ElfReader(buffer);
elf_reader->LoadInto(0x00100000);
- Core::g_app_core->SetPC(elf_reader->GetEntryPoint());
+ __KernelLoadExec(elf_reader->GetEntryPoint());
delete[] buffer;
delete elf_reader;
@@ -89,11 +89,11 @@ bool Load_DAT(std::string &filename) {
* but for the sake of making it easier... we'll temporarily/hackishly
* allow it. No sense in making a proper reader for this.
*/
- u32 entrypoint = 0x00100000; // write to same entrypoint as elf
+ u32 entry_point = 0x00100000; // write to same entrypoint as elf
u32 payload_offset = 0xA150;
const u8 *src = &buffer[payload_offset];
- u8 *dst = Memory::GetPointer(entrypoint);
+ u8 *dst = Memory::GetPointer(entry_point);
u32 srcSize = size - payload_offset; //just load everything...
u32 *s = (u32*)src;
u32 *d = (u32*)dst;
@@ -102,7 +102,8 @@ bool Load_DAT(std::string &filename) {
*d++ = (*s++);
}
- Core::g_app_core->SetPC(entrypoint);
+ __KernelLoadExec(entry_point);
+
delete[] buffer;
}
@@ -131,10 +132,10 @@ bool Load_BIN(std::string &filename) {
f.ReadBytes(buffer, size);
- u32 entrypoint = 0x00100000; // Hardcoded, read from exheader
+ u32 entry_point = 0x00100000; // Hardcoded, read from exheader
const u8 *src = buffer;
- u8 *dst = Memory::GetPointer(entrypoint);
+ u8 *dst = Memory::GetPointer(entry_point);
u32 srcSize = size;
u32 *s = (u32*)src;
u32 *d = (u32*)dst;
@@ -143,7 +144,7 @@ bool Load_BIN(std::string &filename) {
*d++ = (*s++);
}
- Core::g_app_core->SetPC(entrypoint);
+ __KernelLoadExec(entry_point);
delete[] buffer;
}
@@ -186,6 +187,9 @@ FileType IdentifyFile(std::string &filename) {
else if (!strcasecmp(extension.c_str(), ".elf")) {
return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p
}
+ else if (!strcasecmp(extension.c_str(), ".axf")) {
+ return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p
+ }
else if (!strcasecmp(extension.c_str(), ".bin")) {
return FILETYPE_CTR_BIN;
}
diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp
index 59560b87d..c45746be9 100644
--- a/src/core/mem_map.cpp
+++ b/src/core/mem_map.cpp
@@ -17,6 +17,7 @@ u8* g_base = NULL; ///< The base pointer to the aut
MemArena g_arena; ///< The MemArena class
u8* g_exefs_code = NULL; ///< ExeFS:/.code is loaded here
+u8* g_system_mem = NULL; ///< System memory
u8* g_heap = NULL; ///< Application heap (main memory)
u8* g_heap_gsp = NULL; ///< GSP heap (main memory)
u8* g_vram = NULL; ///< Video memory (VRAM) pointer
@@ -27,6 +28,7 @@ u8* g_physical_bootrom = NULL; ///< Bootrom physical memory
u8* g_uncached_bootrom = NULL;
u8* g_physical_exefs_code = NULL; ///< Phsical ExeFS:/.code is loaded here
+u8* g_physical_system_mem = NULL; ///< System physical memory
u8* g_physical_fcram = NULL; ///< Main physical memory (FCRAM)
u8* g_physical_heap_gsp = NULL; ///< GSP heap physical memory
u8* g_physical_vram = NULL; ///< Video physical memory (VRAM)
@@ -39,6 +41,7 @@ static MemoryView g_views[] = {
{&g_vram, &g_physical_vram, VRAM_VADDR, VRAM_SIZE, 0},
{&g_heap, &g_physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM},
{&g_shared_mem, &g_physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0},
+ {&g_system_mem, &g_physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0},
{&g_kernel_mem, &g_physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0},
{&g_heap_gsp, &g_physical_heap_gsp, HEAP_GSP_VADDR, HEAP_GSP_SIZE, 0},
};
diff --git a/src/core/mem_map.h b/src/core/mem_map.h
index af2212a5f..12d497ef3 100644
--- a/src/core/mem_map.h
+++ b/src/core/mem_map.h
@@ -47,6 +47,12 @@ enum {
EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE),
EXEFS_CODE_MASK = 0x03FFFFFF,
+ // Region of FCRAM used by system
+ SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB
+ SYSTEM_MEMORY_VADDR = 0x04000000,
+ SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE),
+ SYSTEM_MEMORY_MASK = 0x03FFFFFF,
+
HEAP_SIZE = FCRAM_SIZE, ///< Application heap size
//HEAP_PADDR = HEAP_GSP_SIZE,
//HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE),
@@ -116,6 +122,7 @@ extern u8* g_heap; ///< Application heap (main memory)
extern u8* g_vram; ///< Video memory (VRAM)
extern u8* g_shared_mem; ///< Shared memory
extern u8* g_kernel_mem; ///< Kernel memory
+extern u8* g_system_mem; ///< System memory
extern u8* g_exefs_code; ///< ExeFS:/.code is loaded here
void Init();
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp
index 8ab647714..86e9eaa20 100644
--- a/src/core/mem_map_funcs.cpp
+++ b/src/core/mem_map_funcs.cpp
@@ -73,6 +73,10 @@ inline void _Read(T &var, const u32 addr) {
} else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
var = *((const T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK]);
+ // System memory
+ } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
+ var = *((const T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK]);
+
// Config memory
} else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) {
ConfigMem::Read<T>(var, vaddr);
@@ -115,6 +119,10 @@ inline void _Write(u32 addr, const T data) {
} else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
*(T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK] = data;
+ // System memory
+ } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
+ *(T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK] = data;
+
// VRAM
} else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
*(T*)&g_vram[vaddr & VRAM_MASK] = data;
@@ -153,9 +161,13 @@ u8 *GetPointer(const u32 addr) {
return g_heap + (vaddr & HEAP_MASK);
// Shared memory
- } else if ((vaddr > SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
+ } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
return g_shared_mem + (vaddr & SHARED_MEMORY_MASK);
+ // System memory
+ } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
+ return g_system_mem + (vaddr & SYSTEM_MEMORY_MASK);
+
// VRAM
} else if ((vaddr > VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
return g_vram + (vaddr & VRAM_MASK);