diff options
Diffstat (limited to '')
-rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/core/arm/arm_interface.h | 6 | ||||
-rw-r--r-- | src/core/arm/interpreter/arm_interpreter.cpp | 8 | ||||
-rw-r--r-- | src/core/arm/interpreter/arm_interpreter.h | 6 | ||||
-rw-r--r-- | src/core/core.vcxproj | 4 | ||||
-rw-r--r-- | src/core/core.vcxproj.filters | 15 | ||||
-rw-r--r-- | src/core/hle/function_wrappers.h | 7 | ||||
-rw-r--r-- | src/core/hle/hle.cpp | 11 | ||||
-rw-r--r-- | src/core/hle/hle.h | 4 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.cpp | 154 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.h | 134 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.cpp | 369 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.h | 35 | ||||
-rw-r--r-- | src/core/hle/service/apt.cpp | 6 | ||||
-rw-r--r-- | src/core/hle/service/srv.cpp | 8 | ||||
-rw-r--r-- | src/core/hle/syscall.cpp | 63 | ||||
-rw-r--r-- | src/core/hle/syscall.h | 25 | ||||
-rw-r--r-- | src/core/loader.cpp | 20 | ||||
-rw-r--r-- | src/core/mem_map.cpp | 3 | ||||
-rw-r--r-- | src/core/mem_map.h | 7 | ||||
-rw-r--r-- | src/core/mem_map_funcs.cpp | 14 |
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); |