mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #9 from bunnei/master
Add initial kernel HLE, includes thread creation and context switching
This commit is contained in:
		
						commit
						6448c2f300
					
				
					 45 changed files with 1571 additions and 195 deletions
				
			
		|  | @ -24,7 +24,14 @@ int __cdecl main(int argc, char **argv) { | |||
| 
 | ||||
|     System::Init(emu_window); | ||||
| 
 | ||||
|     std::string boot_filename = "homebrew.elf"; | ||||
|     std::string boot_filename; | ||||
| 
 | ||||
|     if (argc < 2) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); | ||||
|     } | ||||
|     else { | ||||
|         boot_filename = argv[1]; | ||||
|     } | ||||
|     std::string error_str; | ||||
| 
 | ||||
|     bool res = Loader::LoadFile(boot_filename, &error_str); | ||||
|  |  | |||
|  | @ -142,7 +142,7 @@ void GMainWindow::BootGame(const char* filename) | |||
| 
 | ||||
| void GMainWindow::OnMenuLoadFile() | ||||
| { | ||||
|     QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS homebrew (*.elf *.dat *.bin)")); | ||||
|     QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS homebrew (*.elf *.axf *.dat *.bin)")); | ||||
|     if (filename.size()) | ||||
|        BootGame(filename.toLatin1().data()); | ||||
| } | ||||
|  |  | |||
|  | @ -190,6 +190,7 @@ | |||
|     <ClInclude Include="swap.h" /> | ||||
|     <ClInclude Include="symbols.h" /> | ||||
|     <ClInclude Include="thread.h" /> | ||||
|     <ClInclude Include="thread_queue_list.h" /> | ||||
|     <ClInclude Include="thunk.h" /> | ||||
|     <ClInclude Include="timer.h" /> | ||||
|     <ClInclude Include="utf8.h" /> | ||||
|  |  | |||
|  | @ -40,6 +40,7 @@ | |||
|     <ClInclude Include="symbols.h" /> | ||||
|     <ClInclude Include="scm_rev.h" /> | ||||
|     <ClInclude Include="bit_field.h" /> | ||||
|     <ClInclude Include="thread_queue_list.h" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="break_points.cpp" /> | ||||
|  |  | |||
|  | @ -22,6 +22,11 @@ template<> struct CompileTimeAssert<true> {}; | |||
| #define b32(x)  (b16(x) | (b16(x) >>16) ) | ||||
| #define ROUND_UP_POW2(x)    (b32(x - 1) + 1) | ||||
| 
 | ||||
| #define MIN(a, b)   ((a)<(b)?(a):(b)) | ||||
| #define MAX(a, b)   ((a)>(b)?(a):(b)) | ||||
| 
 | ||||
| #define CLAMP(x, min, max)  (((x) > max) ? max : (((x) < min) ? min : (x))) | ||||
| 
 | ||||
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) | ||||
| 
 | ||||
| #ifndef _WIN32 | ||||
|  |  | |||
|  | @ -5,6 +5,8 @@ | |||
| #ifndef _LOG_H_ | ||||
| #define _LOG_H_ | ||||
| 
 | ||||
| #define LOGGING | ||||
| 
 | ||||
| #define    NOTICE_LEVEL  1  // VERY important information that is NOT errors. Like startup and OSReports.
 | ||||
| #define    ERROR_LEVEL   2  // Critical errors 
 | ||||
| #define    WARNING_LEVEL 3  // Something is suspicious.
 | ||||
|  | @ -53,7 +55,7 @@ enum LOG_TYPE { | |||
|     WII_IPC_ES, | ||||
|     WII_IPC_FILEIO, | ||||
|     WII_IPC_HID, | ||||
|     WII_IPC_HLE, | ||||
|     KERNEL, | ||||
|     SVC, | ||||
|     NDMA, | ||||
|     HLE, | ||||
|  |  | |||
|  | @ -60,13 +60,13 @@ LogManager::LogManager() | |||
|     m_Log[LogTypes::LOADER]             = new LogContainer("Loader",            "Loader"); | ||||
|     m_Log[LogTypes::FILESYS]            = new LogContainer("FileSys",           "File System"); | ||||
|     m_Log[LogTypes::WII_IPC_HID]        = new LogContainer("WII_IPC_HID",       "WII IPC HID"); | ||||
|     m_Log[LogTypes::WII_IPC_HLE]        = new LogContainer("WII_IPC_HLE",       "WII IPC HLE"); | ||||
|     m_Log[LogTypes::KERNEL]             = new LogContainer("KERNEL",            "KERNEL HLE"); | ||||
|     m_Log[LogTypes::WII_IPC_DVD]        = new LogContainer("WII_IPC_DVD",       "WII IPC DVD"); | ||||
|     m_Log[LogTypes::WII_IPC_ES]         = new LogContainer("WII_IPC_ES",        "WII IPC ES"); | ||||
|     m_Log[LogTypes::WII_IPC_FILEIO]     = new LogContainer("WII_IPC_FILEIO",    "WII IPC FILEIO"); | ||||
|     m_Log[LogTypes::RENDER]             = new LogContainer("RENDER",            "RENDER"); | ||||
|     m_Log[LogTypes::LCD]                = new LogContainer("LCD",               "LCD"); | ||||
|     m_Log[LogTypes::SVC]                = new LogContainer("SVC",               "Supervisor Call"); | ||||
|     m_Log[LogTypes::SVC]                = new LogContainer("SVC",               "Supervisor Call HLE"); | ||||
|     m_Log[LogTypes::NDMA]               = new LogContainer("NDMA",              "NDMA"); | ||||
|     m_Log[LogTypes::HLE]                = new LogContainer("HLE",               "High Level Emulation"); | ||||
|     m_Log[LogTypes::HW]                 = new LogContainer("HW",                "Hardware"); | ||||
|  |  | |||
							
								
								
									
										216
									
								
								src/common/thread_queue_list.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								src/common/thread_queue_list.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,216 @@ | |||
| // Copyright 2014 Citra Emulator Project / PPSSPP Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.  
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| template<class IdType> | ||||
| struct ThreadQueueList { | ||||
|     // Number of queues (number of priority levels starting at 0.)
 | ||||
|     static const int NUM_QUEUES = 128; | ||||
|      | ||||
|     // Initial number of threads a single queue can handle.
 | ||||
|     static const int INITIAL_CAPACITY = 32; | ||||
| 
 | ||||
|     struct Queue { | ||||
|         // Next ever-been-used queue (worse priority.)
 | ||||
|         Queue *next; | ||||
|         // First valid item in data.
 | ||||
|         int first; | ||||
|         // One after last valid item in data.
 | ||||
|         int end; | ||||
|         // A too-large array with room on the front and end.
 | ||||
|         IdType *data; | ||||
|         // Size of data array.
 | ||||
|         int capacity; | ||||
|     }; | ||||
| 
 | ||||
|     ThreadQueueList() { | ||||
|         memset(queues, 0, sizeof(queues)); | ||||
|         first = invalid(); | ||||
|     } | ||||
| 
 | ||||
|     ~ThreadQueueList() { | ||||
|         for (int i = 0; i < NUM_QUEUES; ++i) | ||||
|         { | ||||
|             if (queues[i].data != NULL) | ||||
|                 free(queues[i].data); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Only for debugging, returns priority level.
 | ||||
|     int contains(const IdType uid) { | ||||
|         for (int i = 0; i < NUM_QUEUES; ++i) | ||||
|         { | ||||
|             if (queues[i].data == NULL) | ||||
|                 continue; | ||||
| 
 | ||||
|             Queue *cur = &queues[i]; | ||||
|             for (int j = cur->first; j < cur->end; ++j) | ||||
|             { | ||||
|                 if (cur->data[j] == uid) | ||||
|                     return i; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     inline IdType pop_first() { | ||||
|         Queue *cur = first; | ||||
|         while (cur != invalid()) | ||||
|         { | ||||
|             if (cur->end - cur->first > 0) | ||||
|                 return cur->data[cur->first++]; | ||||
|             cur = cur->next; | ||||
|         } | ||||
| 
 | ||||
|         //_dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty.");
 | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     inline IdType pop_first_better(u32 priority) { | ||||
|         Queue *cur = first; | ||||
|         Queue *stop = &queues[priority]; | ||||
|         while (cur < stop) | ||||
|         { | ||||
|             if (cur->end - cur->first > 0) | ||||
|                 return cur->data[cur->first++]; | ||||
|             cur = cur->next; | ||||
|         } | ||||
| 
 | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     inline void push_front(u32 priority, const IdType threadID) { | ||||
|         Queue *cur = &queues[priority]; | ||||
|         cur->data[--cur->first] = threadID; | ||||
|         if (cur->first == 0) | ||||
|             rebalance(priority); | ||||
|     } | ||||
| 
 | ||||
|     inline void push_back(u32 priority, const IdType threadID) { | ||||
|         Queue *cur = &queues[priority]; | ||||
|         cur->data[cur->end++] = threadID; | ||||
|         if (cur->end == cur->capacity) | ||||
|             rebalance(priority); | ||||
|     } | ||||
| 
 | ||||
|     inline void remove(u32 priority, const IdType threadID) { | ||||
|         Queue *cur = &queues[priority]; | ||||
|         //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
 | ||||
| 
 | ||||
|         for (int i = cur->first; i < cur->end; ++i) | ||||
|         { | ||||
|             if (cur->data[i] == threadID) | ||||
|             { | ||||
|                 int remaining = --cur->end - i; | ||||
|                 if (remaining > 0) | ||||
|                     memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(IdType)); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Wasn't there.
 | ||||
|     } | ||||
| 
 | ||||
|     inline void rotate(u32 priority) { | ||||
|         Queue *cur = &queues[priority]; | ||||
|         //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
 | ||||
| 
 | ||||
|         if (cur->end - cur->first > 1) | ||||
|         { | ||||
|             cur->data[cur->end++] = cur->data[cur->first++]; | ||||
|             if (cur->end == cur->capacity) | ||||
|                 rebalance(priority); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     inline void clear() { | ||||
|         for (int i = 0; i < NUM_QUEUES; ++i) | ||||
|         { | ||||
|             if (queues[i].data != NULL) | ||||
|                 free(queues[i].data); | ||||
|         } | ||||
|         memset(queues, 0, sizeof(queues)); | ||||
|         first = invalid(); | ||||
|     } | ||||
| 
 | ||||
|     inline bool empty(u32 priority) const { | ||||
|         const Queue *cur = &queues[priority]; | ||||
|         return cur->first == cur->end; | ||||
|     } | ||||
| 
 | ||||
|     inline void prepare(u32 priority) { | ||||
|         Queue *cur = &queues[priority]; | ||||
|         if (cur->next == NULL) | ||||
|             link(priority, INITIAL_CAPACITY); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     Queue *invalid() const { | ||||
|         return (Queue *) -1; | ||||
|     } | ||||
| 
 | ||||
|     void link(u32 priority, int size) { | ||||
|         //_dbg_assert_msg_(SCEKERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once.");
 | ||||
| 
 | ||||
|         if (size <= INITIAL_CAPACITY) | ||||
|             size = INITIAL_CAPACITY; | ||||
|         else | ||||
|         { | ||||
|             int goal = size; | ||||
|             size = INITIAL_CAPACITY; | ||||
|             while (size < goal) | ||||
|                 size *= 2; | ||||
|         } | ||||
|         Queue *cur = &queues[priority]; | ||||
|         cur->data = (IdType *) malloc(sizeof(IdType) * size); | ||||
|         cur->capacity = size; | ||||
|         cur->first = size / 2; | ||||
|         cur->end = size / 2; | ||||
| 
 | ||||
|         for (int i = (int) priority - 1; i >= 0; --i) | ||||
|         { | ||||
|             if (queues[i].next != NULL) | ||||
|             { | ||||
|                 cur->next = queues[i].next; | ||||
|                 queues[i].next = cur; | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         cur->next = first; | ||||
|         first = cur; | ||||
|     } | ||||
| 
 | ||||
|     void rebalance(u32 priority) { | ||||
|         Queue *cur = &queues[priority]; | ||||
|         int size = cur->end - cur->first; | ||||
|         if (size >= cur->capacity - 2)  { | ||||
|             IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType)); | ||||
|             if (new_data != NULL)  { | ||||
|                 cur->capacity *= 2; | ||||
|                 cur->data = new_data; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         int newFirst = (cur->capacity - size) / 2; | ||||
|         if (newFirst != cur->first) { | ||||
|             memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(IdType)); | ||||
|             cur->first = newFirst; | ||||
|             cur->end = newFirst + size; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // The first queue that's ever been used.
 | ||||
|     Queue *first; | ||||
|     // The priority level queues of thread ids.
 | ||||
|     Queue queues[NUM_QUEUES]; | ||||
| }; | ||||
| 
 | ||||
| } // namespace
 | ||||
|  | @ -33,7 +33,10 @@ set(SRCS    core.cpp | |||
|             hle/hle.cpp | ||||
|             hle/config_mem.cpp | ||||
|             hle/coprocessor.cpp | ||||
|             hle/syscall.cpp | ||||
|             hle/svc.cpp | ||||
|             hle/kernel/kernel.cpp | ||||
|             hle/kernel/mutex.cpp | ||||
|             hle/kernel/thread.cpp | ||||
|             hle/service/apt.cpp | ||||
|             hle/service/gsp.cpp | ||||
|             hle/service/hid.cpp | ||||
|  | @ -75,7 +78,10 @@ set(HEADERS core.h | |||
|             hle/config_mem.h | ||||
|             hle/coprocessor.h | ||||
|             hle/hle.h | ||||
|             hle/syscall.h | ||||
|             hle/svc.h | ||||
|             hle/kernel/kernel.h | ||||
|             hle/kernel/mutex.h | ||||
|             hle/kernel/thread.h | ||||
|             hle/function_wrappers.h | ||||
|             hle/service/apt.h | ||||
|             hle/service/gsp.h | ||||
|  |  | |||
|  | @ -7,11 +7,13 @@ | |||
| #include "common/common.h" | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| #include "core/hle/svc.h" | ||||
| 
 | ||||
| /// Generic ARM11 CPU interface
 | ||||
| class ARM_Interface : NonCopyable { | ||||
| public: | ||||
|     ARM_Interface() { | ||||
|         m_num_instructions = 0; | ||||
|         num_instructions = 0; | ||||
|     } | ||||
| 
 | ||||
|     ~ARM_Interface() { | ||||
|  | @ -23,7 +25,7 @@ public: | |||
|      */ | ||||
|     void Run(int num_instructions) { | ||||
|         ExecuteInstructions(num_instructions); | ||||
|         m_num_instructions += num_instructions; | ||||
|         num_instructions += num_instructions; | ||||
|     } | ||||
| 
 | ||||
|     /// Step CPU by one instruction
 | ||||
|  | @ -63,15 +65,33 @@ 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 | ||||
|      */ | ||||
|     virtual u64 GetTicks() const = 0; | ||||
| 
 | ||||
|     /// Getter for m_num_instructions
 | ||||
|     /**
 | ||||
|      * Saves the current CPU context | ||||
|      * @param ctx Thread context to save | ||||
|      */ | ||||
|     virtual void SaveContext(ThreadContext& ctx) = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Loads a CPU context | ||||
|      * @param ctx Thread context to load | ||||
|      */ | ||||
|     virtual void LoadContext(const ThreadContext& ctx) = 0; | ||||
| 
 | ||||
|     /// Getter for num_instructions
 | ||||
|     u64 GetNumInstructions() { | ||||
|         return m_num_instructions; | ||||
|         return num_instructions; | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|  | @ -84,6 +104,6 @@ protected: | |||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     u64 m_num_instructions;                     ///< Number of instructions executed
 | ||||
|     u64 num_instructions; ///< Number of instructions executed
 | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -9,30 +9,30 @@ const static cpu_config_t s_arm11_cpu_info = { | |||
| }; | ||||
| 
 | ||||
| ARM_Interpreter::ARM_Interpreter()  { | ||||
|     m_state = new ARMul_State; | ||||
|     state = new ARMul_State; | ||||
| 
 | ||||
|     ARMul_EmulateInit(); | ||||
|     ARMul_NewState(m_state); | ||||
|     ARMul_NewState(state); | ||||
| 
 | ||||
|     m_state->abort_model = 0; | ||||
|     m_state->cpu = (cpu_config_t*)&s_arm11_cpu_info; | ||||
|     m_state->bigendSig = LOW; | ||||
|     state->abort_model = 0; | ||||
|     state->cpu = (cpu_config_t*)&s_arm11_cpu_info; | ||||
|     state->bigendSig = LOW; | ||||
| 
 | ||||
|     ARMul_SelectProcessor(m_state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop); | ||||
|     m_state->lateabtSig = LOW; | ||||
|     mmu_init(m_state); | ||||
|     ARMul_SelectProcessor(state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop); | ||||
|     state->lateabtSig = LOW; | ||||
|     mmu_init(state); | ||||
| 
 | ||||
|     // Reset the core to initial state
 | ||||
|     ARMul_Reset(m_state); | ||||
|     m_state->NextInstr = 0; | ||||
|     m_state->Emulate = 3; | ||||
|     ARMul_Reset(state); | ||||
|     state->NextInstr = 0; | ||||
|     state->Emulate = 3; | ||||
| 
 | ||||
|     m_state->pc = m_state->Reg[15] = 0x00000000; | ||||
|     m_state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack
 | ||||
|     state->pc = state->Reg[15] = 0x00000000; | ||||
|     state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack
 | ||||
| } | ||||
| 
 | ||||
| ARM_Interpreter::~ARM_Interpreter() { | ||||
|     delete m_state; | ||||
|     delete state; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -40,7 +40,7 @@ ARM_Interpreter::~ARM_Interpreter() { | |||
|  * @param addr Address to set PC to | ||||
|  */ | ||||
| void ARM_Interpreter::SetPC(u32 pc) { | ||||
|     m_state->pc = m_state->Reg[15] = pc; | ||||
|     state->pc = state->Reg[15] = pc; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -48,7 +48,7 @@ void ARM_Interpreter::SetPC(u32 pc) { | |||
|  * @return Returns current PC | ||||
|  */ | ||||
| u32 ARM_Interpreter::GetPC() const { | ||||
|     return m_state->pc; | ||||
|     return state->pc; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -57,7 +57,7 @@ u32 ARM_Interpreter::GetPC() const { | |||
|  * @return Returns the value in the register | ||||
|  */ | ||||
| u32 ARM_Interpreter::GetReg(int index) const { | ||||
|     return m_state->Reg[index]; | ||||
|     return state->Reg[index]; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -66,7 +66,7 @@ u32 ARM_Interpreter::GetReg(int index) const { | |||
|  * @param value Value to set register to | ||||
|  */ | ||||
| void ARM_Interpreter::SetReg(int index, u32 value) { | ||||
|     m_state->Reg[index] = value; | ||||
|     state->Reg[index] = value; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -74,7 +74,15 @@ void ARM_Interpreter::SetReg(int index, u32 value) { | |||
|  * @return Returns the value of the CPSR register | ||||
|  */ | ||||
| u32 ARM_Interpreter::GetCPSR() const { | ||||
|     return m_state->Cpsr; | ||||
|     return state->Cpsr; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Set the current CPSR register | ||||
|  * @param cpsr Value to set CPSR to | ||||
|  */ | ||||
| void ARM_Interpreter::SetCPSR(u32 cpsr) { | ||||
|     state->Cpsr = cpsr; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -82,7 +90,7 @@ u32 ARM_Interpreter::GetCPSR() const { | |||
|  * @return Returns number of clock ticks | ||||
|  */ | ||||
| u64 ARM_Interpreter::GetTicks() const { | ||||
|     return ARMul_Time(m_state); | ||||
|     return ARMul_Time(state); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -90,6 +98,45 @@ u64 ARM_Interpreter::GetTicks() const { | |||
|  * @param num_instructions Number of instructions to executes | ||||
|  */ | ||||
| void ARM_Interpreter::ExecuteInstructions(int num_instructions) { | ||||
|     m_state->NumInstrsToExecute = num_instructions; | ||||
|     ARMul_Emulate32(m_state); | ||||
|     state->NumInstrsToExecute = num_instructions; | ||||
|     ARMul_Emulate32(state); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Saves the current CPU context | ||||
|  * @param ctx Thread context to save | ||||
|  * @todo Do we need to save Reg[15] and NextInstr? | ||||
|  */ | ||||
| void ARM_Interpreter::SaveContext(ThreadContext& ctx) { | ||||
|     memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); | ||||
|     memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); | ||||
| 
 | ||||
|     ctx.sp = state->Reg[13]; | ||||
|     ctx.lr = state->Reg[14]; | ||||
|     ctx.pc = state->pc; | ||||
|     ctx.cpsr = state->Cpsr; | ||||
| 
 | ||||
|     ctx.fpscr = state->VFP[1]; | ||||
|     ctx.fpexc = state->VFP[2]; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads a CPU context | ||||
|  * @param ctx Thread context to load | ||||
|  * @param Do we need to load Reg[15] and NextInstr? | ||||
|  */ | ||||
| void ARM_Interpreter::LoadContext(const ThreadContext& ctx) { | ||||
|     memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); | ||||
|     memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); | ||||
| 
 | ||||
|     state->Reg[13] = ctx.sp; | ||||
|     state->Reg[14] = ctx.lr; | ||||
|     state->pc = ctx.pc; | ||||
|     state->Cpsr = ctx.cpsr; | ||||
| 
 | ||||
|     state->VFP[1] = ctx.fpscr; | ||||
|     state->VFP[2] = ctx.fpexc; | ||||
| 
 | ||||
|     state->Reg[15] = ctx.pc; | ||||
|     state->NextInstr = RESUME; | ||||
| } | ||||
|  |  | |||
|  | @ -48,12 +48,30 @@ 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 | ||||
|      */ | ||||
|     u64 GetTicks() const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Saves the current CPU context | ||||
|      * @param ctx Thread context to save | ||||
|      */ | ||||
|     void SaveContext(ThreadContext& ctx); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Loads a CPU context | ||||
|      * @param ctx Thread context to load | ||||
|      */ | ||||
|     void LoadContext(const ThreadContext& ctx); | ||||
| 
 | ||||
| protected: | ||||
| 
 | ||||
|     /**
 | ||||
|  | @ -64,6 +82,6 @@ protected: | |||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     ARMul_State* m_state; | ||||
|     ARMul_State* state; | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -24,10 +24,6 @@ | |||
| 
 | ||||
| #include "common/platform.h" | ||||
| 
 | ||||
| #if EMU_PLATFORM == PLATFORM_WINDOWS | ||||
| #include <windows.h> | ||||
| #endif | ||||
| 
 | ||||
| //teawater add for arm2x86 2005.02.14-------------------------------------------
 | ||||
| // koodailar remove it for mingw 2005.12.18----------------
 | ||||
| //anthonylee modify it for portable 2007.01.30
 | ||||
|  |  | |||
|  | @ -4478,8 +4478,7 @@ ARMul_Emulate26 (ARMul_State * state) | |||
|                                  isize) & | ||||
|                                 R15PCBITS)); | ||||
| #endif | ||||
|                     } | ||||
|                     else | ||||
|                     } else if (instr != 0xDEADC0DE) // thumbemu uses 0xDEADCODE for debugging to catch non updates 
 | ||||
|                         ARMul_MCR (state, instr, | ||||
|                                DEST); | ||||
|                 } | ||||
|  | @ -4549,7 +4548,7 @@ ARMul_Emulate26 (ARMul_State * state) | |||
|                 //    ARMul_OSHandleSWI (state, BITS (0, 23));
 | ||||
|                 //    break;
 | ||||
|                 //}
 | ||||
|                 HLE::CallSyscall(instr); | ||||
|                 HLE::CallSVC(instr); | ||||
|                 ARMul_Abort (state, ARMul_SWIV); | ||||
|                 break; | ||||
|             } | ||||
|  |  | |||
|  | @ -17,8 +17,11 @@ | |||
| 
 | ||||
| 
 | ||||
| #include "common/platform.h" | ||||
| 
 | ||||
| #if EMU_PLATFORM == PLATFORM_LINUX | ||||
| #include <unistd.h> | ||||
| #elif EMU_PLATFORM == PLATFORM_WINDOWS | ||||
| #include <windows.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <math.h> | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ | |||
| #ifndef __VFP_H__ | ||||
| #define __VFP_H__ | ||||
| 
 | ||||
| #define DBG(...) DEBUG_LOG(ARM11, __VA_ARGS__) | ||||
| #define DBG(...) //DEBUG_LOG(ARM11, __VA_ARGS__)
 | ||||
| 
 | ||||
| #define vfpdebug //printf
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,6 +12,8 @@ | |||
| #include "core/arm/disassembler/arm_disasm.h" | ||||
| #include "core/arm/interpreter/arm_interpreter.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/thread.h" | ||||
| 
 | ||||
| namespace Core { | ||||
| 
 | ||||
| ARM_Disasm*     g_disasm    = NULL; ///< ARM disassembler
 | ||||
|  | @ -21,14 +23,17 @@ ARM_Interface*  g_sys_core  = NULL; ///< ARM11 system (OS) core | |||
| /// Run the core CPU loop
 | ||||
| void RunLoop() { | ||||
|     for (;;){ | ||||
|         g_app_core->Run(10000); | ||||
|         g_app_core->Run(100); | ||||
|         HW::Update(); | ||||
|         Kernel::Reschedule(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Step the CPU one instruction
 | ||||
| void SingleStep() { | ||||
|     g_app_core->Step(); | ||||
|     HW::Update(); | ||||
|     Kernel::Reschedule(); | ||||
| } | ||||
| 
 | ||||
| /// Halt the core
 | ||||
|  |  | |||
|  | @ -168,12 +168,15 @@ | |||
|     <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\mutex.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" /> | ||||
|     <ClCompile Include="hle\service\service.cpp" /> | ||||
|     <ClCompile Include="hle\service\srv.cpp" /> | ||||
|     <ClCompile Include="hle\syscall.cpp" /> | ||||
|     <ClCompile Include="hle\svc.cpp" /> | ||||
|     <ClCompile Include="hw\hw.cpp" /> | ||||
|     <ClCompile Include="hw\lcd.cpp" /> | ||||
|     <ClCompile Include="hw\ndma.cpp" /> | ||||
|  | @ -214,12 +217,15 @@ | |||
|     <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\mutex.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" /> | ||||
|     <ClInclude Include="hle\service\service.h" /> | ||||
|     <ClInclude Include="hle\service\srv.h" /> | ||||
|     <ClInclude Include="hle\syscall.h" /> | ||||
|     <ClInclude Include="hle\svc.h" /> | ||||
|     <ClInclude Include="hw\hw.h" /> | ||||
|     <ClInclude Include="hw\lcd.h" /> | ||||
|     <ClInclude Include="hw\ndma.h" /> | ||||
|  |  | |||
|  | @ -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"> | ||||
|  | @ -81,9 +84,6 @@ | |||
|     <ClCompile Include="hle\hle.cpp"> | ||||
|       <Filter>hle</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="hle\syscall.cpp"> | ||||
|       <Filter>hle</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="hle\service\service.cpp"> | ||||
|       <Filter>hle\service</Filter> | ||||
|     </ClCompile> | ||||
|  | @ -147,12 +147,24 @@ | |||
|     <ClCompile Include="arm\interpreter\mmu\wb.cpp"> | ||||
|       <Filter>arm\interpreter\mmu</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="arm\interpreter\armcopro.cpp"> | ||||
|       <Filter>arm</Filter> | ||||
|     </ClCompile> | ||||
|     <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> | ||||
|     <ClCompile Include="hle\svc.cpp"> | ||||
|       <Filter>hle</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="hle\kernel\mutex.cpp"> | ||||
|       <Filter>hle\kernel</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="arm\interpreter\armcopro.cpp"> | ||||
|       <Filter>arm\interpreter</Filter> | ||||
|     </ClCompile> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClInclude Include="arm\disassembler\arm_disasm.h"> | ||||
|  | @ -217,9 +229,6 @@ | |||
|     <ClInclude Include="hle\service\service.h"> | ||||
|       <Filter>hle\service</Filter> | ||||
|     </ClInclude> | ||||
|     <ClInclude Include="hle\syscall.h"> | ||||
|       <Filter>hle</Filter> | ||||
|     </ClInclude> | ||||
|     <ClInclude Include="hle\service\apt.h"> | ||||
|       <Filter>hle\service</Filter> | ||||
|     </ClInclude> | ||||
|  | @ -274,6 +283,18 @@ | |||
|     <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> | ||||
|     <ClInclude Include="hle\svc.h"> | ||||
|       <Filter>hle</Filter> | ||||
|     </ClInclude> | ||||
|     <ClInclude Include="hle\kernel\mutex.h"> | ||||
|       <Filter>hle\kernel</Filter> | ||||
|     </ClInclude> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Text Include="CMakeLists.txt" /> | ||||
|  |  | |||
|  | @ -719,17 +719,27 @@ 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); | ||||
| } | ||||
| 
 | ||||
| template<int func(u32, s64)> void WrapI_US64() { | ||||
|     int retval = func(PARAM(0), PARAM64(2)); | ||||
|     int retval = func(PARAM(0), PARAM64(1)); | ||||
|     RETURN(retval); | ||||
| } | ||||
| 
 | ||||
| template<int func(void*, void*, u32, u32, s64)> void WrapI_VVUUS64() { | ||||
|     int retval = func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2), PARAM(3), PARAM(4)); | ||||
|     RETURN(retval); | ||||
| } | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| 
 | ||||
| #include "core/mem_map.h" | ||||
| #include "core/hle/hle.h" | ||||
| #include "core/hle/syscall.h" | ||||
| #include "core/hle/svc.h" | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
|  | @ -15,17 +15,17 @@ namespace HLE { | |||
| 
 | ||||
| static std::vector<ModuleDef> g_module_db; | ||||
| 
 | ||||
| const FunctionDef* GetSyscallInfo(u32 opcode) { | ||||
| const FunctionDef* GetSVCInfo(u32 opcode) { | ||||
|     u32 func_num = opcode & 0xFFFFFF; // 8 bits
 | ||||
|     if (func_num > 0xFF) { | ||||
|         ERROR_LOG(HLE,"Unknown syscall: 0x%02X", func_num);  | ||||
|         ERROR_LOG(HLE,"Unknown SVC: 0x%02X", func_num);  | ||||
|         return NULL; | ||||
|     } | ||||
|     return &g_module_db[0].func_table[func_num]; | ||||
| } | ||||
| 
 | ||||
| void CallSyscall(u32 opcode) { | ||||
|     const FunctionDef *info = GetSyscallInfo(opcode); | ||||
| void CallSVC(u32 opcode) { | ||||
|     const FunctionDef *info = GetSVCInfo(opcode); | ||||
| 
 | ||||
|     if (!info) { | ||||
|         return; | ||||
|  | @ -33,17 +33,28 @@ void CallSyscall(u32 opcode) { | |||
|     if (info->func) { | ||||
|         info->func(); | ||||
|     } else { | ||||
|         ERROR_LOG(HLE, "Unimplemented SysCall function %s(..)", info->name.c_str()); | ||||
|         ERROR_LOG(HLE, "Unimplemented SVC function %s(..)", info->name.c_str()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| void RegisterAllModules() { | ||||
|     Syscall::Register(); | ||||
|     SVC::Register(); | ||||
| } | ||||
| 
 | ||||
| void Init() { | ||||
|  |  | |||
|  | @ -34,7 +34,11 @@ struct ModuleDef { | |||
| 
 | ||||
| void RegisterModule(std::string name, int num_functions, const FunctionDef *func_table); | ||||
| 
 | ||||
| void CallSyscall(u32 opcode); | ||||
| void CallSVC(u32 opcode); | ||||
| 
 | ||||
| void EatCycles(u32 cycles); | ||||
| 
 | ||||
| void ReSchedule(const char *reason); | ||||
| 
 | ||||
| void Init(); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										158
									
								
								src/core/hle/kernel/kernel.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								src/core/hle/kernel/kernel.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,158 @@ | |||
| // 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" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| ObjectPool g_object_pool; | ||||
| 
 | ||||
| ObjectPool::ObjectPool() { | ||||
|     memset(occupied, 0, sizeof(bool) * MAX_COUNT); | ||||
|     next_id = INITIAL_NEXT_ID; | ||||
| } | ||||
| 
 | ||||
| Handle ObjectPool::Create(Object* 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 ObjectPool::IsValid(Handle handle) { | ||||
|     int index = handle - HANDLE_OFFSET; | ||||
|     if (index < 0) | ||||
|         return false; | ||||
|     if (index >= MAX_COUNT) | ||||
|         return false; | ||||
| 
 | ||||
|     return occupied[index]; | ||||
| } | ||||
| 
 | ||||
| void ObjectPool::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(Object*)*MAX_COUNT); | ||||
|     next_id = INITIAL_NEXT_ID; | ||||
| } | ||||
| 
 | ||||
| Object* &ObjectPool::operator [](Handle handle) | ||||
| { | ||||
|     _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); | ||||
|     return pool[handle - HANDLE_OFFSET]; | ||||
| } | ||||
| 
 | ||||
| void ObjectPool::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 ObjectPool::GetCount() { | ||||
|     int count = 0; | ||||
|     for (int i = 0; i < MAX_COUNT; i++) { | ||||
|         if (occupied[i]) | ||||
|             count++; | ||||
|     } | ||||
|     return count; | ||||
| } | ||||
| 
 | ||||
| Object* ObjectPool::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; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Init() { | ||||
|     Kernel::ThreadingInit(); | ||||
| } | ||||
| 
 | ||||
| void Shutdown() { | ||||
|     Kernel::ThreadingShutdown(); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads executable stored at specified address | ||||
|  * @entry_point Entry point in memory of loaded executable | ||||
|  * @return True on success, otherwise false | ||||
|  */ | ||||
| bool LoadExec(u32 entry_point) { | ||||
|     Init(); | ||||
|      | ||||
|     Core::g_app_core->SetPC(entry_point); | ||||
| 
 | ||||
|     // 0x30 is the typical main thread priority I've seen used so far
 | ||||
|     Handle thread = Kernel::SetupMainThread(0x30); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
							
								
								
									
										154
									
								
								src/core/hle/kernel/kernel.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/core/hle/kernel/kernel.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,154 @@ | |||
| // Copyright 2014 Citra Emulator Project / PPSSPP Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.  
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| 
 | ||||
| typedef u32 Handle; | ||||
| typedef s32 Result; | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| enum class HandleType : u32 { | ||||
|     Unknown         = 0, | ||||
|     Port            = 1, | ||||
|     Service         = 2, | ||||
|     Event           = 3, | ||||
|     Mutex           = 4, | ||||
|     SharedMemory    = 5, | ||||
|     Redirection     = 6, | ||||
|     Thread          = 7, | ||||
|     Process         = 8, | ||||
|     Arbiter         = 9, | ||||
|     File            = 10, | ||||
|     Semaphore       = 11, | ||||
| }; | ||||
|      | ||||
| enum { | ||||
|     MAX_NAME_LENGTH     = 0x100, | ||||
|     DEFAULT_STACK_SIZE  = 0x4000, | ||||
| }; | ||||
| 
 | ||||
| class ObjectPool; | ||||
| 
 | ||||
| class Object : NonCopyable { | ||||
|     friend class ObjectPool; | ||||
|     u32 handle; | ||||
| public: | ||||
|     virtual ~Object() {} | ||||
|     Handle GetHandle() const { return handle; } | ||||
|     virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; } | ||||
|     virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; } | ||||
|     virtual Kernel::HandleType GetHandleType() const = 0; | ||||
| }; | ||||
| 
 | ||||
| class ObjectPool : NonCopyable { | ||||
| public: | ||||
|     ObjectPool(); | ||||
|     ~ObjectPool() {} | ||||
| 
 | ||||
|     // Allocates a handle within the range and inserts the object into the map.
 | ||||
|     Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); | ||||
| 
 | ||||
|     static Object* 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->GetHandleType() != T::GetStaticHandleType()) { | ||||
|                 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, HandleType* 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; | ||||
|         } | ||||
|         Object* t = pool[handle - HANDLE_OFFSET]; | ||||
|         *type = t->GetHandleType(); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     Object* &operator [](Handle handle); | ||||
|     void List(); | ||||
|     void Clear(); | ||||
|     int GetCount(); | ||||
| 
 | ||||
| private: | ||||
|      | ||||
|     enum { | ||||
|         MAX_COUNT       = 0x1000, | ||||
|         HANDLE_OFFSET   = 0x100, | ||||
|         INITIAL_NEXT_ID = 0x10, | ||||
|     }; | ||||
| 
 | ||||
|     Object* pool[MAX_COUNT]; | ||||
|     bool    occupied[MAX_COUNT]; | ||||
|     int     next_id; | ||||
| }; | ||||
| 
 | ||||
| extern ObjectPool g_object_pool; | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads executable stored at specified address | ||||
|  * @entry_point Entry point in memory of loaded executable | ||||
|  * @return True on success, otherwise false | ||||
|  */ | ||||
| bool LoadExec(u32 entry_point); | ||||
| 
 | ||||
| } // namespace
 | ||||
							
								
								
									
										132
									
								
								src/core/hle/kernel/mutex.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/core/hle/kernel/mutex.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,132 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.  
 | ||||
| 
 | ||||
| #include <map> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class Mutex : public Object { | ||||
| public: | ||||
|     const char* GetTypeName() { return "Mutex"; } | ||||
| 
 | ||||
|     static Kernel::HandleType GetStaticHandleType() {  return Kernel::HandleType::Mutex; } | ||||
|     Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; } | ||||
| 
 | ||||
|     bool initial_locked;                        ///< Initial lock state when mutex was created
 | ||||
|     bool locked;                                ///< Current locked state
 | ||||
|     Handle lock_thread;                         ///< Handle to thread that currently has mutex
 | ||||
|     std::vector<Handle> waiting_threads;        ///< Threads that are waiting for the mutex
 | ||||
| }; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| typedef std::multimap<Handle, Handle> MutexMap; | ||||
| static MutexMap g_mutex_held_locks; | ||||
| 
 | ||||
| void MutexAcquireLock(Mutex* mutex, Handle thread) { | ||||
|     g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); | ||||
|     mutex->lock_thread = thread; | ||||
| } | ||||
| 
 | ||||
| void MutexAcquireLock(Mutex* mutex) { | ||||
|     Handle thread = GetCurrentThreadHandle(); | ||||
|     MutexAcquireLock(mutex, thread); | ||||
| } | ||||
| 
 | ||||
| void MutexEraseLock(Mutex* mutex) { | ||||
|     Handle handle = mutex->GetHandle(); | ||||
|     auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); | ||||
|     for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { | ||||
|         if ((*iter).second == handle) { | ||||
|             g_mutex_held_locks.erase(iter); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     mutex->lock_thread = -1; | ||||
| } | ||||
| 
 | ||||
| bool LockMutex(Mutex* mutex) { | ||||
|     // Mutex alread locked?
 | ||||
|     if (mutex->locked) { | ||||
|         return false; | ||||
|     } | ||||
|     MutexAcquireLock(mutex); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { | ||||
|     MutexAcquireLock(mutex, thread); | ||||
|     Kernel::ResumeThreadFromWait(thread); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool ReleaseMutex(Mutex* mutex) { | ||||
|     MutexEraseLock(mutex); | ||||
|     bool woke_threads = false; | ||||
|     auto iter = mutex->waiting_threads.begin(); | ||||
| 
 | ||||
|     // Find the next waiting thread for the mutex...
 | ||||
|     while (!woke_threads && !mutex->waiting_threads.empty()) { | ||||
|         woke_threads |= ReleaseMutexForThread(mutex, *iter); | ||||
|         mutex->waiting_threads.erase(iter); | ||||
|     } | ||||
|     // Reset mutex lock thread handle, nothing is waiting
 | ||||
|     if (!woke_threads) { | ||||
|         mutex->locked = false; | ||||
|         mutex->lock_thread = -1; | ||||
|     } | ||||
|     return woke_threads; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Releases a mutex | ||||
|  * @param handle Handle to mutex to release | ||||
|  */ | ||||
| Result ReleaseMutex(Handle handle) { | ||||
|     Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle); | ||||
|     if (!ReleaseMutex(mutex)) { | ||||
|         return -1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a mutex | ||||
|  * @param handle Reference to handle for the newly created mutex | ||||
|  * @param initial_locked Specifies if the mutex should be locked initially | ||||
|  */ | ||||
| Mutex* CreateMutex(Handle& handle, bool initial_locked) { | ||||
|     Mutex* mutex = new Mutex; | ||||
|     handle = Kernel::g_object_pool.Create(mutex); | ||||
| 
 | ||||
|     mutex->locked = mutex->initial_locked = initial_locked; | ||||
| 
 | ||||
|     // Acquire mutex with current thread if initialized as locked...
 | ||||
|     if (mutex->locked) { | ||||
|         MutexAcquireLock(mutex); | ||||
| 
 | ||||
|     // Otherwise, reset lock thread handle
 | ||||
|     } else { | ||||
|         mutex->lock_thread = -1; | ||||
|     } | ||||
|     return mutex; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a mutex | ||||
|  * @param initial_locked Specifies if the mutex should be locked initially | ||||
|  */ | ||||
| Handle CreateMutex(bool initial_locked) { | ||||
|     Handle handle; | ||||
|     Mutex* mutex = CreateMutex(handle, initial_locked); | ||||
|     return handle; | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
							
								
								
									
										26
									
								
								src/core/hle/kernel/mutex.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/core/hle/kernel/mutex.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.  
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| /**
 | ||||
|  * Releases a mutex | ||||
|  * @param handle Handle to mutex to release | ||||
|  */ | ||||
| Result ReleaseMutex(Handle handle); | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a mutex | ||||
|  * @param handle Reference to handle for the newly created mutex | ||||
|  * @param initial_locked Specifies if the mutex should be locked initially | ||||
|  */ | ||||
| Handle CreateMutex(bool initial_locked); | ||||
| 
 | ||||
| } // namespace
 | ||||
							
								
								
									
										323
									
								
								src/core/hle/kernel/thread.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								src/core/hle/kernel/thread.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,323 @@ | |||
| // 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/svc.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class Thread : public Kernel::Object { | ||||
| public: | ||||
| 
 | ||||
|     const char* GetName() { return name; } | ||||
|     const char* GetTypeName() { return "Thread"; } | ||||
| 
 | ||||
|     static Kernel::HandleType GetStaticHandleType() {  return Kernel::HandleType::Thread; } | ||||
|     Kernel::HandleType GetHandleType() const { return Kernel::HandleType::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; | ||||
| 
 | ||||
| 
 | ||||
| /// Gets the current thread
 | ||||
| inline Thread* GetCurrentThread() { | ||||
|     return g_current_thread; | ||||
| } | ||||
| 
 | ||||
| /// Gets the current thread handle
 | ||||
| Handle GetCurrentThreadHandle() { | ||||
|     return GetCurrentThread()->GetHandle(); | ||||
| } | ||||
| 
 | ||||
| /// Sets the current thread
 | ||||
| inline void SetCurrentThread(Thread* t) { | ||||
|     g_current_thread = t; | ||||
|     g_current_thread_handle = t->GetHandle(); | ||||
| } | ||||
| 
 | ||||
| /// Saves the current CPU context
 | ||||
| void SaveContext(ThreadContext& ctx) { | ||||
|     Core::g_app_core->SaveContext(ctx); | ||||
| } | ||||
| 
 | ||||
| /// Loads a CPU context
 | ||||
| void LoadContext(ThreadContext& ctx) { | ||||
|     Core::g_app_core->LoadContext(ctx); | ||||
| } | ||||
| 
 | ||||
| /// Resets a thread
 | ||||
| void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | ||||
|     memset(&t->context, 0, sizeof(ThreadContext)); | ||||
| 
 | ||||
|     t->context.cpu_registers[0] = arg; | ||||
|     t->context.pc = t->entry_point; | ||||
|     t->context.sp = t->stack_top; | ||||
|     t->context.cpsr = 0x1F; // Usermode
 | ||||
|      | ||||
|     if (t->current_priority < lowest_priority) { | ||||
|         t->current_priority = t->initial_priority; | ||||
|     } | ||||
|          | ||||
|     t->wait_type = WAITTYPE_NONE; | ||||
| } | ||||
| 
 | ||||
| /// Change a thread to "ready" state
 | ||||
| void ChangeReadyState(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 ChangeThreadState(Thread* t, ThreadStatus new_status) { | ||||
|     if (!t || t->status == new_status) { | ||||
|         return; | ||||
|     } | ||||
|     ChangeReadyState(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 CallThread(Thread* t) { | ||||
|     // Stop waiting
 | ||||
|     if (t->wait_type != WAITTYPE_NONE) { | ||||
|         t->wait_type = WAITTYPE_NONE; | ||||
|     } | ||||
|     ChangeThreadState(t, THREADSTATUS_READY); | ||||
| } | ||||
| 
 | ||||
| /// Switches CPU context to that of the specified thread
 | ||||
| void SwitchContext(Thread* t) { | ||||
|     Thread* cur = GetCurrentThread(); | ||||
|      | ||||
|     // Save context for current thread
 | ||||
|     if (cur) { | ||||
|         SaveContext(cur->context); | ||||
|          | ||||
|         if (cur->IsRunning()) { | ||||
|             ChangeReadyState(cur, true); | ||||
|         } | ||||
|     } | ||||
|     // Load context of new thread
 | ||||
|     if (t) { | ||||
|         SetCurrentThread(t); | ||||
|         ChangeReadyState(t, false); | ||||
|         t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; | ||||
|         t->wait_type = WAITTYPE_NONE; | ||||
|         LoadContext(t->context); | ||||
|     } else { | ||||
|         SetCurrentThread(NULL); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Gets the next thread that is ready to be run by priority
 | ||||
| Thread* NextThread() { | ||||
|     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 Kernel::g_object_pool.GetFast<Thread>(next); | ||||
| } | ||||
| 
 | ||||
| /// Puts the current thread in the wait state for the given type
 | ||||
| void WaitCurrentThread(WaitType wait_type) { | ||||
|     Thread* t = GetCurrentThread(); | ||||
|     t->wait_type = wait_type; | ||||
|     ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); | ||||
| } | ||||
| 
 | ||||
| /// Resumes a thread from waiting by marking it as "ready"
 | ||||
| void ResumeThreadFromWait(Handle handle) { | ||||
|     u32 error; | ||||
|     Thread* t = Kernel::g_object_pool.Get<Thread>(handle, error); | ||||
|     if (t) { | ||||
|         t->status &= ~THREADSTATUS_WAIT; | ||||
|         if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | ||||
|             ChangeReadyState(t, true); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Creates a new thread
 | ||||
| Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, | ||||
|     s32 processor_id, u32 stack_top, int stack_size) { | ||||
| 
 | ||||
|     _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),  | ||||
|         "CreateThread priority=%d, outside of allowable range!", priority) | ||||
| 
 | ||||
|     Thread* t = new Thread; | ||||
|      | ||||
|     handle = Kernel::g_object_pool.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 CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, | ||||
|     u32 stack_top, int stack_size) { | ||||
|     if (name == NULL) { | ||||
|         ERROR_LOG(KERNEL, "CreateThread(): NULL name"); | ||||
|         return -1; | ||||
|     } | ||||
|     if ((u32)stack_size < 0x200) { | ||||
|         ERROR_LOG(KERNEL, "CreateThread(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, "CreateThread(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, "CreateThread(name=%s): invalid entry %08x", name, entry_point); | ||||
|         return -1; | ||||
|     } | ||||
|     Handle handle; | ||||
|     Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,  | ||||
|         stack_size); | ||||
| 
 | ||||
|     ResetThread(t, arg, 0); | ||||
| 
 | ||||
|     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"); | ||||
| 
 | ||||
|     CallThread(t); | ||||
|      | ||||
|     return handle; | ||||
| } | ||||
| 
 | ||||
| /// Sets up the primary application thread
 | ||||
| Handle SetupMainThread(s32 priority, int stack_size) { | ||||
|     Handle handle; | ||||
|      | ||||
|     // Initialize new "main" thread
 | ||||
|     Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,  | ||||
|         THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); | ||||
|      | ||||
|     ResetThread(t, 0, 0); | ||||
|      | ||||
|     // If running another thread already, set it to "ready" state
 | ||||
|     Thread* cur = GetCurrentThread(); | ||||
|     if (cur && cur->IsRunning()) { | ||||
|         ChangeReadyState(cur, true); | ||||
|     } | ||||
|      | ||||
|     // Run new "main" thread
 | ||||
|     SetCurrentThread(t); | ||||
|     t->status = THREADSTATUS_RUNNING; | ||||
|     LoadContext(t->context); | ||||
| 
 | ||||
|     return handle; | ||||
| } | ||||
| 
 | ||||
| /// Reschedules to the next available thread (call after current thread is suspended)
 | ||||
| void Reschedule() { | ||||
|     Thread* prev = GetCurrentThread(); | ||||
|     Thread* next = NextThread(); | ||||
|     if (next > 0) { | ||||
|         SwitchContext(next); | ||||
| 
 | ||||
|         // Hack - automatically change previous thread (which would have been in "wait" state) to
 | ||||
|         // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to
 | ||||
|         // actually wait for whatever event it is supposed to be waiting on.
 | ||||
|         ChangeReadyState(prev, true); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| void ThreadingInit() { | ||||
| } | ||||
| 
 | ||||
| void ThreadingShutdown() { | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
							
								
								
									
										74
									
								
								src/core/hle/kernel/thread.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/core/hle/kernel/thread.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | |||
| // 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" | ||||
| 
 | ||||
| enum ThreadPriority { | ||||
|     THREADPRIO_HIGHEST      = 0,    ///< Highest thread priority
 | ||||
|     THREADPRIO_DEFAULT      = 16,   ///< Default thread priority for userland apps
 | ||||
|     THREADPRIO_LOW          = 31,   ///< Low range of thread priority for userland apps
 | ||||
|     THREADPRIO_LOWEST       = 63,   ///< Thread priority max checked by svcCreateThread
 | ||||
| }; | ||||
| 
 | ||||
| enum ThreadProcessorId { | ||||
|     THREADPROCESSORID_0     = 0xFFFFFFFE,   ///< Enables core appcode
 | ||||
|     THREADPROCESSORID_1     = 0xFFFFFFFD,   ///< Enables core syscore
 | ||||
|     THREADPROCESSORID_ALL   = 0xFFFFFFFC,   ///< Enables both cores
 | ||||
| }; | ||||
| 
 | ||||
| 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, | ||||
| }; | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| /// Creates a new thread - wrapper for external user
 | ||||
| Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, | ||||
|     u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE); | ||||
| 
 | ||||
| /// Sets up the primary application thread
 | ||||
| Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); | ||||
| 
 | ||||
| /// Reschedules to the next available thread (call after current thread is suspended)
 | ||||
| void Reschedule(); | ||||
| 
 | ||||
| /// Puts the current thread in the wait state for the given type
 | ||||
| void WaitCurrentThread(WaitType wait_type); | ||||
| 
 | ||||
| /// Resumes a thread from waiting by marking it as "ready"
 | ||||
| void ResumeThreadFromWait(Handle handle); | ||||
| 
 | ||||
| /// Gets the current thread handle
 | ||||
| Handle GetCurrentThreadHandle(); | ||||
| 
 | ||||
| /// Put current thread in a wait state - on WaitSynchronization
 | ||||
| void WaitThread_Synchronization(); | ||||
| 
 | ||||
| /// Initialize threading
 | ||||
| void ThreadingInit(); | ||||
| 
 | ||||
| /// Shutdown threading
 | ||||
| void ThreadingShutdown(); | ||||
| 
 | ||||
| } // namespace
 | ||||
|  | @ -3,9 +3,10 @@ | |||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| 
 | ||||
| #include "common/log.h" | ||||
| #include "common/common.h" | ||||
| 
 | ||||
| #include "core/hle/hle.h" | ||||
| #include "core/hle/kernel/mutex.h" | ||||
| #include "core/hle/service/apt.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
|  | @ -19,7 +20,10 @@ 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(bunnei): Figure out the purpose of the flag field
 | ||||
|     cmd_buff[1] = 0; // No error
 | ||||
|     cmd_buff[5] = Kernel::CreateMutex(false); | ||||
|     DEBUG_LOG(KERNEL, "APT_U::GetLockHandle called : created handle 0x%08X", cmd_buff[5]); | ||||
| } | ||||
| 
 | ||||
| const Interface::FunctionInfo FunctionTable[] = { | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ public: | |||
|      * Gets the string port name used by CTROS for the service | ||||
|      * @return Port name of service | ||||
|      */ | ||||
|     std::string GetPortName() const { | ||||
|     const char *GetPortName() const { | ||||
|         return "APT:U"; | ||||
|     } | ||||
| }; | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ union GX_CmdBufferHeader { | |||
|     // <=15 when writing a command to shared memory. This is incremented by the application when 
 | ||||
|     // writing a command to shared memory, after increasing this value TriggerCmdReqQueue is only 
 | ||||
|     // used if this field is value 1.
 | ||||
|     BitField<8,8,u32>  number_commands; | ||||
|     BitField<8,8,u32>   number_commands; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  | @ -101,9 +101,7 @@ void RegisterInterruptRelayQueue(Service::Interface* self) { | |||
|     u32* cmd_buff = Service::GetCommandBuffer(); | ||||
|     u32 flags = cmd_buff[1]; | ||||
|     u32 event_handle = cmd_buff[3]; // TODO(bunnei): Implement event handling
 | ||||
|      | ||||
|     cmd_buff[2] = g_thread_id;          // ThreadID
 | ||||
|     cmd_buff[4] = self->NewHandle(); | ||||
| } | ||||
| 
 | ||||
| /// This triggers handling of the GX command written to the command buffer in shared memory.
 | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ public: | |||
|      * Gets the string port name used by CTROS for the service | ||||
|      * @return Port name of service | ||||
|      */ | ||||
|     std::string GetPortName() const { | ||||
|     const char *GetPortName() const { | ||||
|         return "gsp::Gpu"; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,7 +25,7 @@ public: | |||
|      * Gets the string port name used by CTROS for the service | ||||
|      * @return Port name of service | ||||
|      */ | ||||
|     std::string GetPortName() const { | ||||
|     const char *GetPortName() const { | ||||
|         return "hid:USER"; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,12 +7,15 @@ | |||
| #include "common/string_util.h" | ||||
| 
 | ||||
| #include "core/hle/hle.h" | ||||
| 
 | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/hle/service/apt.h" | ||||
| #include "core/hle/service/gsp.h" | ||||
| #include "core/hle/service/hid.h" | ||||
| #include "core/hle/service/srv.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| 
 | ||||
| namespace Service { | ||||
| 
 | ||||
| Manager* g_manager = NULL;  ///< Service manager
 | ||||
|  | @ -31,32 +34,21 @@ Manager::~Manager() { | |||
| 
 | ||||
| /// Add a service to the manager (does not create it though)
 | ||||
| void Manager::AddService(Interface* service) { | ||||
|     int index = m_services.size(); | ||||
|     u32 new_uid = GetUIDFromIndex(index); | ||||
| 
 | ||||
|     m_port_map[service->GetPortName()] = Kernel::g_object_pool.Create(service); | ||||
|     m_services.push_back(service); | ||||
| 
 | ||||
|     m_port_map[service->GetPortName()] = new_uid; | ||||
|     service->m_uid = new_uid; | ||||
| } | ||||
| 
 | ||||
| /// Removes a service from the manager, also frees memory
 | ||||
| void Manager::DeleteService(std::string port_name) { | ||||
|     auto service = FetchFromPortName(port_name); | ||||
| 
 | ||||
|     m_services.erase(m_services.begin() + GetIndexFromUID(service->m_uid)); | ||||
|     Interface* service = FetchFromPortName(port_name); | ||||
|     m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end()); | ||||
|     m_port_map.erase(port_name); | ||||
| 
 | ||||
|     delete service; | ||||
| } | ||||
| 
 | ||||
| /// Get a Service Interface from its UID
 | ||||
| Interface* Manager::FetchFromUID(u32 uid) { | ||||
|     int index = GetIndexFromUID(uid); | ||||
|     if (index < (int)m_services.size()) { | ||||
|         return m_services[index]; | ||||
|     } | ||||
|     return NULL; | ||||
| /// Get a Service Interface from its Handle
 | ||||
| Interface* Manager::FetchFromHandle(Handle handle) { | ||||
|     return Kernel::g_object_pool.GetFast<Interface>(handle); | ||||
| } | ||||
| 
 | ||||
| /// Get a Service Interface from its port
 | ||||
|  | @ -65,7 +57,7 @@ Interface* Manager::FetchFromPortName(std::string port_name) { | |||
|     if (itr == m_port_map.end()) { | ||||
|         return NULL; | ||||
|     } | ||||
|     return FetchFromUID(itr->second); | ||||
|     return FetchFromHandle(itr->second); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,22 +4,22 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <vector> | ||||
| #include <map> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/mem_map.h" | ||||
| #include "core/hle/syscall.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/svc.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Namespace Service
 | ||||
| 
 | ||||
| namespace Service { | ||||
| 
 | ||||
| typedef s32 NativeUID;                          ///< Native handle for a service
 | ||||
| 
 | ||||
| static const int kMaxPortSize           = 0x08; ///< Maximum size of a port name (8 characters)
 | ||||
| static const int kCommandHeaderOffset   = 0x80; ///< Offset into command buffer of header
 | ||||
| 
 | ||||
|  | @ -35,15 +35,15 @@ inline static u32* GetCommandBuffer(const int offset=0) { | |||
| class Manager; | ||||
| 
 | ||||
| /// Interface to a CTROS service
 | ||||
| class Interface : NonCopyable { | ||||
| class Interface  : public Kernel::Object { | ||||
|     friend class Manager; | ||||
| public: | ||||
|      | ||||
|     const char *GetName() { return GetPortName(); } | ||||
|     const char *GetTypeName() { return GetPortName(); } | ||||
| 
 | ||||
|     Interface() { | ||||
|     } | ||||
| 
 | ||||
|     virtual ~Interface() { | ||||
|     } | ||||
|     static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; } | ||||
|     Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Service; } | ||||
| 
 | ||||
|     typedef void (*Function)(Interface*); | ||||
| 
 | ||||
|  | @ -53,55 +53,44 @@ public: | |||
|         std::string name; | ||||
|     }; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets the UID for the serice | ||||
|      * @return UID of service in native format | ||||
|      */ | ||||
|     NativeUID GetUID() const { | ||||
|         return (NativeUID)m_uid; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets the string name used by CTROS for a service | ||||
|      * @return Port name of service | ||||
|      */ | ||||
|     virtual std::string GetPortName() const { | ||||
|     virtual const char *GetPortName() const { | ||||
|         return "[UNKNOWN SERVICE PORT]"; | ||||
|     } | ||||
| 
 | ||||
|     /// Allocates a new handle for the service
 | ||||
|     Syscall::Handle NewHandle() { | ||||
|         Syscall::Handle handle = (m_handles.size() << 16) | m_uid; | ||||
|     Handle CreateHandle(Kernel::Object *obj) { | ||||
|         Handle handle = Kernel::g_object_pool.Create(obj); | ||||
|         m_handles.push_back(handle); | ||||
|         return handle; | ||||
|     } | ||||
| 
 | ||||
|     /// Frees a handle from the service
 | ||||
|     void DeleteHandle(Syscall::Handle handle) { | ||||
|         for(auto iter = m_handles.begin(); iter != m_handles.end(); ++iter) { | ||||
|             if(*iter == handle) { | ||||
|                 m_handles.erase(iter); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     template <class T> | ||||
|     void DeleteHandle(const Handle handle) { | ||||
|         Kernel::g_object_pool.Destroy<T>(handle); | ||||
|         m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Called when svcSendSyncRequest is called, loads command buffer and executes comand | ||||
|      * @return Return result of svcSendSyncRequest passed back to user app | ||||
|      */ | ||||
|     Syscall::Result Sync() { | ||||
|     Result Sync() { | ||||
|         u32* cmd_buff = GetCommandBuffer(); | ||||
|         auto itr = m_functions.find(cmd_buff[0]); | ||||
| 
 | ||||
|         if (itr == m_functions.end()) { | ||||
|             ERROR_LOG(OSHLE, "Unknown/unimplemented function: port = %s, command = 0x%08X!",  | ||||
|                 GetPortName().c_str(), cmd_buff[0]); | ||||
|                 GetPortName(), cmd_buff[0]); | ||||
|             return -1; | ||||
|         } | ||||
|         if (itr->second.func == NULL) { | ||||
|             ERROR_LOG(OSHLE, "Unimplemented function: port = %s, name = %s!",  | ||||
|                 GetPortName().c_str(), itr->second.name.c_str()); | ||||
|                 GetPortName(), itr->second.name.c_str()); | ||||
|             return -1; | ||||
|         }  | ||||
| 
 | ||||
|  | @ -122,10 +111,10 @@ protected: | |||
|     } | ||||
| 
 | ||||
| private: | ||||
|     u32 m_uid; | ||||
|      | ||||
|     std::vector<Syscall::Handle>    m_handles; | ||||
|     std::map<u32, FunctionInfo>     m_functions; | ||||
| 
 | ||||
|     std::vector<Handle>         m_handles; | ||||
|     std::map<u32, FunctionInfo> m_functions; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| /// Simple class to manage accessing services from ports and UID handles
 | ||||
|  | @ -143,25 +132,16 @@ public: | |||
|     void DeleteService(std::string port_name); | ||||
| 
 | ||||
|     /// Get a Service Interface from its UID
 | ||||
|     Interface* FetchFromUID(u32 uid); | ||||
|     Interface* FetchFromHandle(u32 uid); | ||||
| 
 | ||||
|     /// Get a Service Interface from its port
 | ||||
|     Interface* FetchFromPortName(std::string port_name); | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     /// Convert an index into m_services vector into a UID
 | ||||
|     static u32 GetUIDFromIndex(const int index) { | ||||
|         return index | 0x10000000; | ||||
|     } | ||||
| 
 | ||||
|     /// Convert a UID into an index into m_services
 | ||||
|     static int GetIndexFromUID(const u32 uid) { | ||||
|         return uid & 0x0FFFFFFF; | ||||
|     } | ||||
| 
 | ||||
|     std::vector<Interface*>     m_services; | ||||
|     std::map<std::string, u32>  m_port_map; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| /// Initialize ServiceManager
 | ||||
|  |  | |||
|  | @ -16,18 +16,24 @@ 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; | ||||
|     Result res = 0; | ||||
|     u32* cmd_buff = Service::GetCommandBuffer(); | ||||
| 
 | ||||
|     std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); | ||||
|     Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); | ||||
| 
 | ||||
|     NOTICE_LOG(OSHLE, "SRV::Sync - GetHandle - port: %s, handle: 0x%08X", port_name.c_str(),  | ||||
|         service->GetUID()); | ||||
|         service->GetHandle()); | ||||
| 
 | ||||
|     if (NULL != service) { | ||||
|         cmd_buff[3] = service->GetUID(); | ||||
|         cmd_buff[3] = service->GetHandle(); | ||||
|     } else { | ||||
|         ERROR_LOG(OSHLE, "Service %s does not exist", port_name.c_str()); | ||||
|         res = -1; | ||||
|  | @ -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"}, | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ public: | |||
|      * Gets the string name used by CTROS for the service | ||||
|      * @return Port name of service | ||||
|      */ | ||||
|     std::string GetPortName() const { | ||||
|     const char *GetPortName() const { | ||||
|         return "srv:"; | ||||
|     } | ||||
| 
 | ||||
|  | @ -30,7 +30,7 @@ public: | |||
|      * Called when svcSendSyncRequest is called, loads command buffer and executes comand | ||||
|      * @return Return result of svcSendSyncRequest passed back to user app | ||||
|      */ | ||||
|     Syscall::Result Sync(); | ||||
|     Result Sync(); | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,17 +3,25 @@ | |||
| // Refer to the license.txt file included.  
 | ||||
| 
 | ||||
| #include <map> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "common/symbols.h" | ||||
| 
 | ||||
| #include "core/mem_map.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/mutex.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| 
 | ||||
| #include "core/hle/function_wrappers.h" | ||||
| #include "core/hle/syscall.h" | ||||
| #include "core/hle/svc.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Namespace Syscall
 | ||||
| // Namespace SVC
 | ||||
| 
 | ||||
| namespace Syscall { | ||||
| namespace SVC { | ||||
| 
 | ||||
| enum ControlMemoryOperation { | ||||
|     MEMORY_OPERATION_HEAP       = 0x00000003, | ||||
|  | @ -26,7 +34,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 +57,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; | ||||
|  | @ -72,17 +83,20 @@ Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherper | |||
| 
 | ||||
| /// Connect to an OS service given the port name, returns the handle to the port to out
 | ||||
| Result ConnectToPort(void* out, const char* port_name) { | ||||
|      | ||||
|     Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); | ||||
|     Core::g_app_core->SetReg(1, service->GetUID()); | ||||
|     if (service) { | ||||
|         Core::g_app_core->SetReg(1, service->GetHandle()); | ||||
|     } else { | ||||
|         PanicYesNo("ConnectToPort called port_name=%s, but it is not implemented!", port_name); | ||||
|     } | ||||
|     DEBUG_LOG(SVC, "ConnectToPort called port_name=%s", port_name); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /// Synchronize to an OS service
 | ||||
| Result SendSyncRequest(Handle session) { | ||||
|     DEBUG_LOG(SVC, "SendSyncRequest called session=0x%08X"); | ||||
|     Service::Interface* service = Service::g_manager->FetchFromUID(session); | ||||
| Result SendSyncRequest(Handle handle) { | ||||
|     DEBUG_LOG(SVC, "SendSyncRequest called handle=0x%08X"); | ||||
|     Service::Interface* service = Service::g_manager->FetchFromHandle(handle); | ||||
|     service->Sync(); | ||||
|     return 0; | ||||
| } | ||||
|  | @ -95,10 +109,25 @@ Result CloseHandle(Handle handle) { | |||
| } | ||||
| 
 | ||||
| /// Wait for a handle to synchronize, timeout after the specified nanoseconds
 | ||||
| Result WaitSynchronization1(Handle handle, s64 nanoseconds) { | ||||
|     // ImplementMe
 | ||||
| Result WaitSynchronization1(Handle handle, s64 nano_seconds) { | ||||
|     DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronization1 called handle=0x%08X, nanoseconds=%d",  | ||||
|         handle, nanoseconds); | ||||
|         handle, nano_seconds); | ||||
|     Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /// Wait for the given handles to synchronize, timeout after the specified nanoseconds
 | ||||
| Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wait_all, s64 nano_seconds) { | ||||
|     s32* out = (s32*)_out; | ||||
|     Handle* handles = (Handle*)_handles; | ||||
| 
 | ||||
|     DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronizationN called handle_count=%d, wait_all=%s, nanoseconds=%d %s",  | ||||
|         handle_count, (wait_all ? "true" : "false"), nano_seconds); | ||||
| 
 | ||||
|     for (u32 i = 0; i < handle_count; i++) { | ||||
|         DEBUG_LOG(SVC, "\thandle[%d]=0x%08X", i, handles[i]); | ||||
|     } | ||||
|     Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -106,7 +135,7 @@ Result WaitSynchronization1(Handle handle, s64 nanoseconds) { | |||
| Result CreateAddressArbiter(void* arbiter) { | ||||
|     // ImplementMe
 | ||||
|     DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateAddressArbiter called"); | ||||
|     Core::g_app_core->SetReg(1, 0xDEADBEEF); | ||||
|     Core::g_app_core->SetReg(1, 0xFABBDADD); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -134,16 +163,79 @@ Result GetResourceLimitCurrentValues(void* _values, Handle resource_limit, void* | |||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| const HLE::FunctionDef Syscall_Table[] = { | ||||
| /// Creates a new thread
 | ||||
| Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { | ||||
|     std::string name; | ||||
|     if (Symbols::HasSymbol(entry_point)) { | ||||
|         TSymbol symbol = Symbols::GetSymbol(entry_point); | ||||
|         name = symbol.name; | ||||
|     } else { | ||||
|         char buff[100]; | ||||
|         sprintf(buff, "%s", "unknown-%08X", entry_point); | ||||
|         name = buff; | ||||
|     } | ||||
| 
 | ||||
|     Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, arg, processor_id, | ||||
|         stack_top); | ||||
| 
 | ||||
|     Core::g_app_core->SetReg(1, thread); | ||||
| 
 | ||||
|     DEBUG_LOG(SVC, "CreateThread called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | ||||
|         "threadpriority=0x%08X, processorid=0x%08X : created handle 0x%08X", entry_point,  | ||||
|         name.c_str(), arg, stack_top, priority, processor_id, thread); | ||||
|      | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /// Create a mutex
 | ||||
| Result CreateMutex(void* _mutex, u32 initial_locked) { | ||||
|     Handle* mutex = (Handle*)_mutex; | ||||
|     *mutex = Kernel::CreateMutex((initial_locked != 0)); | ||||
|     Core::g_app_core->SetReg(1, *mutex); | ||||
|     DEBUG_LOG(SVC, "CreateMutex called initial_locked=%s : created handle 0x%08X",  | ||||
|         initial_locked ? "true" : "false", *mutex); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /// Release a mutex
 | ||||
| Result ReleaseMutex(Handle handle) { | ||||
|     DEBUG_LOG(SVC, "ReleaseMutex called handle=0x%08X", handle); | ||||
|     Kernel::ReleaseMutex(handle); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /// Get current thread ID
 | ||||
| Result GetThreadId(void* thread_id, u32 thread) { | ||||
|     DEBUG_LOG(SVC, "(UNIMPLEMENTED) GetThreadId called thread=0x%08X", thread); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /// Query memory
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| /// Create an event
 | ||||
| Result CreateEvent(void* _event, u32 reset_type) { | ||||
|     Handle* event = (Handle*)_event; | ||||
|     DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateEvent called reset_type=0x%08X", reset_type); | ||||
|     Core::g_app_core->SetReg(1, 0xBADC0DE0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| const HLE::FunctionDef SVC_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_UUUUU<CreateThread>,                  "CreateThread"}, | ||||
|     {0x09,  NULL,                                       "ExitThread"}, | ||||
|     {0x0A,  NULL,                                       "SleepThread"}, | ||||
|     {0x0B,  NULL,                                       "GetThreadPriority"}, | ||||
|  | @ -154,11 +246,11 @@ 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"}, | ||||
|     {0x17,  WrapI_VU<CreateEvent>,                      "CreateEvent"}, | ||||
|     {0x18,  NULL,                                       "SignalEvent"}, | ||||
|     {0x19,  NULL,                                       "ClearEvent"}, | ||||
|     {0x1A,  NULL,                                       "CreateTimer"}, | ||||
|  | @ -172,7 +264,7 @@ const HLE::FunctionDef Syscall_Table[] = { | |||
|     {0x22,  NULL,                                       "ArbitrateAddress"}, | ||||
|     {0x23,  WrapI_U<CloseHandle>,                       "CloseHandle"}, | ||||
|     {0x24,  WrapI_US64<WaitSynchronization1>,           "WaitSynchronization1"}, | ||||
|     {0x25,  NULL,                                       "WaitSynchronizationN"}, | ||||
|     {0x25,  WrapI_VVUUS64<WaitSynchronizationN>,        "WaitSynchronizationN"}, | ||||
|     {0x26,  NULL,                                       "SignalAndWait"}, | ||||
|     {0x27,  NULL,                                       "DuplicateHandle"}, | ||||
|     {0x28,  NULL,                                       "GetSystemTick"}, | ||||
|  | @ -190,7 +282,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"}, | ||||
|  | @ -264,7 +356,7 @@ const HLE::FunctionDef Syscall_Table[] = { | |||
| }; | ||||
| 
 | ||||
| void Register() { | ||||
|     HLE::RegisterModule("SyscallTable", ARRAY_SIZE(Syscall_Table), Syscall_Table); | ||||
|     HLE::RegisterModule("SVC_Table", ARRAY_SIZE(SVC_Table), SVC_Table); | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
							
								
								
									
										48
									
								
								src/core/hle/svc.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/core/hle/svc.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.  
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // SVC types
 | ||||
| 
 | ||||
| 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; | ||||
| }; | ||||
| 
 | ||||
| enum ResetType { | ||||
|     RESETTYPE_ONESHOT, | ||||
|     RESETTYPE_STICKY, | ||||
|     RESETTYPE_PULSE, | ||||
|     RESETTYPE_MAX_BIT = (1u << 31), | ||||
| }; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Namespace SVC
 | ||||
| 
 | ||||
| namespace SVC { | ||||
| 
 | ||||
| void Register(); | ||||
| 
 | ||||
| } // namespace
 | ||||
|  | @ -1,19 +0,0 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.  
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Namespace Syscall
 | ||||
| 
 | ||||
| namespace Syscall { | ||||
| 
 | ||||
| typedef u32 Handle; | ||||
| typedef s32 Result; | ||||
| 
 | ||||
| void Register(); | ||||
| 
 | ||||
| } // namespace
 | ||||
|  | @ -11,6 +11,8 @@ | |||
| 
 | ||||
| #include "video_core/video_core.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/thread.h" | ||||
| 
 | ||||
| namespace LCD { | ||||
| 
 | ||||
| Registers g_regs; | ||||
|  | @ -130,9 +132,11 @@ template void Write<u8>(u32 addr, const u8 data); | |||
| void Update() { | ||||
|     u64 current_ticks = Core::g_app_core->GetTicks(); | ||||
| 
 | ||||
|     // Fake a vertical blank
 | ||||
|     if ((current_ticks - g_last_ticks) >= kFrameTicks) { | ||||
|         g_last_ticks = current_ticks; | ||||
|         VideoCore::g_renderer->SwapBuffers(); | ||||
|         Kernel::WaitCurrentThread(WAITTYPE_VBLANK); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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()); | ||||
|         Kernel::LoadExec(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); | ||||
|         Kernel::LoadExec(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); | ||||
|         Kernel::LoadExec(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; | ||||
|     } | ||||
|  |  | |||
|  | @ -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}, | ||||
| }; | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue