mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge branch 'threading'
This commit is contained in:
		
						commit
						32c314c992
					
				
					 44 changed files with 1559 additions and 193 deletions
				
			
		|  | @ -142,7 +142,7 @@ void GMainWindow::BootGame(const char* filename) | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnMenuLoadFile() | 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()) |     if (filename.size()) | ||||||
|        BootGame(filename.toLatin1().data()); |        BootGame(filename.toLatin1().data()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -190,6 +190,7 @@ | ||||||
|     <ClInclude Include="swap.h" /> |     <ClInclude Include="swap.h" /> | ||||||
|     <ClInclude Include="symbols.h" /> |     <ClInclude Include="symbols.h" /> | ||||||
|     <ClInclude Include="thread.h" /> |     <ClInclude Include="thread.h" /> | ||||||
|  |     <ClInclude Include="thread_queue_list.h" /> | ||||||
|     <ClInclude Include="thunk.h" /> |     <ClInclude Include="thunk.h" /> | ||||||
|     <ClInclude Include="timer.h" /> |     <ClInclude Include="timer.h" /> | ||||||
|     <ClInclude Include="utf8.h" /> |     <ClInclude Include="utf8.h" /> | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ | ||||||
|     <ClInclude Include="symbols.h" /> |     <ClInclude Include="symbols.h" /> | ||||||
|     <ClInclude Include="scm_rev.h" /> |     <ClInclude Include="scm_rev.h" /> | ||||||
|     <ClInclude Include="bit_field.h" /> |     <ClInclude Include="bit_field.h" /> | ||||||
|  |     <ClInclude Include="thread_queue_list.h" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClCompile Include="break_points.cpp" /> |     <ClCompile Include="break_points.cpp" /> | ||||||
|  |  | ||||||
|  | @ -22,6 +22,11 @@ template<> struct CompileTimeAssert<true> {}; | ||||||
| #define b32(x)  (b16(x) | (b16(x) >>16) ) | #define b32(x)  (b16(x) | (b16(x) >>16) ) | ||||||
| #define ROUND_UP_POW2(x)    (b32(x - 1) + 1) | #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])) | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) | ||||||
| 
 | 
 | ||||||
| #ifndef _WIN32 | #ifndef _WIN32 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,8 @@ | ||||||
| #ifndef _LOG_H_ | #ifndef _LOG_H_ | ||||||
| #define _LOG_H_ | #define _LOG_H_ | ||||||
| 
 | 
 | ||||||
|  | #define LOGGING | ||||||
|  | 
 | ||||||
| #define    NOTICE_LEVEL  1  // VERY important information that is NOT errors. Like startup and OSReports.
 | #define    NOTICE_LEVEL  1  // VERY important information that is NOT errors. Like startup and OSReports.
 | ||||||
| #define    ERROR_LEVEL   2  // Critical errors 
 | #define    ERROR_LEVEL   2  // Critical errors 
 | ||||||
| #define    WARNING_LEVEL 3  // Something is suspicious.
 | #define    WARNING_LEVEL 3  // Something is suspicious.
 | ||||||
|  | @ -53,7 +55,7 @@ enum LOG_TYPE { | ||||||
|     WII_IPC_ES, |     WII_IPC_ES, | ||||||
|     WII_IPC_FILEIO, |     WII_IPC_FILEIO, | ||||||
|     WII_IPC_HID, |     WII_IPC_HID, | ||||||
|     WII_IPC_HLE, |     KERNEL, | ||||||
|     SVC, |     SVC, | ||||||
|     NDMA, |     NDMA, | ||||||
|     HLE, |     HLE, | ||||||
|  |  | ||||||
|  | @ -60,13 +60,13 @@ LogManager::LogManager() | ||||||
|     m_Log[LogTypes::LOADER]             = new LogContainer("Loader",            "Loader"); |     m_Log[LogTypes::LOADER]             = new LogContainer("Loader",            "Loader"); | ||||||
|     m_Log[LogTypes::FILESYS]            = new LogContainer("FileSys",           "File System"); |     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_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_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_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::WII_IPC_FILEIO]     = new LogContainer("WII_IPC_FILEIO",    "WII IPC FILEIO"); | ||||||
|     m_Log[LogTypes::RENDER]             = new LogContainer("RENDER",            "RENDER"); |     m_Log[LogTypes::RENDER]             = new LogContainer("RENDER",            "RENDER"); | ||||||
|     m_Log[LogTypes::LCD]                = new LogContainer("LCD",               "LCD"); |     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::NDMA]               = new LogContainer("NDMA",              "NDMA"); | ||||||
|     m_Log[LogTypes::HLE]                = new LogContainer("HLE",               "High Level Emulation"); |     m_Log[LogTypes::HLE]                = new LogContainer("HLE",               "High Level Emulation"); | ||||||
|     m_Log[LogTypes::HW]                 = new LogContainer("HW",                "Hardware"); |     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/hle.cpp | ||||||
|             hle/config_mem.cpp |             hle/config_mem.cpp | ||||||
|             hle/coprocessor.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/apt.cpp | ||||||
|             hle/service/gsp.cpp |             hle/service/gsp.cpp | ||||||
|             hle/service/hid.cpp |             hle/service/hid.cpp | ||||||
|  |  | ||||||
|  | @ -7,11 +7,13 @@ | ||||||
| #include "common/common.h" | #include "common/common.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
|  | #include "core/hle/svc.h" | ||||||
|  | 
 | ||||||
| /// Generic ARM11 CPU interface
 | /// Generic ARM11 CPU interface
 | ||||||
| class ARM_Interface : NonCopyable { | class ARM_Interface : NonCopyable { | ||||||
| public: | public: | ||||||
|     ARM_Interface() { |     ARM_Interface() { | ||||||
|         m_num_instructions = 0; |         num_instructions = 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ~ARM_Interface() { |     ~ARM_Interface() { | ||||||
|  | @ -23,7 +25,7 @@ public: | ||||||
|      */ |      */ | ||||||
|     void Run(int num_instructions) { |     void Run(int num_instructions) { | ||||||
|         ExecuteInstructions(num_instructions); |         ExecuteInstructions(num_instructions); | ||||||
|         m_num_instructions += num_instructions; |         num_instructions += num_instructions; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Step CPU by one instruction
 |     /// Step CPU by one instruction
 | ||||||
|  | @ -63,15 +65,33 @@ public: | ||||||
|      */ |      */ | ||||||
|     virtual u32 GetCPSR() const = 0;   |     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 |      * Returns the number of clock ticks since the last rese | ||||||
|      * @return Returns number of clock ticks |      * @return Returns number of clock ticks | ||||||
|      */ |      */ | ||||||
|     virtual u64 GetTicks() const = 0; |     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() { |     u64 GetNumInstructions() { | ||||||
|         return m_num_instructions; |         return num_instructions; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|  | @ -84,6 +104,6 @@ protected: | ||||||
| 
 | 
 | ||||||
| private: | 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()  { | ARM_Interpreter::ARM_Interpreter()  { | ||||||
|     m_state = new ARMul_State; |     state = new ARMul_State; | ||||||
| 
 | 
 | ||||||
|     ARMul_EmulateInit(); |     ARMul_EmulateInit(); | ||||||
|     ARMul_NewState(m_state); |     ARMul_NewState(state); | ||||||
| 
 | 
 | ||||||
|     m_state->abort_model = 0; |     state->abort_model = 0; | ||||||
|     m_state->cpu = (cpu_config_t*)&s_arm11_cpu_info; |     state->cpu = (cpu_config_t*)&s_arm11_cpu_info; | ||||||
|     m_state->bigendSig = LOW; |     state->bigendSig = LOW; | ||||||
| 
 | 
 | ||||||
|     ARMul_SelectProcessor(m_state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop); |     ARMul_SelectProcessor(state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop); | ||||||
|     m_state->lateabtSig = LOW; |     state->lateabtSig = LOW; | ||||||
|     mmu_init(m_state); |     mmu_init(state); | ||||||
| 
 | 
 | ||||||
|     // Reset the core to initial state
 |     // Reset the core to initial state
 | ||||||
|     ARMul_Reset(m_state); |     ARMul_Reset(state); | ||||||
|     m_state->NextInstr = 0; |     state->NextInstr = 0; | ||||||
|     m_state->Emulate = 3; |     state->Emulate = 3; | ||||||
| 
 | 
 | ||||||
|     m_state->pc = m_state->Reg[15] = 0x00000000; |     state->pc = state->Reg[15] = 0x00000000; | ||||||
|     m_state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack
 |     state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ARM_Interpreter::~ARM_Interpreter() { | ARM_Interpreter::~ARM_Interpreter() { | ||||||
|     delete m_state; |     delete state; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -40,7 +40,7 @@ ARM_Interpreter::~ARM_Interpreter() { | ||||||
|  * @param addr Address to set PC to |  * @param addr Address to set PC to | ||||||
|  */ |  */ | ||||||
| void ARM_Interpreter::SetPC(u32 pc) { | 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 |  * @return Returns current PC | ||||||
|  */ |  */ | ||||||
| u32 ARM_Interpreter::GetPC() const { | 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 |  * @return Returns the value in the register | ||||||
|  */ |  */ | ||||||
| u32 ARM_Interpreter::GetReg(int index) const { | 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 |  * @param value Value to set register to | ||||||
|  */ |  */ | ||||||
| void ARM_Interpreter::SetReg(int index, u32 value) { | 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 |  * @return Returns the value of the CPSR register | ||||||
|  */ |  */ | ||||||
| u32 ARM_Interpreter::GetCPSR() const { | 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 |  * @return Returns number of clock ticks | ||||||
|  */ |  */ | ||||||
| u64 ARM_Interpreter::GetTicks() const { | 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 |  * @param num_instructions Number of instructions to executes | ||||||
|  */ |  */ | ||||||
| void ARM_Interpreter::ExecuteInstructions(int num_instructions) { | void ARM_Interpreter::ExecuteInstructions(int num_instructions) { | ||||||
|     m_state->NumInstrsToExecute = num_instructions; |     state->NumInstrsToExecute = num_instructions; | ||||||
|     ARMul_Emulate32(m_state); |     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; |     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 |      * Returns the number of clock ticks since the last reset | ||||||
|      * @return Returns number of clock ticks |      * @return Returns number of clock ticks | ||||||
|      */ |      */ | ||||||
|     u64 GetTicks() const; |     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: | protected: | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|  | @ -64,6 +82,6 @@ protected: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 
 | 
 | ||||||
|     ARMul_State* m_state; |     ARMul_State* state; | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -24,10 +24,6 @@ | ||||||
| 
 | 
 | ||||||
| #include "common/platform.h" | #include "common/platform.h" | ||||||
| 
 | 
 | ||||||
| #if EMU_PLATFORM == PLATFORM_WINDOWS |  | ||||||
| #include <windows.h> |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| //teawater add for arm2x86 2005.02.14-------------------------------------------
 | //teawater add for arm2x86 2005.02.14-------------------------------------------
 | ||||||
| // koodailar remove it for mingw 2005.12.18----------------
 | // koodailar remove it for mingw 2005.12.18----------------
 | ||||||
| //anthonylee modify it for portable 2007.01.30
 | //anthonylee modify it for portable 2007.01.30
 | ||||||
|  |  | ||||||
|  | @ -4478,8 +4478,7 @@ ARMul_Emulate26 (ARMul_State * state) | ||||||
|                                  isize) & |                                  isize) & | ||||||
|                                 R15PCBITS)); |                                 R15PCBITS)); | ||||||
| #endif | #endif | ||||||
|                     } |                     } else if (instr != 0xDEADC0DE) // thumbemu uses 0xDEADCODE for debugging to catch non updates 
 | ||||||
|                     else |  | ||||||
|                         ARMul_MCR (state, instr, |                         ARMul_MCR (state, instr, | ||||||
|                                DEST); |                                DEST); | ||||||
|                 } |                 } | ||||||
|  | @ -4549,7 +4548,7 @@ ARMul_Emulate26 (ARMul_State * state) | ||||||
|                 //    ARMul_OSHandleSWI (state, BITS (0, 23));
 |                 //    ARMul_OSHandleSWI (state, BITS (0, 23));
 | ||||||
|                 //    break;
 |                 //    break;
 | ||||||
|                 //}
 |                 //}
 | ||||||
|                 HLE::CallSyscall(instr); |                 HLE::CallSVC(instr); | ||||||
|                 ARMul_Abort (state, ARMul_SWIV); |                 ARMul_Abort (state, ARMul_SWIV); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -17,8 +17,11 @@ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #include "common/platform.h" | #include "common/platform.h" | ||||||
|  | 
 | ||||||
| #if EMU_PLATFORM == PLATFORM_LINUX | #if EMU_PLATFORM == PLATFORM_LINUX | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  | #elif EMU_PLATFORM == PLATFORM_WINDOWS | ||||||
|  | #include <windows.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include <math.h> | #include <math.h> | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ | ||||||
| #ifndef __VFP_H__ | #ifndef __VFP_H__ | ||||||
| #define __VFP_H__ | #define __VFP_H__ | ||||||
| 
 | 
 | ||||||
| #define DBG(...) DEBUG_LOG(ARM11, __VA_ARGS__) | #define DBG(...) //DEBUG_LOG(ARM11, __VA_ARGS__)
 | ||||||
| 
 | 
 | ||||||
| #define vfpdebug //printf
 | #define vfpdebug //printf
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,6 +12,8 @@ | ||||||
| #include "core/arm/disassembler/arm_disasm.h" | #include "core/arm/disassembler/arm_disasm.h" | ||||||
| #include "core/arm/interpreter/arm_interpreter.h" | #include "core/arm/interpreter/arm_interpreter.h" | ||||||
| 
 | 
 | ||||||
|  | #include "core/hle/kernel/thread.h" | ||||||
|  | 
 | ||||||
| namespace Core { | namespace Core { | ||||||
| 
 | 
 | ||||||
| ARM_Disasm*     g_disasm    = NULL; ///< ARM disassembler
 | 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
 | /// Run the core CPU loop
 | ||||||
| void RunLoop() { | void RunLoop() { | ||||||
|     for (;;){ |     for (;;){ | ||||||
|         g_app_core->Run(10000); |         g_app_core->Run(100); | ||||||
|         HW::Update(); |         HW::Update(); | ||||||
|  |         Kernel::Reschedule(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Step the CPU one instruction
 | /// Step the CPU one instruction
 | ||||||
| void SingleStep() { | void SingleStep() { | ||||||
|     g_app_core->Step(); |     g_app_core->Step(); | ||||||
|  |     HW::Update(); | ||||||
|  |     Kernel::Reschedule(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Halt the core
 | /// Halt the core
 | ||||||
|  |  | ||||||
|  | @ -168,12 +168,15 @@ | ||||||
|     <ClCompile Include="hle\config_mem.cpp" /> |     <ClCompile Include="hle\config_mem.cpp" /> | ||||||
|     <ClCompile Include="hle\coprocessor.cpp" /> |     <ClCompile Include="hle\coprocessor.cpp" /> | ||||||
|     <ClCompile Include="hle\hle.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\apt.cpp" /> | ||||||
|     <ClCompile Include="hle\service\gsp.cpp" /> |     <ClCompile Include="hle\service\gsp.cpp" /> | ||||||
|     <ClCompile Include="hle\service\hid.cpp" /> |     <ClCompile Include="hle\service\hid.cpp" /> | ||||||
|     <ClCompile Include="hle\service\service.cpp" /> |     <ClCompile Include="hle\service\service.cpp" /> | ||||||
|     <ClCompile Include="hle\service\srv.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\hw.cpp" /> | ||||||
|     <ClCompile Include="hw\lcd.cpp" /> |     <ClCompile Include="hw\lcd.cpp" /> | ||||||
|     <ClCompile Include="hw\ndma.cpp" /> |     <ClCompile Include="hw\ndma.cpp" /> | ||||||
|  | @ -214,12 +217,15 @@ | ||||||
|     <ClInclude Include="hle\coprocessor.h" /> |     <ClInclude Include="hle\coprocessor.h" /> | ||||||
|     <ClInclude Include="hle\function_wrappers.h" /> |     <ClInclude Include="hle\function_wrappers.h" /> | ||||||
|     <ClInclude Include="hle\hle.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\apt.h" /> | ||||||
|     <ClInclude Include="hle\service\gsp.h" /> |     <ClInclude Include="hle\service\gsp.h" /> | ||||||
|     <ClInclude Include="hle\service\hid.h" /> |     <ClInclude Include="hle\service\hid.h" /> | ||||||
|     <ClInclude Include="hle\service\service.h" /> |     <ClInclude Include="hle\service\service.h" /> | ||||||
|     <ClInclude Include="hle\service\srv.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\hw.h" /> | ||||||
|     <ClInclude Include="hw\lcd.h" /> |     <ClInclude Include="hw\lcd.h" /> | ||||||
|     <ClInclude Include="hw\ndma.h" /> |     <ClInclude Include="hw\ndma.h" /> | ||||||
|  |  | ||||||
|  | @ -31,6 +31,9 @@ | ||||||
|     <Filter Include="arm\interpreter\mmu"> |     <Filter Include="arm\interpreter\mmu"> | ||||||
|       <UniqueIdentifier>{13ef9860-2ba0-47e9-a93d-b4052adab269}</UniqueIdentifier> |       <UniqueIdentifier>{13ef9860-2ba0-47e9-a93d-b4052adab269}</UniqueIdentifier> | ||||||
|     </Filter> |     </Filter> | ||||||
|  |     <Filter Include="hle\kernel"> | ||||||
|  |       <UniqueIdentifier>{8089d94b-5faa-43dc-854b-ffd2fa2e7fe3}</UniqueIdentifier> | ||||||
|  |     </Filter> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClCompile Include="arm\disassembler\arm_disasm.cpp"> |     <ClCompile Include="arm\disassembler\arm_disasm.cpp"> | ||||||
|  | @ -81,9 +84,6 @@ | ||||||
|     <ClCompile Include="hle\hle.cpp"> |     <ClCompile Include="hle\hle.cpp"> | ||||||
|       <Filter>hle</Filter> |       <Filter>hle</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|     <ClCompile Include="hle\syscall.cpp"> |  | ||||||
|       <Filter>hle</Filter> |  | ||||||
|     </ClCompile> |  | ||||||
|     <ClCompile Include="hle\service\service.cpp"> |     <ClCompile Include="hle\service\service.cpp"> | ||||||
|       <Filter>hle\service</Filter> |       <Filter>hle\service</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  | @ -147,12 +147,24 @@ | ||||||
|     <ClCompile Include="arm\interpreter\mmu\wb.cpp"> |     <ClCompile Include="arm\interpreter\mmu\wb.cpp"> | ||||||
|       <Filter>arm\interpreter\mmu</Filter> |       <Filter>arm\interpreter\mmu</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|     <ClCompile Include="arm\interpreter\armcopro.cpp"> |  | ||||||
|       <Filter>arm</Filter> |  | ||||||
|     </ClCompile> |  | ||||||
|     <ClCompile Include="arm\interpreter\mmu\maverick.cpp"> |     <ClCompile Include="arm\interpreter\mmu\maverick.cpp"> | ||||||
|       <Filter>arm\interpreter\mmu</Filter> |       <Filter>arm\interpreter\mmu</Filter> | ||||||
|     </ClCompile> |     </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> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClInclude Include="arm\disassembler\arm_disasm.h"> |     <ClInclude Include="arm\disassembler\arm_disasm.h"> | ||||||
|  | @ -217,9 +229,6 @@ | ||||||
|     <ClInclude Include="hle\service\service.h"> |     <ClInclude Include="hle\service\service.h"> | ||||||
|       <Filter>hle\service</Filter> |       <Filter>hle\service</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|     <ClInclude Include="hle\syscall.h"> |  | ||||||
|       <Filter>hle</Filter> |  | ||||||
|     </ClInclude> |  | ||||||
|     <ClInclude Include="hle\service\apt.h"> |     <ClInclude Include="hle\service\apt.h"> | ||||||
|       <Filter>hle\service</Filter> |       <Filter>hle\service</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|  | @ -274,6 +283,18 @@ | ||||||
|     <ClInclude Include="arm\interpreter\mmu\sa_mmu.h"> |     <ClInclude Include="arm\interpreter\mmu\sa_mmu.h"> | ||||||
|       <Filter>arm\interpreter\mmu</Filter> |       <Filter>arm\interpreter\mmu</Filter> | ||||||
|     </ClInclude> |     </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> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Text Include="CMakeLists.txt" /> |     <Text Include="CMakeLists.txt" /> | ||||||
|  |  | ||||||
|  | @ -719,17 +719,27 @@ template<int func(void*, u32)> void WrapI_VU(){ | ||||||
|     RETURN(retval); |     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(){ | 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)); |     u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)), PARAM(3)); | ||||||
|     RETURN(retval); |     RETURN(retval); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template<int func(void*, u32, u32, u32, u32, u32)> void WrapI_VUUUUU(){ | 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); |     RETURN(retval); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template<int func(u32, s64)> void WrapI_US64() { | 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); |     RETURN(retval); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "core/mem_map.h" | #include "core/mem_map.h" | ||||||
| #include "core/hle/hle.h" | #include "core/hle/hle.h" | ||||||
| #include "core/hle/syscall.h" | #include "core/hle/svc.h" | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | @ -15,17 +15,17 @@ namespace HLE { | ||||||
| 
 | 
 | ||||||
| static std::vector<ModuleDef> g_module_db; | static std::vector<ModuleDef> g_module_db; | ||||||
| 
 | 
 | ||||||
| const FunctionDef* GetSyscallInfo(u32 opcode) { | const FunctionDef* GetSVCInfo(u32 opcode) { | ||||||
|     u32 func_num = opcode & 0xFFFFFF; // 8 bits
 |     u32 func_num = opcode & 0xFFFFFF; // 8 bits
 | ||||||
|     if (func_num > 0xFF) { |     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 NULL; | ||||||
|     } |     } | ||||||
|     return &g_module_db[0].func_table[func_num]; |     return &g_module_db[0].func_table[func_num]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CallSyscall(u32 opcode) { | void CallSVC(u32 opcode) { | ||||||
|     const FunctionDef *info = GetSyscallInfo(opcode); |     const FunctionDef *info = GetSVCInfo(opcode); | ||||||
| 
 | 
 | ||||||
|     if (!info) { |     if (!info) { | ||||||
|         return; |         return; | ||||||
|  | @ -33,17 +33,28 @@ void CallSyscall(u32 opcode) { | ||||||
|     if (info->func) { |     if (info->func) { | ||||||
|         info->func(); |         info->func(); | ||||||
|     } else { |     } 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) { | void RegisterModule(std::string name, int num_functions, const FunctionDef* func_table) { | ||||||
|     ModuleDef module = {name, num_functions, func_table}; |     ModuleDef module = {name, num_functions, func_table}; | ||||||
|     g_module_db.push_back(module); |     g_module_db.push_back(module); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RegisterAllModules() { | void RegisterAllModules() { | ||||||
|     Syscall::Register(); |     SVC::Register(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Init() { | void Init() { | ||||||
|  |  | ||||||
|  | @ -34,7 +34,11 @@ struct ModuleDef { | ||||||
| 
 | 
 | ||||||
| void RegisterModule(std::string name, int num_functions, const FunctionDef *func_table); | 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(); | 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.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #include "common/log.h" | #include "common/common.h" | ||||||
| 
 | 
 | ||||||
| #include "core/hle/hle.h" | #include "core/hle/hle.h" | ||||||
|  | #include "core/hle/kernel/mutex.h" | ||||||
| #include "core/hle/service/apt.h" | #include "core/hle/service/apt.h" | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | @ -19,7 +20,10 @@ void Initialize(Service::Interface* self) { | ||||||
| 
 | 
 | ||||||
| void GetLockHandle(Service::Interface* self) { | void GetLockHandle(Service::Interface* self) { | ||||||
|     u32* cmd_buff = Service::GetCommandBuffer(); |     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[] = { | const Interface::FunctionInfo FunctionTable[] = { | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ public: | ||||||
|      * Gets the string port name used by CTROS for the service |      * Gets the string port name used by CTROS for the service | ||||||
|      * @return Port name of service |      * @return Port name of service | ||||||
|      */ |      */ | ||||||
|     std::string GetPortName() const { |     const char *GetPortName() const { | ||||||
|         return "APT:U"; |         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 
 |     // <=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 
 |     // writing a command to shared memory, after increasing this value TriggerCmdReqQueue is only 
 | ||||||
|     // used if this field is value 1.
 |     // 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* cmd_buff = Service::GetCommandBuffer(); | ||||||
|     u32 flags = cmd_buff[1]; |     u32 flags = cmd_buff[1]; | ||||||
|     u32 event_handle = cmd_buff[3]; // TODO(bunnei): Implement event handling
 |     u32 event_handle = cmd_buff[3]; // TODO(bunnei): Implement event handling
 | ||||||
|      |  | ||||||
|     cmd_buff[2] = g_thread_id;          // ThreadID
 |     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.
 | /// 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 |      * Gets the string port name used by CTROS for the service | ||||||
|      * @return Port name of service |      * @return Port name of service | ||||||
|      */ |      */ | ||||||
|     std::string GetPortName() const { |     const char *GetPortName() const { | ||||||
|         return "gsp::Gpu"; |         return "gsp::Gpu"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ public: | ||||||
|      * Gets the string port name used by CTROS for the service |      * Gets the string port name used by CTROS for the service | ||||||
|      * @return Port name of service |      * @return Port name of service | ||||||
|      */ |      */ | ||||||
|     std::string GetPortName() const { |     const char *GetPortName() const { | ||||||
|         return "hid:USER"; |         return "hid:USER"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,12 +7,15 @@ | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| 
 | 
 | ||||||
| #include "core/hle/hle.h" | #include "core/hle/hle.h" | ||||||
|  | 
 | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
| #include "core/hle/service/apt.h" | #include "core/hle/service/apt.h" | ||||||
| #include "core/hle/service/gsp.h" | #include "core/hle/service/gsp.h" | ||||||
| #include "core/hle/service/hid.h" | #include "core/hle/service/hid.h" | ||||||
| #include "core/hle/service/srv.h" | #include "core/hle/service/srv.h" | ||||||
| 
 | 
 | ||||||
|  | #include "core/hle/kernel/kernel.h" | ||||||
|  | 
 | ||||||
| namespace Service { | namespace Service { | ||||||
| 
 | 
 | ||||||
| Manager* g_manager = NULL;  ///< Service manager
 | Manager* g_manager = NULL;  ///< Service manager
 | ||||||
|  | @ -31,32 +34,21 @@ Manager::~Manager() { | ||||||
| 
 | 
 | ||||||
| /// Add a service to the manager (does not create it though)
 | /// Add a service to the manager (does not create it though)
 | ||||||
| void Manager::AddService(Interface* service) { | void Manager::AddService(Interface* service) { | ||||||
|     int index = m_services.size(); |     m_port_map[service->GetPortName()] = Kernel::g_object_pool.Create(service); | ||||||
|     u32 new_uid = GetUIDFromIndex(index); |  | ||||||
| 
 |  | ||||||
|     m_services.push_back(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
 | /// Removes a service from the manager, also frees memory
 | ||||||
| void Manager::DeleteService(std::string port_name) { | void Manager::DeleteService(std::string port_name) { | ||||||
|     auto service = FetchFromPortName(port_name); |     Interface* service = FetchFromPortName(port_name); | ||||||
| 
 |     m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end()); | ||||||
|     m_services.erase(m_services.begin() + GetIndexFromUID(service->m_uid)); |  | ||||||
|     m_port_map.erase(port_name); |     m_port_map.erase(port_name); | ||||||
| 
 |  | ||||||
|     delete service; |     delete service; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Get a Service Interface from its UID
 | /// Get a Service Interface from its Handle
 | ||||||
| Interface* Manager::FetchFromUID(u32 uid) { | Interface* Manager::FetchFromHandle(Handle handle) { | ||||||
|     int index = GetIndexFromUID(uid); |     return Kernel::g_object_pool.GetFast<Interface>(handle); | ||||||
|     if (index < (int)m_services.size()) { |  | ||||||
|         return m_services[index]; |  | ||||||
|     } |  | ||||||
|     return NULL; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Get a Service Interface from its port
 | /// Get a Service Interface from its port
 | ||||||
|  | @ -65,7 +57,7 @@ Interface* Manager::FetchFromPortName(std::string port_name) { | ||||||
|     if (itr == m_port_map.end()) { |     if (itr == m_port_map.end()) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     return FetchFromUID(itr->second); |     return FetchFromHandle(itr->second); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,22 +4,22 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <algorithm> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <map> | #include <map> | ||||||
| #include <string> | #include <string> | ||||||
| 
 | 
 | ||||||
| #include "common/common.h" | #include "common/common.h" | ||||||
| #include "common/common_types.h" |  | ||||||
| #include "core/mem_map.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
 | ||||||
| 
 | 
 | ||||||
| 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 kMaxPortSize           = 0x08; ///< Maximum size of a port name (8 characters)
 | ||||||
| static const int kCommandHeaderOffset   = 0x80; ///< Offset into command buffer of header
 | 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; | class Manager; | ||||||
| 
 | 
 | ||||||
| /// Interface to a CTROS service
 | /// Interface to a CTROS service
 | ||||||
| class Interface : NonCopyable { | class Interface  : public Kernel::Object { | ||||||
|     friend class Manager; |     friend class Manager; | ||||||
| public: | public: | ||||||
|      |      | ||||||
|     Interface() { |     const char *GetName() { return GetPortName(); } | ||||||
|     } |     const char *GetTypeName() { return GetPortName(); } | ||||||
| 
 | 
 | ||||||
|     virtual ~Interface() { |     static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; } | ||||||
|     } |     Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Service; } | ||||||
| 
 | 
 | ||||||
|     typedef void (*Function)(Interface*); |     typedef void (*Function)(Interface*); | ||||||
| 
 | 
 | ||||||
|  | @ -53,55 +53,44 @@ public: | ||||||
|         std::string name; |         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 |      * Gets the string name used by CTROS for a service | ||||||
|      * @return Port name of service |      * @return Port name of service | ||||||
|      */ |      */ | ||||||
|     virtual std::string GetPortName() const { |     virtual const char *GetPortName() const { | ||||||
|         return "[UNKNOWN SERVICE PORT]"; |         return "[UNKNOWN SERVICE PORT]"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Allocates a new handle for the service
 |     /// Allocates a new handle for the service
 | ||||||
|     Syscall::Handle NewHandle() { |     Handle CreateHandle(Kernel::Object *obj) { | ||||||
|         Syscall::Handle handle = (m_handles.size() << 16) | m_uid; |         Handle handle = Kernel::g_object_pool.Create(obj); | ||||||
|         m_handles.push_back(handle); |         m_handles.push_back(handle); | ||||||
|         return handle; |         return handle; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Frees a handle from the service
 |     /// Frees a handle from the service
 | ||||||
|     void DeleteHandle(Syscall::Handle handle) { |     template <class T> | ||||||
|         for(auto iter = m_handles.begin(); iter != m_handles.end(); ++iter) { |     void DeleteHandle(const Handle handle) { | ||||||
|             if(*iter == handle) { |         g_object_pool.Destroy<T>(handle); | ||||||
|                 m_handles.erase(iter); |         m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Called when svcSendSyncRequest is called, loads command buffer and executes comand |      * Called when svcSendSyncRequest is called, loads command buffer and executes comand | ||||||
|      * @return Return result of svcSendSyncRequest passed back to user app |      * @return Return result of svcSendSyncRequest passed back to user app | ||||||
|      */ |      */ | ||||||
|     Syscall::Result Sync() { |     Result Sync() { | ||||||
|         u32* cmd_buff = GetCommandBuffer(); |         u32* cmd_buff = GetCommandBuffer(); | ||||||
|         auto itr = m_functions.find(cmd_buff[0]); |         auto itr = m_functions.find(cmd_buff[0]); | ||||||
| 
 | 
 | ||||||
|         if (itr == m_functions.end()) { |         if (itr == m_functions.end()) { | ||||||
|             ERROR_LOG(OSHLE, "Unknown/unimplemented function: port = %s, command = 0x%08X!",  |             ERROR_LOG(OSHLE, "Unknown/unimplemented function: port = %s, command = 0x%08X!",  | ||||||
|                 GetPortName().c_str(), cmd_buff[0]); |                 GetPortName(), cmd_buff[0]); | ||||||
|             return -1; |             return -1; | ||||||
|         } |         } | ||||||
|         if (itr->second.func == NULL) { |         if (itr->second.func == NULL) { | ||||||
|             ERROR_LOG(OSHLE, "Unimplemented function: port = %s, name = %s!",  |             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; |             return -1; | ||||||
|         }  |         }  | ||||||
| 
 | 
 | ||||||
|  | @ -122,10 +111,10 @@ protected: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     u32 m_uid; |  | ||||||
| 
 | 
 | ||||||
|     std::vector<Syscall::Handle>    m_handles; |     std::vector<Handle>         m_handles; | ||||||
|     std::map<u32, FunctionInfo>     m_functions; |     std::map<u32, FunctionInfo> m_functions; | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Simple class to manage accessing services from ports and UID handles
 | /// Simple class to manage accessing services from ports and UID handles
 | ||||||
|  | @ -143,25 +132,16 @@ public: | ||||||
|     void DeleteService(std::string port_name); |     void DeleteService(std::string port_name); | ||||||
| 
 | 
 | ||||||
|     /// Get a Service Interface from its UID
 |     /// Get a Service Interface from its UID
 | ||||||
|     Interface* FetchFromUID(u32 uid); |     Interface* FetchFromHandle(u32 uid); | ||||||
| 
 | 
 | ||||||
|     /// Get a Service Interface from its port
 |     /// Get a Service Interface from its port
 | ||||||
|     Interface* FetchFromPortName(std::string port_name); |     Interface* FetchFromPortName(std::string port_name); | ||||||
| 
 | 
 | ||||||
| private: | 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::vector<Interface*>     m_services; | ||||||
|     std::map<std::string, u32>  m_port_map; |     std::map<std::string, u32>  m_port_map; | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Initialize ServiceManager
 | /// Initialize ServiceManager
 | ||||||
|  |  | ||||||
|  | @ -16,18 +16,24 @@ void Initialize(Service::Interface* self) { | ||||||
|     NOTICE_LOG(OSHLE, "SRV::Sync - Initialize"); |     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) { | void GetServiceHandle(Service::Interface* self) { | ||||||
|     Syscall::Result res = 0; |     Result res = 0; | ||||||
|     u32* cmd_buff = Service::GetCommandBuffer(); |     u32* cmd_buff = Service::GetCommandBuffer(); | ||||||
| 
 | 
 | ||||||
|     std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); |     std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); | ||||||
|     Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); |     Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); | ||||||
| 
 | 
 | ||||||
|     NOTICE_LOG(OSHLE, "SRV::Sync - GetHandle - port: %s, handle: 0x%08X", port_name.c_str(),  |     NOTICE_LOG(OSHLE, "SRV::Sync - GetHandle - port: %s, handle: 0x%08X", port_name.c_str(),  | ||||||
|         service->GetUID()); |         service->GetHandle()); | ||||||
| 
 | 
 | ||||||
|     if (NULL != service) { |     if (NULL != service) { | ||||||
|         cmd_buff[3] = service->GetUID(); |         cmd_buff[3] = service->GetHandle(); | ||||||
|     } else { |     } else { | ||||||
|         ERROR_LOG(OSHLE, "Service %s does not exist", port_name.c_str()); |         ERROR_LOG(OSHLE, "Service %s does not exist", port_name.c_str()); | ||||||
|         res = -1; |         res = -1; | ||||||
|  | @ -39,7 +45,7 @@ void GetServiceHandle(Service::Interface* self) { | ||||||
| 
 | 
 | ||||||
| const Interface::FunctionInfo FunctionTable[] = { | const Interface::FunctionInfo FunctionTable[] = { | ||||||
|     {0x00010002, Initialize,        "Initialize"}, |     {0x00010002, Initialize,        "Initialize"}, | ||||||
|     {0x00020000, NULL,              "GetProcSemaphore"}, |     {0x00020000, GetProcSemaphore,  "GetProcSemaphore"}, | ||||||
|     {0x00030100, NULL,              "RegisterService"}, |     {0x00030100, NULL,              "RegisterService"}, | ||||||
|     {0x000400C0, NULL,              "UnregisterService"}, |     {0x000400C0, NULL,              "UnregisterService"}, | ||||||
|     {0x00050100, GetServiceHandle,  "GetServiceHandle"}, |     {0x00050100, GetServiceHandle,  "GetServiceHandle"}, | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ public: | ||||||
|      * Gets the string name used by CTROS for the service |      * Gets the string name used by CTROS for the service | ||||||
|      * @return Port name of service |      * @return Port name of service | ||||||
|      */ |      */ | ||||||
|     std::string GetPortName() const { |     const char *GetPortName() const { | ||||||
|         return "srv:"; |         return "srv:"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -30,7 +30,7 @@ public: | ||||||
|      * Called when svcSendSyncRequest is called, loads command buffer and executes comand |      * Called when svcSendSyncRequest is called, loads command buffer and executes comand | ||||||
|      * @return Return result of svcSendSyncRequest passed back to user app |      * @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.  
 | // Refer to the license.txt file included.  
 | ||||||
| 
 | 
 | ||||||
| #include <map> | #include <map> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common/symbols.h" | ||||||
| 
 | 
 | ||||||
| #include "core/mem_map.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/function_wrappers.h" | ||||||
| #include "core/hle/syscall.h" | #include "core/hle/svc.h" | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
|  | #include "core/hle/kernel/thread.h" | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| // Namespace Syscall
 | // Namespace SVC
 | ||||||
| 
 | 
 | ||||||
| namespace Syscall { | namespace SVC { | ||||||
| 
 | 
 | ||||||
| enum ControlMemoryOperation { | enum ControlMemoryOperation { | ||||||
|     MEMORY_OPERATION_HEAP       = 0x00000003, |     MEMORY_OPERATION_HEAP       = 0x00000003, | ||||||
|  | @ -26,7 +34,8 @@ enum MapMemoryPermission { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Map application or GSP heap memory
 | /// 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; |     u32 virtual_address = 0x00000000; | ||||||
| 
 | 
 | ||||||
|     DEBUG_LOG(SVC, "ControlMemory called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",  |     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: |     default: | ||||||
|         ERROR_LOG(SVC, "ControlMemory unknown operation=0x%08X", operation); |         ERROR_LOG(SVC, "ControlMemory unknown operation=0x%08X", operation); | ||||||
|     } |     } | ||||||
| 
 |     if (NULL != outaddr) { | ||||||
|  |         *outaddr = virtual_address; | ||||||
|  |     } | ||||||
|     Core::g_app_core->SetReg(1, virtual_address); |     Core::g_app_core->SetReg(1, virtual_address); | ||||||
| 
 | 
 | ||||||
|     return 0; |     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
 | /// 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) { | Result ConnectToPort(void* out, const char* port_name) { | ||||||
|      |  | ||||||
|     Service::Interface* service = Service::g_manager->FetchFromPortName(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); |     DEBUG_LOG(SVC, "ConnectToPort called port_name=%s", port_name); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Synchronize to an OS service
 | /// Synchronize to an OS service
 | ||||||
| Result SendSyncRequest(Handle session) { | Result SendSyncRequest(Handle handle) { | ||||||
|     DEBUG_LOG(SVC, "SendSyncRequest called session=0x%08X"); |     DEBUG_LOG(SVC, "SendSyncRequest called handle=0x%08X"); | ||||||
|     Service::Interface* service = Service::g_manager->FetchFromUID(session); |     Service::Interface* service = Service::g_manager->FetchFromHandle(handle); | ||||||
|     service->Sync(); |     service->Sync(); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  | @ -95,10 +109,25 @@ Result CloseHandle(Handle handle) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Wait for a handle to synchronize, timeout after the specified nanoseconds
 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds
 | ||||||
| Result WaitSynchronization1(Handle handle, s64 nanoseconds) { | Result WaitSynchronization1(Handle handle, s64 nano_seconds) { | ||||||
|     // ImplementMe
 |  | ||||||
|     DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronization1 called handle=0x%08X, nanoseconds=%d",  |     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; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -106,7 +135,7 @@ Result WaitSynchronization1(Handle handle, s64 nanoseconds) { | ||||||
| Result CreateAddressArbiter(void* arbiter) { | Result CreateAddressArbiter(void* arbiter) { | ||||||
|     // ImplementMe
 |     // ImplementMe
 | ||||||
|     DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateAddressArbiter called"); |     DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateAddressArbiter called"); | ||||||
|     Core::g_app_core->SetReg(1, 0xDEADBEEF); |     Core::g_app_core->SetReg(1, 0xFABBDADD); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -134,16 +163,79 @@ Result GetResourceLimitCurrentValues(void* _values, Handle resource_limit, void* | ||||||
|     return 0; |     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"}, |     {0x00,  NULL,                                       "Unknown"}, | ||||||
|     {0x01,  WrapI_UUUUU<ControlMemory>,                 "ControlMemory"}, |     {0x01,  WrapI_VUUUUU<ControlMemory>,                "ControlMemory"}, | ||||||
|     {0x02,  NULL,                                       "QueryMemory"}, |     {0x02,  WrapI_VVU<QueryMemory>,                     "QueryMemory"}, | ||||||
|     {0x03,  NULL,                                       "ExitProcess"}, |     {0x03,  NULL,                                       "ExitProcess"}, | ||||||
|     {0x04,  NULL,                                       "GetProcessAffinityMask"}, |     {0x04,  NULL,                                       "GetProcessAffinityMask"}, | ||||||
|     {0x05,  NULL,                                       "SetProcessAffinityMask"}, |     {0x05,  NULL,                                       "SetProcessAffinityMask"}, | ||||||
|     {0x06,  NULL,                                       "GetProcessIdealProcessor"}, |     {0x06,  NULL,                                       "GetProcessIdealProcessor"}, | ||||||
|     {0x07,  NULL,                                       "SetProcessIdealProcessor"}, |     {0x07,  NULL,                                       "SetProcessIdealProcessor"}, | ||||||
|     {0x08,  NULL,                                       "CreateThread"}, |     {0x08,  WrapI_UUUUU<CreateThread>,                  "CreateThread"}, | ||||||
|     {0x09,  NULL,                                       "ExitThread"}, |     {0x09,  NULL,                                       "ExitThread"}, | ||||||
|     {0x0A,  NULL,                                       "SleepThread"}, |     {0x0A,  NULL,                                       "SleepThread"}, | ||||||
|     {0x0B,  NULL,                                       "GetThreadPriority"}, |     {0x0B,  NULL,                                       "GetThreadPriority"}, | ||||||
|  | @ -154,11 +246,11 @@ const HLE::FunctionDef Syscall_Table[] = { | ||||||
|     {0x10,  NULL,                                       "SetThreadIdealProcessor"}, |     {0x10,  NULL,                                       "SetThreadIdealProcessor"}, | ||||||
|     {0x11,  NULL,                                       "GetCurrentProcessorNumber"}, |     {0x11,  NULL,                                       "GetCurrentProcessorNumber"}, | ||||||
|     {0x12,  NULL,                                       "Run"}, |     {0x12,  NULL,                                       "Run"}, | ||||||
|     {0x13,  NULL,                                       "CreateMutex"}, |     {0x13,  WrapI_VU<CreateMutex>,                      "CreateMutex"}, | ||||||
|     {0x14,  NULL,                                       "ReleaseMutex"}, |     {0x14,  WrapI_U<ReleaseMutex>,                      "ReleaseMutex"}, | ||||||
|     {0x15,  NULL,                                       "CreateSemaphore"}, |     {0x15,  NULL,                                       "CreateSemaphore"}, | ||||||
|     {0x16,  NULL,                                       "ReleaseSemaphore"}, |     {0x16,  NULL,                                       "ReleaseSemaphore"}, | ||||||
|     {0x17,  NULL,                                       "CreateEvent"}, |     {0x17,  WrapI_VU<CreateEvent>,                      "CreateEvent"}, | ||||||
|     {0x18,  NULL,                                       "SignalEvent"}, |     {0x18,  NULL,                                       "SignalEvent"}, | ||||||
|     {0x19,  NULL,                                       "ClearEvent"}, |     {0x19,  NULL,                                       "ClearEvent"}, | ||||||
|     {0x1A,  NULL,                                       "CreateTimer"}, |     {0x1A,  NULL,                                       "CreateTimer"}, | ||||||
|  | @ -172,7 +264,7 @@ const HLE::FunctionDef Syscall_Table[] = { | ||||||
|     {0x22,  NULL,                                       "ArbitrateAddress"}, |     {0x22,  NULL,                                       "ArbitrateAddress"}, | ||||||
|     {0x23,  WrapI_U<CloseHandle>,                       "CloseHandle"}, |     {0x23,  WrapI_U<CloseHandle>,                       "CloseHandle"}, | ||||||
|     {0x24,  WrapI_US64<WaitSynchronization1>,           "WaitSynchronization1"}, |     {0x24,  WrapI_US64<WaitSynchronization1>,           "WaitSynchronization1"}, | ||||||
|     {0x25,  NULL,                                       "WaitSynchronizationN"}, |     {0x25,  WrapI_VVUUS64<WaitSynchronizationN>,        "WaitSynchronizationN"}, | ||||||
|     {0x26,  NULL,                                       "SignalAndWait"}, |     {0x26,  NULL,                                       "SignalAndWait"}, | ||||||
|     {0x27,  NULL,                                       "DuplicateHandle"}, |     {0x27,  NULL,                                       "DuplicateHandle"}, | ||||||
|     {0x28,  NULL,                                       "GetSystemTick"}, |     {0x28,  NULL,                                       "GetSystemTick"}, | ||||||
|  | @ -190,7 +282,7 @@ const HLE::FunctionDef Syscall_Table[] = { | ||||||
|     {0x34,  NULL,                                       "OpenThread"}, |     {0x34,  NULL,                                       "OpenThread"}, | ||||||
|     {0x35,  NULL,                                       "GetProcessId"}, |     {0x35,  NULL,                                       "GetProcessId"}, | ||||||
|     {0x36,  NULL,                                       "GetProcessIdOfThread"}, |     {0x36,  NULL,                                       "GetProcessIdOfThread"}, | ||||||
|     {0x37,  NULL,                                       "GetThreadId"}, |     {0x37,  WrapI_VU<GetThreadId>,                      "GetThreadId"}, | ||||||
|     {0x38,  WrapI_VU<GetResourceLimit>,                 "GetResourceLimit"}, |     {0x38,  WrapI_VU<GetResourceLimit>,                 "GetResourceLimit"}, | ||||||
|     {0x39,  NULL,                                       "GetResourceLimitLimitValues"}, |     {0x39,  NULL,                                       "GetResourceLimitLimitValues"}, | ||||||
|     {0x3A,  WrapI_VUVI<GetResourceLimitCurrentValues>,  "GetResourceLimitCurrentValues"}, |     {0x3A,  WrapI_VUVI<GetResourceLimitCurrentValues>,  "GetResourceLimitCurrentValues"}, | ||||||
|  | @ -264,7 +356,7 @@ const HLE::FunctionDef Syscall_Table[] = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void Register() { | void Register() { | ||||||
|     HLE::RegisterModule("SyscallTable", ARRAY_SIZE(Syscall_Table), Syscall_Table); |     HLE::RegisterModule("SVC_Table", ARRAY_SIZE(SVC_Table), SVC_Table); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace
 | } // 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 "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
|  | #include "core/hle/kernel/thread.h" | ||||||
|  | 
 | ||||||
| namespace LCD { | namespace LCD { | ||||||
| 
 | 
 | ||||||
| Registers g_regs; | Registers g_regs; | ||||||
|  | @ -130,9 +132,11 @@ template void Write<u8>(u32 addr, const u8 data); | ||||||
| void Update() { | void Update() { | ||||||
|     u64 current_ticks = Core::g_app_core->GetTicks(); |     u64 current_ticks = Core::g_app_core->GetTicks(); | ||||||
| 
 | 
 | ||||||
|  |     // Fake a vertical blank
 | ||||||
|     if ((current_ticks - g_last_ticks) >= kFrameTicks) { |     if ((current_ticks - g_last_ticks) >= kFrameTicks) { | ||||||
|         g_last_ticks = current_ticks; |         g_last_ticks = current_ticks; | ||||||
|         VideoCore::g_renderer->SwapBuffers(); |         VideoCore::g_renderer->SwapBuffers(); | ||||||
|  |         Kernel::WaitCurrentThread(WAITTYPE_VBLANK); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/file_sys/directory_file_system.h" | #include "core/file_sys/directory_file_system.h" | ||||||
| #include "core/elf/elf_reader.h" | #include "core/elf/elf_reader.h" | ||||||
| 
 | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/mem_map.h" | #include "core/mem_map.h" | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | @ -56,7 +56,7 @@ bool Load_ELF(std::string &filename) { | ||||||
|         elf_reader = new ElfReader(buffer); |         elf_reader = new ElfReader(buffer); | ||||||
|         elf_reader->LoadInto(0x00100000); |         elf_reader->LoadInto(0x00100000); | ||||||
| 
 | 
 | ||||||
|         Core::g_app_core->SetPC(elf_reader->GetEntryPoint()); |         Kernel::LoadExec(elf_reader->GetEntryPoint()); | ||||||
| 
 | 
 | ||||||
|         delete[] buffer; |         delete[] buffer; | ||||||
|         delete elf_reader; |         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 |         * but for the sake of making it easier... we'll temporarily/hackishly | ||||||
|         * allow it. No sense in making a proper reader for this. |         * 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; |         u32 payload_offset = 0xA150; | ||||||
|          |          | ||||||
|         const u8 *src = &buffer[payload_offset]; |         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 srcSize = size - payload_offset; //just load everything...
 | ||||||
|         u32 *s = (u32*)src; |         u32 *s = (u32*)src; | ||||||
|         u32 *d = (u32*)dst; |         u32 *d = (u32*)dst; | ||||||
|  | @ -102,7 +102,8 @@ bool Load_DAT(std::string &filename) { | ||||||
|             *d++ = (*s++); |             *d++ = (*s++); | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         Core::g_app_core->SetPC(entrypoint); |         Kernel::LoadExec(entry_point); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|         delete[] buffer; |         delete[] buffer; | ||||||
|     } |     } | ||||||
|  | @ -131,10 +132,10 @@ bool Load_BIN(std::string &filename) { | ||||||
| 
 | 
 | ||||||
|         f.ReadBytes(buffer, size); |         f.ReadBytes(buffer, size); | ||||||
| 
 | 
 | ||||||
|         u32 entrypoint = 0x00100000; // Hardcoded, read from exheader
 |         u32 entry_point = 0x00100000; // Hardcoded, read from exheader
 | ||||||
|          |          | ||||||
|         const u8 *src = buffer; |         const u8 *src = buffer; | ||||||
|         u8 *dst = Memory::GetPointer(entrypoint); |         u8 *dst = Memory::GetPointer(entry_point); | ||||||
|         u32 srcSize = size; |         u32 srcSize = size; | ||||||
|         u32 *s = (u32*)src; |         u32 *s = (u32*)src; | ||||||
|         u32 *d = (u32*)dst; |         u32 *d = (u32*)dst; | ||||||
|  | @ -143,7 +144,7 @@ bool Load_BIN(std::string &filename) { | ||||||
|             *d++ = (*s++); |             *d++ = (*s++); | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         Core::g_app_core->SetPC(entrypoint); |         Kernel::LoadExec(entry_point); | ||||||
| 
 | 
 | ||||||
|         delete[] buffer; |         delete[] buffer; | ||||||
|     } |     } | ||||||
|  | @ -186,6 +187,9 @@ FileType IdentifyFile(std::string &filename) { | ||||||
|     else if (!strcasecmp(extension.c_str(), ".elf")) { |     else if (!strcasecmp(extension.c_str(), ".elf")) { | ||||||
|         return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p
 |         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")) { |     else if (!strcasecmp(extension.c_str(), ".bin")) { | ||||||
|         return FILETYPE_CTR_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
 | MemArena g_arena;                               ///< The MemArena class
 | ||||||
| 
 | 
 | ||||||
| u8* g_exefs_code                = NULL;         ///< ExeFS:/.code is loaded here
 | 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                      = NULL;         ///< Application heap (main memory)
 | ||||||
| u8* g_heap_gsp                  = NULL;         ///< GSP heap (main memory)
 | u8* g_heap_gsp                  = NULL;         ///< GSP heap (main memory)
 | ||||||
| u8* g_vram                      = NULL;         ///< Video memory (VRAM) pointer
 | 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_uncached_bootrom          = NULL; | ||||||
| 
 | 
 | ||||||
| u8* g_physical_exefs_code       = NULL;         ///< Phsical ExeFS:/.code is loaded here
 | 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_fcram            = NULL;         ///< Main physical memory (FCRAM)
 | ||||||
| u8* g_physical_heap_gsp         = NULL;         ///< GSP heap physical memory
 | u8* g_physical_heap_gsp         = NULL;         ///< GSP heap physical memory
 | ||||||
| u8* g_physical_vram             = NULL;         ///< Video physical memory (VRAM)
 | 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_vram,       &g_physical_vram,       VRAM_VADDR,             VRAM_SIZE,          0}, | ||||||
|     {&g_heap,       &g_physical_fcram,      HEAP_VADDR,             HEAP_SIZE,          MV_IS_PRIMARY_RAM}, |     {&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_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_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}, |     {&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_VADDR_END    = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE), | ||||||
|     EXEFS_CODE_MASK         = 0x03FFFFFF, |     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_SIZE               = FCRAM_SIZE,   ///< Application heap size
 | ||||||
|     //HEAP_PADDR              = HEAP_GSP_SIZE,
 |     //HEAP_PADDR              = HEAP_GSP_SIZE,
 | ||||||
|     //HEAP_PADDR_END          = (HEAP_PADDR + HEAP_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_vram;          ///< Video memory (VRAM)
 | ||||||
| extern u8* g_shared_mem;    ///< Shared memory
 | extern u8* g_shared_mem;    ///< Shared memory
 | ||||||
| extern u8* g_kernel_mem;    ///< Kernel 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
 | extern u8* g_exefs_code;    ///< ExeFS:/.code is loaded here
 | ||||||
| 
 | 
 | ||||||
| void Init(); | 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)) { |     } else if ((vaddr >= SHARED_MEMORY_VADDR)  && (vaddr < SHARED_MEMORY_VADDR_END)) { | ||||||
|         var = *((const T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK]); |         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
 |     // Config memory
 | ||||||
|     } else if ((vaddr >= CONFIG_MEMORY_VADDR)  && (vaddr < CONFIG_MEMORY_VADDR_END)) { |     } else if ((vaddr >= CONFIG_MEMORY_VADDR)  && (vaddr < CONFIG_MEMORY_VADDR_END)) { | ||||||
|         ConfigMem::Read<T>(var, vaddr); |         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)) { |     } else if ((vaddr >= SHARED_MEMORY_VADDR)  && (vaddr < SHARED_MEMORY_VADDR_END)) { | ||||||
|         *(T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK] = data; |         *(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
 |     // VRAM
 | ||||||
|     } else if ((vaddr >= VRAM_VADDR)  && (vaddr < VRAM_VADDR_END)) { |     } else if ((vaddr >= VRAM_VADDR)  && (vaddr < VRAM_VADDR_END)) { | ||||||
|         *(T*)&g_vram[vaddr & VRAM_MASK] = data; |         *(T*)&g_vram[vaddr & VRAM_MASK] = data; | ||||||
|  | @ -153,9 +161,13 @@ u8 *GetPointer(const u32 addr) { | ||||||
|         return g_heap + (vaddr & HEAP_MASK); |         return g_heap + (vaddr & HEAP_MASK); | ||||||
| 
 | 
 | ||||||
|     // Shared memory
 |     // 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); |         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
 |     // VRAM
 | ||||||
|     } else if ((vaddr > VRAM_VADDR)  && (vaddr < VRAM_VADDR_END)) { |     } else if ((vaddr > VRAM_VADDR)  && (vaddr < VRAM_VADDR_END)) { | ||||||
|         return g_vram + (vaddr & VRAM_MASK); |         return g_vram + (vaddr & VRAM_MASK); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue