diff options
| author | 2014-05-26 20:52:00 -0400 | |
|---|---|---|
| committer | 2014-05-26 20:52:00 -0400 | |
| commit | 6448c2f30062c085330ff26a4812c9a91c7b492c (patch) | |
| tree | 386e32cf3ec053491fb8dfd8459a1c92553241d9 /src | |
| parent | Merge pull request #4 from archshift/patch-1 (diff) | |
| parent | service: fixed typo that MSVC did not catch as an error (diff) | |
| download | yuzu-6448c2f30062c085330ff26a4812c9a91c7b492c.tar.gz yuzu-6448c2f30062c085330ff26a4812c9a91c7b492c.tar.xz yuzu-6448c2f30062c085330ff26a4812c9a91c7b492c.zip | |
Merge pull request #9 from bunnei/master
Add initial kernel HLE, includes thread creation and context switching
Diffstat (limited to '')
45 files changed, 1571 insertions, 195 deletions
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 6f3bc6f84..5a8642d1b 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp | |||
| @@ -24,7 +24,14 @@ int __cdecl main(int argc, char **argv) { | |||
| 24 | 24 | ||
| 25 | System::Init(emu_window); | 25 | System::Init(emu_window); |
| 26 | 26 | ||
| 27 | std::string boot_filename = "homebrew.elf"; | 27 | std::string boot_filename; |
| 28 | |||
| 29 | if (argc < 2) { | ||
| 30 | ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); | ||
| 31 | } | ||
| 32 | else { | ||
| 33 | boot_filename = argv[1]; | ||
| 34 | } | ||
| 28 | std::string error_str; | 35 | std::string error_str; |
| 29 | 36 | ||
| 30 | bool res = Loader::LoadFile(boot_filename, &error_str); | 37 | bool res = Loader::LoadFile(boot_filename, &error_str); |
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 76e0c68c3..9be982909 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp | |||
| @@ -142,7 +142,7 @@ void GMainWindow::BootGame(const char* filename) | |||
| 142 | 142 | ||
| 143 | void GMainWindow::OnMenuLoadFile() | 143 | void GMainWindow::OnMenuLoadFile() |
| 144 | { | 144 | { |
| 145 | QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS homebrew (*.elf *.dat *.bin)")); | 145 | QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS homebrew (*.elf *.axf *.dat *.bin)")); |
| 146 | if (filename.size()) | 146 | if (filename.size()) |
| 147 | BootGame(filename.toLatin1().data()); | 147 | BootGame(filename.toLatin1().data()); |
| 148 | } | 148 | } |
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 5dc6ff790..86295a480 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj | |||
| @@ -190,6 +190,7 @@ | |||
| 190 | <ClInclude Include="swap.h" /> | 190 | <ClInclude Include="swap.h" /> |
| 191 | <ClInclude Include="symbols.h" /> | 191 | <ClInclude Include="symbols.h" /> |
| 192 | <ClInclude Include="thread.h" /> | 192 | <ClInclude Include="thread.h" /> |
| 193 | <ClInclude Include="thread_queue_list.h" /> | ||
| 193 | <ClInclude Include="thunk.h" /> | 194 | <ClInclude Include="thunk.h" /> |
| 194 | <ClInclude Include="timer.h" /> | 195 | <ClInclude Include="timer.h" /> |
| 195 | <ClInclude Include="utf8.h" /> | 196 | <ClInclude Include="utf8.h" /> |
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 268730228..84cfa8837 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters | |||
| @@ -40,6 +40,7 @@ | |||
| 40 | <ClInclude Include="symbols.h" /> | 40 | <ClInclude Include="symbols.h" /> |
| 41 | <ClInclude Include="scm_rev.h" /> | 41 | <ClInclude Include="scm_rev.h" /> |
| 42 | <ClInclude Include="bit_field.h" /> | 42 | <ClInclude Include="bit_field.h" /> |
| 43 | <ClInclude Include="thread_queue_list.h" /> | ||
| 43 | </ItemGroup> | 44 | </ItemGroup> |
| 44 | <ItemGroup> | 45 | <ItemGroup> |
| 45 | <ClCompile Include="break_points.cpp" /> | 46 | <ClCompile Include="break_points.cpp" /> |
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index f8d10eb3e..dca4dc47f 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -22,6 +22,11 @@ template<> struct CompileTimeAssert<true> {}; | |||
| 22 | #define b32(x) (b16(x) | (b16(x) >>16) ) | 22 | #define b32(x) (b16(x) | (b16(x) >>16) ) |
| 23 | #define ROUND_UP_POW2(x) (b32(x - 1) + 1) | 23 | #define ROUND_UP_POW2(x) (b32(x - 1) + 1) |
| 24 | 24 | ||
| 25 | #define MIN(a, b) ((a)<(b)?(a):(b)) | ||
| 26 | #define MAX(a, b) ((a)>(b)?(a):(b)) | ||
| 27 | |||
| 28 | #define CLAMP(x, min, max) (((x) > max) ? max : (((x) < min) ? min : (x))) | ||
| 29 | |||
| 25 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) | 30 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
| 26 | 31 | ||
| 27 | #ifndef _WIN32 | 32 | #ifndef _WIN32 |
diff --git a/src/common/log.h b/src/common/log.h index d95f51f56..8b39b03a1 100644 --- a/src/common/log.h +++ b/src/common/log.h | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #ifndef _LOG_H_ | 5 | #ifndef _LOG_H_ |
| 6 | #define _LOG_H_ | 6 | #define _LOG_H_ |
| 7 | 7 | ||
| 8 | #define LOGGING | ||
| 9 | |||
| 8 | #define NOTICE_LEVEL 1 // VERY important information that is NOT errors. Like startup and OSReports. | 10 | #define NOTICE_LEVEL 1 // VERY important information that is NOT errors. Like startup and OSReports. |
| 9 | #define ERROR_LEVEL 2 // Critical errors | 11 | #define ERROR_LEVEL 2 // Critical errors |
| 10 | #define WARNING_LEVEL 3 // Something is suspicious. | 12 | #define WARNING_LEVEL 3 // Something is suspicious. |
| @@ -53,7 +55,7 @@ enum LOG_TYPE { | |||
| 53 | WII_IPC_ES, | 55 | WII_IPC_ES, |
| 54 | WII_IPC_FILEIO, | 56 | WII_IPC_FILEIO, |
| 55 | WII_IPC_HID, | 57 | WII_IPC_HID, |
| 56 | WII_IPC_HLE, | 58 | KERNEL, |
| 57 | SVC, | 59 | SVC, |
| 58 | NDMA, | 60 | NDMA, |
| 59 | HLE, | 61 | HLE, |
diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp index 80fd473b9..146472888 100644 --- a/src/common/log_manager.cpp +++ b/src/common/log_manager.cpp | |||
| @@ -60,13 +60,13 @@ LogManager::LogManager() | |||
| 60 | m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader"); | 60 | m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader"); |
| 61 | m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System"); | 61 | m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System"); |
| 62 | m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID"); | 62 | m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID"); |
| 63 | m_Log[LogTypes::WII_IPC_HLE] = new LogContainer("WII_IPC_HLE", "WII IPC HLE"); | 63 | m_Log[LogTypes::KERNEL] = new LogContainer("KERNEL", "KERNEL HLE"); |
| 64 | m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD"); | 64 | m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD"); |
| 65 | m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES"); | 65 | m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES"); |
| 66 | m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO"); | 66 | m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO"); |
| 67 | m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER"); | 67 | m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER"); |
| 68 | m_Log[LogTypes::LCD] = new LogContainer("LCD", "LCD"); | 68 | m_Log[LogTypes::LCD] = new LogContainer("LCD", "LCD"); |
| 69 | m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call"); | 69 | m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE"); |
| 70 | m_Log[LogTypes::NDMA] = new LogContainer("NDMA", "NDMA"); | 70 | m_Log[LogTypes::NDMA] = new LogContainer("NDMA", "NDMA"); |
| 71 | m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation"); | 71 | m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation"); |
| 72 | m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware"); | 72 | m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware"); |
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h new file mode 100644 index 000000000..4a89572f6 --- /dev/null +++ b/src/common/thread_queue_list.h | |||
| @@ -0,0 +1,216 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common.h" | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | template<class IdType> | ||
| 12 | struct ThreadQueueList { | ||
| 13 | // Number of queues (number of priority levels starting at 0.) | ||
| 14 | static const int NUM_QUEUES = 128; | ||
| 15 | |||
| 16 | // Initial number of threads a single queue can handle. | ||
| 17 | static const int INITIAL_CAPACITY = 32; | ||
| 18 | |||
| 19 | struct Queue { | ||
| 20 | // Next ever-been-used queue (worse priority.) | ||
| 21 | Queue *next; | ||
| 22 | // First valid item in data. | ||
| 23 | int first; | ||
| 24 | // One after last valid item in data. | ||
| 25 | int end; | ||
| 26 | // A too-large array with room on the front and end. | ||
| 27 | IdType *data; | ||
| 28 | // Size of data array. | ||
| 29 | int capacity; | ||
| 30 | }; | ||
| 31 | |||
| 32 | ThreadQueueList() { | ||
| 33 | memset(queues, 0, sizeof(queues)); | ||
| 34 | first = invalid(); | ||
| 35 | } | ||
| 36 | |||
| 37 | ~ThreadQueueList() { | ||
| 38 | for (int i = 0; i < NUM_QUEUES; ++i) | ||
| 39 | { | ||
| 40 | if (queues[i].data != NULL) | ||
| 41 | free(queues[i].data); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | // Only for debugging, returns priority level. | ||
| 46 | int contains(const IdType uid) { | ||
| 47 | for (int i = 0; i < NUM_QUEUES; ++i) | ||
| 48 | { | ||
| 49 | if (queues[i].data == NULL) | ||
| 50 | continue; | ||
| 51 | |||
| 52 | Queue *cur = &queues[i]; | ||
| 53 | for (int j = cur->first; j < cur->end; ++j) | ||
| 54 | { | ||
| 55 | if (cur->data[j] == uid) | ||
| 56 | return i; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | return -1; | ||
| 61 | } | ||
| 62 | |||
| 63 | inline IdType pop_first() { | ||
| 64 | Queue *cur = first; | ||
| 65 | while (cur != invalid()) | ||
| 66 | { | ||
| 67 | if (cur->end - cur->first > 0) | ||
| 68 | return cur->data[cur->first++]; | ||
| 69 | cur = cur->next; | ||
| 70 | } | ||
| 71 | |||
| 72 | //_dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty."); | ||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | |||
| 76 | inline IdType pop_first_better(u32 priority) { | ||
| 77 | Queue *cur = first; | ||
| 78 | Queue *stop = &queues[priority]; | ||
| 79 | while (cur < stop) | ||
| 80 | { | ||
| 81 | if (cur->end - cur->first > 0) | ||
| 82 | return cur->data[cur->first++]; | ||
| 83 | cur = cur->next; | ||
| 84 | } | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | inline void push_front(u32 priority, const IdType threadID) { | ||
| 90 | Queue *cur = &queues[priority]; | ||
| 91 | cur->data[--cur->first] = threadID; | ||
| 92 | if (cur->first == 0) | ||
| 93 | rebalance(priority); | ||
| 94 | } | ||
| 95 | |||
| 96 | inline void push_back(u32 priority, const IdType threadID) { | ||
| 97 | Queue *cur = &queues[priority]; | ||
| 98 | cur->data[cur->end++] = threadID; | ||
| 99 | if (cur->end == cur->capacity) | ||
| 100 | rebalance(priority); | ||
| 101 | } | ||
| 102 | |||
| 103 | inline void remove(u32 priority, const IdType threadID) { | ||
| 104 | Queue *cur = &queues[priority]; | ||
| 105 | //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); | ||
| 106 | |||
| 107 | for (int i = cur->first; i < cur->end; ++i) | ||
| 108 | { | ||
| 109 | if (cur->data[i] == threadID) | ||
| 110 | { | ||
| 111 | int remaining = --cur->end - i; | ||
| 112 | if (remaining > 0) | ||
| 113 | memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(IdType)); | ||
| 114 | return; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | // Wasn't there. | ||
| 119 | } | ||
| 120 | |||
| 121 | inline void rotate(u32 priority) { | ||
| 122 | Queue *cur = &queues[priority]; | ||
| 123 | //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); | ||
| 124 | |||
| 125 | if (cur->end - cur->first > 1) | ||
| 126 | { | ||
| 127 | cur->data[cur->end++] = cur->data[cur->first++]; | ||
| 128 | if (cur->end == cur->capacity) | ||
| 129 | rebalance(priority); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | inline void clear() { | ||
| 134 | for (int i = 0; i < NUM_QUEUES; ++i) | ||
| 135 | { | ||
| 136 | if (queues[i].data != NULL) | ||
| 137 | free(queues[i].data); | ||
| 138 | } | ||
| 139 | memset(queues, 0, sizeof(queues)); | ||
| 140 | first = invalid(); | ||
| 141 | } | ||
| 142 | |||
| 143 | inline bool empty(u32 priority) const { | ||
| 144 | const Queue *cur = &queues[priority]; | ||
| 145 | return cur->first == cur->end; | ||
| 146 | } | ||
| 147 | |||
| 148 | inline void prepare(u32 priority) { | ||
| 149 | Queue *cur = &queues[priority]; | ||
| 150 | if (cur->next == NULL) | ||
| 151 | link(priority, INITIAL_CAPACITY); | ||
| 152 | } | ||
| 153 | |||
| 154 | private: | ||
| 155 | Queue *invalid() const { | ||
| 156 | return (Queue *) -1; | ||
| 157 | } | ||
| 158 | |||
| 159 | void link(u32 priority, int size) { | ||
| 160 | //_dbg_assert_msg_(SCEKERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once."); | ||
| 161 | |||
| 162 | if (size <= INITIAL_CAPACITY) | ||
| 163 | size = INITIAL_CAPACITY; | ||
| 164 | else | ||
| 165 | { | ||
| 166 | int goal = size; | ||
| 167 | size = INITIAL_CAPACITY; | ||
| 168 | while (size < goal) | ||
| 169 | size *= 2; | ||
| 170 | } | ||
| 171 | Queue *cur = &queues[priority]; | ||
| 172 | cur->data = (IdType *) malloc(sizeof(IdType) * size); | ||
| 173 | cur->capacity = size; | ||
| 174 | cur->first = size / 2; | ||
| 175 | cur->end = size / 2; | ||
| 176 | |||
| 177 | for (int i = (int) priority - 1; i >= 0; --i) | ||
| 178 | { | ||
| 179 | if (queues[i].next != NULL) | ||
| 180 | { | ||
| 181 | cur->next = queues[i].next; | ||
| 182 | queues[i].next = cur; | ||
| 183 | return; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | cur->next = first; | ||
| 188 | first = cur; | ||
| 189 | } | ||
| 190 | |||
| 191 | void rebalance(u32 priority) { | ||
| 192 | Queue *cur = &queues[priority]; | ||
| 193 | int size = cur->end - cur->first; | ||
| 194 | if (size >= cur->capacity - 2) { | ||
| 195 | IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType)); | ||
| 196 | if (new_data != NULL) { | ||
| 197 | cur->capacity *= 2; | ||
| 198 | cur->data = new_data; | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | int newFirst = (cur->capacity - size) / 2; | ||
| 203 | if (newFirst != cur->first) { | ||
| 204 | memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(IdType)); | ||
| 205 | cur->first = newFirst; | ||
| 206 | cur->end = newFirst + size; | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | // The first queue that's ever been used. | ||
| 211 | Queue *first; | ||
| 212 | // The priority level queues of thread ids. | ||
| 213 | Queue queues[NUM_QUEUES]; | ||
| 214 | }; | ||
| 215 | |||
| 216 | } // namespace | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 14c598bf3..4086b415b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -33,7 +33,10 @@ set(SRCS core.cpp | |||
| 33 | hle/hle.cpp | 33 | hle/hle.cpp |
| 34 | hle/config_mem.cpp | 34 | hle/config_mem.cpp |
| 35 | hle/coprocessor.cpp | 35 | hle/coprocessor.cpp |
| 36 | hle/syscall.cpp | 36 | hle/svc.cpp |
| 37 | hle/kernel/kernel.cpp | ||
| 38 | hle/kernel/mutex.cpp | ||
| 39 | hle/kernel/thread.cpp | ||
| 37 | hle/service/apt.cpp | 40 | hle/service/apt.cpp |
| 38 | hle/service/gsp.cpp | 41 | hle/service/gsp.cpp |
| 39 | hle/service/hid.cpp | 42 | hle/service/hid.cpp |
| @@ -75,7 +78,10 @@ set(HEADERS core.h | |||
| 75 | hle/config_mem.h | 78 | hle/config_mem.h |
| 76 | hle/coprocessor.h | 79 | hle/coprocessor.h |
| 77 | hle/hle.h | 80 | hle/hle.h |
| 78 | hle/syscall.h | 81 | hle/svc.h |
| 82 | hle/kernel/kernel.h | ||
| 83 | hle/kernel/mutex.h | ||
| 84 | hle/kernel/thread.h | ||
| 79 | hle/function_wrappers.h | 85 | hle/function_wrappers.h |
| 80 | hle/service/apt.h | 86 | hle/service/apt.h |
| 81 | hle/service/gsp.h | 87 | hle/service/gsp.h |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 9fdc7ba3c..b73786ccd 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -7,11 +7,13 @@ | |||
| 7 | #include "common/common.h" | 7 | #include "common/common.h" |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | 9 | ||
| 10 | #include "core/hle/svc.h" | ||
| 11 | |||
| 10 | /// Generic ARM11 CPU interface | 12 | /// Generic ARM11 CPU interface |
| 11 | class ARM_Interface : NonCopyable { | 13 | class ARM_Interface : NonCopyable { |
| 12 | public: | 14 | public: |
| 13 | ARM_Interface() { | 15 | ARM_Interface() { |
| 14 | m_num_instructions = 0; | 16 | num_instructions = 0; |
| 15 | } | 17 | } |
| 16 | 18 | ||
| 17 | ~ARM_Interface() { | 19 | ~ARM_Interface() { |
| @@ -23,7 +25,7 @@ public: | |||
| 23 | */ | 25 | */ |
| 24 | void Run(int num_instructions) { | 26 | void Run(int num_instructions) { |
| 25 | ExecuteInstructions(num_instructions); | 27 | ExecuteInstructions(num_instructions); |
| 26 | m_num_instructions += num_instructions; | 28 | num_instructions += num_instructions; |
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | /// Step CPU by one instruction | 31 | /// Step CPU by one instruction |
| @@ -64,14 +66,32 @@ public: | |||
| 64 | virtual u32 GetCPSR() const = 0; | 66 | virtual u32 GetCPSR() const = 0; |
| 65 | 67 | ||
| 66 | /** | 68 | /** |
| 69 | * Set the current CPSR register | ||
| 70 | * @param cpsr Value to set CPSR to | ||
| 71 | */ | ||
| 72 | virtual void SetCPSR(u32 cpsr) = 0; | ||
| 73 | |||
| 74 | /** | ||
| 67 | * Returns the number of clock ticks since the last rese | 75 | * Returns the number of clock ticks since the last rese |
| 68 | * @return Returns number of clock ticks | 76 | * @return Returns number of clock ticks |
| 69 | */ | 77 | */ |
| 70 | virtual u64 GetTicks() const = 0; | 78 | virtual u64 GetTicks() const = 0; |
| 71 | 79 | ||
| 72 | /// Getter for m_num_instructions | 80 | /** |
| 81 | * Saves the current CPU context | ||
| 82 | * @param ctx Thread context to save | ||
| 83 | */ | ||
| 84 | virtual void SaveContext(ThreadContext& ctx) = 0; | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Loads a CPU context | ||
| 88 | * @param ctx Thread context to load | ||
| 89 | */ | ||
| 90 | virtual void LoadContext(const ThreadContext& ctx) = 0; | ||
| 91 | |||
| 92 | /// Getter for num_instructions | ||
| 73 | u64 GetNumInstructions() { | 93 | u64 GetNumInstructions() { |
| 74 | return m_num_instructions; | 94 | return num_instructions; |
| 75 | } | 95 | } |
| 76 | 96 | ||
| 77 | protected: | 97 | protected: |
| @@ -84,6 +104,6 @@ protected: | |||
| 84 | 104 | ||
| 85 | private: | 105 | private: |
| 86 | 106 | ||
| 87 | u64 m_num_instructions; ///< Number of instructions executed | 107 | u64 num_instructions; ///< Number of instructions executed |
| 88 | 108 | ||
| 89 | }; | 109 | }; |
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp index 23d96d292..17f787b86 100644 --- a/src/core/arm/interpreter/arm_interpreter.cpp +++ b/src/core/arm/interpreter/arm_interpreter.cpp | |||
| @@ -9,30 +9,30 @@ const static cpu_config_t s_arm11_cpu_info = { | |||
| 9 | }; | 9 | }; |
| 10 | 10 | ||
| 11 | ARM_Interpreter::ARM_Interpreter() { | 11 | ARM_Interpreter::ARM_Interpreter() { |
| 12 | m_state = new ARMul_State; | 12 | state = new ARMul_State; |
| 13 | 13 | ||
| 14 | ARMul_EmulateInit(); | 14 | ARMul_EmulateInit(); |
| 15 | ARMul_NewState(m_state); | 15 | ARMul_NewState(state); |
| 16 | 16 | ||
| 17 | m_state->abort_model = 0; | 17 | state->abort_model = 0; |
| 18 | m_state->cpu = (cpu_config_t*)&s_arm11_cpu_info; | 18 | state->cpu = (cpu_config_t*)&s_arm11_cpu_info; |
| 19 | m_state->bigendSig = LOW; | 19 | state->bigendSig = LOW; |
| 20 | 20 | ||
| 21 | ARMul_SelectProcessor(m_state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop); | 21 | ARMul_SelectProcessor(state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop); |
| 22 | m_state->lateabtSig = LOW; | 22 | state->lateabtSig = LOW; |
| 23 | mmu_init(m_state); | 23 | mmu_init(state); |
| 24 | 24 | ||
| 25 | // Reset the core to initial state | 25 | // Reset the core to initial state |
| 26 | ARMul_Reset(m_state); | 26 | ARMul_Reset(state); |
| 27 | m_state->NextInstr = 0; | 27 | state->NextInstr = 0; |
| 28 | m_state->Emulate = 3; | 28 | state->Emulate = 3; |
| 29 | 29 | ||
| 30 | m_state->pc = m_state->Reg[15] = 0x00000000; | 30 | state->pc = state->Reg[15] = 0x00000000; |
| 31 | m_state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack | 31 | state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | ARM_Interpreter::~ARM_Interpreter() { | 34 | ARM_Interpreter::~ARM_Interpreter() { |
| 35 | delete m_state; | 35 | delete state; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | /** | 38 | /** |
| @@ -40,7 +40,7 @@ ARM_Interpreter::~ARM_Interpreter() { | |||
| 40 | * @param addr Address to set PC to | 40 | * @param addr Address to set PC to |
| 41 | */ | 41 | */ |
| 42 | void ARM_Interpreter::SetPC(u32 pc) { | 42 | void ARM_Interpreter::SetPC(u32 pc) { |
| 43 | m_state->pc = m_state->Reg[15] = pc; | 43 | state->pc = state->Reg[15] = pc; |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | /* | 46 | /* |
| @@ -48,7 +48,7 @@ void ARM_Interpreter::SetPC(u32 pc) { | |||
| 48 | * @return Returns current PC | 48 | * @return Returns current PC |
| 49 | */ | 49 | */ |
| 50 | u32 ARM_Interpreter::GetPC() const { | 50 | u32 ARM_Interpreter::GetPC() const { |
| 51 | return m_state->pc; | 51 | return state->pc; |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | /** | 54 | /** |
| @@ -57,7 +57,7 @@ u32 ARM_Interpreter::GetPC() const { | |||
| 57 | * @return Returns the value in the register | 57 | * @return Returns the value in the register |
| 58 | */ | 58 | */ |
| 59 | u32 ARM_Interpreter::GetReg(int index) const { | 59 | u32 ARM_Interpreter::GetReg(int index) const { |
| 60 | return m_state->Reg[index]; | 60 | return state->Reg[index]; |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | /** | 63 | /** |
| @@ -66,7 +66,7 @@ u32 ARM_Interpreter::GetReg(int index) const { | |||
| 66 | * @param value Value to set register to | 66 | * @param value Value to set register to |
| 67 | */ | 67 | */ |
| 68 | void ARM_Interpreter::SetReg(int index, u32 value) { | 68 | void ARM_Interpreter::SetReg(int index, u32 value) { |
| 69 | m_state->Reg[index] = value; | 69 | state->Reg[index] = value; |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | /** | 72 | /** |
| @@ -74,7 +74,15 @@ void ARM_Interpreter::SetReg(int index, u32 value) { | |||
| 74 | * @return Returns the value of the CPSR register | 74 | * @return Returns the value of the CPSR register |
| 75 | */ | 75 | */ |
| 76 | u32 ARM_Interpreter::GetCPSR() const { | 76 | u32 ARM_Interpreter::GetCPSR() const { |
| 77 | return m_state->Cpsr; | 77 | return state->Cpsr; |
| 78 | } | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Set the current CPSR register | ||
| 82 | * @param cpsr Value to set CPSR to | ||
| 83 | */ | ||
| 84 | void ARM_Interpreter::SetCPSR(u32 cpsr) { | ||
| 85 | state->Cpsr = cpsr; | ||
| 78 | } | 86 | } |
| 79 | 87 | ||
| 80 | /** | 88 | /** |
| @@ -82,7 +90,7 @@ u32 ARM_Interpreter::GetCPSR() const { | |||
| 82 | * @return Returns number of clock ticks | 90 | * @return Returns number of clock ticks |
| 83 | */ | 91 | */ |
| 84 | u64 ARM_Interpreter::GetTicks() const { | 92 | u64 ARM_Interpreter::GetTicks() const { |
| 85 | return ARMul_Time(m_state); | 93 | return ARMul_Time(state); |
| 86 | } | 94 | } |
| 87 | 95 | ||
| 88 | /** | 96 | /** |
| @@ -90,6 +98,45 @@ u64 ARM_Interpreter::GetTicks() const { | |||
| 90 | * @param num_instructions Number of instructions to executes | 98 | * @param num_instructions Number of instructions to executes |
| 91 | */ | 99 | */ |
| 92 | void ARM_Interpreter::ExecuteInstructions(int num_instructions) { | 100 | void ARM_Interpreter::ExecuteInstructions(int num_instructions) { |
| 93 | m_state->NumInstrsToExecute = num_instructions; | 101 | state->NumInstrsToExecute = num_instructions; |
| 94 | ARMul_Emulate32(m_state); | 102 | ARMul_Emulate32(state); |
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Saves the current CPU context | ||
| 107 | * @param ctx Thread context to save | ||
| 108 | * @todo Do we need to save Reg[15] and NextInstr? | ||
| 109 | */ | ||
| 110 | void ARM_Interpreter::SaveContext(ThreadContext& ctx) { | ||
| 111 | memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); | ||
| 112 | memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); | ||
| 113 | |||
| 114 | ctx.sp = state->Reg[13]; | ||
| 115 | ctx.lr = state->Reg[14]; | ||
| 116 | ctx.pc = state->pc; | ||
| 117 | ctx.cpsr = state->Cpsr; | ||
| 118 | |||
| 119 | ctx.fpscr = state->VFP[1]; | ||
| 120 | ctx.fpexc = state->VFP[2]; | ||
| 121 | } | ||
| 122 | |||
| 123 | /** | ||
| 124 | * Loads a CPU context | ||
| 125 | * @param ctx Thread context to load | ||
| 126 | * @param Do we need to load Reg[15] and NextInstr? | ||
| 127 | */ | ||
| 128 | void ARM_Interpreter::LoadContext(const ThreadContext& ctx) { | ||
| 129 | memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); | ||
| 130 | memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); | ||
| 131 | |||
| 132 | state->Reg[13] = ctx.sp; | ||
| 133 | state->Reg[14] = ctx.lr; | ||
| 134 | state->pc = ctx.pc; | ||
| 135 | state->Cpsr = ctx.cpsr; | ||
| 136 | |||
| 137 | state->VFP[1] = ctx.fpscr; | ||
| 138 | state->VFP[2] = ctx.fpexc; | ||
| 139 | |||
| 140 | state->Reg[15] = ctx.pc; | ||
| 141 | state->NextInstr = RESUME; | ||
| 95 | } | 142 | } |
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h index 509025080..6a531e497 100644 --- a/src/core/arm/interpreter/arm_interpreter.h +++ b/src/core/arm/interpreter/arm_interpreter.h | |||
| @@ -49,11 +49,29 @@ public: | |||
| 49 | u32 GetCPSR() const; | 49 | u32 GetCPSR() const; |
| 50 | 50 | ||
| 51 | /** | 51 | /** |
| 52 | * Set the current CPSR register | ||
| 53 | * @param cpsr Value to set CPSR to | ||
| 54 | */ | ||
| 55 | void SetCPSR(u32 cpsr); | ||
| 56 | |||
| 57 | /** | ||
| 52 | * Returns the number of clock ticks since the last reset | 58 | * Returns the number of clock ticks since the last reset |
| 53 | * @return Returns number of clock ticks | 59 | * @return Returns number of clock ticks |
| 54 | */ | 60 | */ |
| 55 | u64 GetTicks() const; | 61 | u64 GetTicks() const; |
| 56 | 62 | ||
| 63 | /** | ||
| 64 | * Saves the current CPU context | ||
| 65 | * @param ctx Thread context to save | ||
| 66 | */ | ||
| 67 | void SaveContext(ThreadContext& ctx); | ||
| 68 | |||
| 69 | /** | ||
| 70 | * Loads a CPU context | ||
| 71 | * @param ctx Thread context to load | ||
| 72 | */ | ||
| 73 | void LoadContext(const ThreadContext& ctx); | ||
| 74 | |||
| 57 | protected: | 75 | protected: |
| 58 | 76 | ||
| 59 | /** | 77 | /** |
| @@ -64,6 +82,6 @@ protected: | |||
| 64 | 82 | ||
| 65 | private: | 83 | private: |
| 66 | 84 | ||
| 67 | ARMul_State* m_state; | 85 | ARMul_State* state; |
| 68 | 86 | ||
| 69 | }; | 87 | }; |
diff --git a/src/core/arm/interpreter/armdefs.h b/src/core/arm/interpreter/armdefs.h index 5b2abc7f7..d8eae4d3f 100644 --- a/src/core/arm/interpreter/armdefs.h +++ b/src/core/arm/interpreter/armdefs.h | |||
| @@ -24,10 +24,6 @@ | |||
| 24 | 24 | ||
| 25 | #include "common/platform.h" | 25 | #include "common/platform.h" |
| 26 | 26 | ||
| 27 | #if EMU_PLATFORM == PLATFORM_WINDOWS | ||
| 28 | #include <windows.h> | ||
| 29 | #endif | ||
| 30 | |||
| 31 | //teawater add for arm2x86 2005.02.14------------------------------------------- | 27 | //teawater add for arm2x86 2005.02.14------------------------------------------- |
| 32 | // koodailar remove it for mingw 2005.12.18---------------- | 28 | // koodailar remove it for mingw 2005.12.18---------------- |
| 33 | //anthonylee modify it for portable 2007.01.30 | 29 | //anthonylee modify it for portable 2007.01.30 |
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp index 32e315f4b..e5dc7bd44 100644 --- a/src/core/arm/interpreter/armemu.cpp +++ b/src/core/arm/interpreter/armemu.cpp | |||
| @@ -4478,8 +4478,7 @@ ARMul_Emulate26 (ARMul_State * state) | |||
| 4478 | isize) & | 4478 | isize) & |
| 4479 | R15PCBITS)); | 4479 | R15PCBITS)); |
| 4480 | #endif | 4480 | #endif |
| 4481 | } | 4481 | } else if (instr != 0xDEADC0DE) // thumbemu uses 0xDEADCODE for debugging to catch non updates |
| 4482 | else | ||
| 4483 | ARMul_MCR (state, instr, | 4482 | ARMul_MCR (state, instr, |
| 4484 | DEST); | 4483 | DEST); |
| 4485 | } | 4484 | } |
| @@ -4549,7 +4548,7 @@ ARMul_Emulate26 (ARMul_State * state) | |||
| 4549 | // ARMul_OSHandleSWI (state, BITS (0, 23)); | 4548 | // ARMul_OSHandleSWI (state, BITS (0, 23)); |
| 4550 | // break; | 4549 | // break; |
| 4551 | //} | 4550 | //} |
| 4552 | HLE::CallSyscall(instr); | 4551 | HLE::CallSVC(instr); |
| 4553 | ARMul_Abort (state, ARMul_SWIV); | 4552 | ARMul_Abort (state, ARMul_SWIV); |
| 4554 | break; | 4553 | break; |
| 4555 | } | 4554 | } |
diff --git a/src/core/arm/interpreter/arminit.cpp b/src/core/arm/interpreter/arminit.cpp index 2c771cdda..e05667bea 100644 --- a/src/core/arm/interpreter/arminit.cpp +++ b/src/core/arm/interpreter/arminit.cpp | |||
| @@ -17,8 +17,11 @@ | |||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | #include "common/platform.h" | 19 | #include "common/platform.h" |
| 20 | |||
| 20 | #if EMU_PLATFORM == PLATFORM_LINUX | 21 | #if EMU_PLATFORM == PLATFORM_LINUX |
| 21 | #include <unistd.h> | 22 | #include <unistd.h> |
| 23 | #elif EMU_PLATFORM == PLATFORM_WINDOWS | ||
| 24 | #include <windows.h> | ||
| 22 | #endif | 25 | #endif |
| 23 | 26 | ||
| 24 | #include <math.h> | 27 | #include <math.h> |
diff --git a/src/core/arm/interpreter/vfp/vfp.h b/src/core/arm/interpreter/vfp/vfp.h index f738a615b..bbf4caeb0 100644 --- a/src/core/arm/interpreter/vfp/vfp.h +++ b/src/core/arm/interpreter/vfp/vfp.h | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | #ifndef __VFP_H__ | 21 | #ifndef __VFP_H__ |
| 22 | #define __VFP_H__ | 22 | #define __VFP_H__ |
| 23 | 23 | ||
| 24 | #define DBG(...) DEBUG_LOG(ARM11, __VA_ARGS__) | 24 | #define DBG(...) //DEBUG_LOG(ARM11, __VA_ARGS__) |
| 25 | 25 | ||
| 26 | #define vfpdebug //printf | 26 | #define vfpdebug //printf |
| 27 | 27 | ||
diff --git a/src/core/core.cpp b/src/core/core.cpp index 61c237b2c..f88bcd704 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -12,6 +12,8 @@ | |||
| 12 | #include "core/arm/disassembler/arm_disasm.h" | 12 | #include "core/arm/disassembler/arm_disasm.h" |
| 13 | #include "core/arm/interpreter/arm_interpreter.h" | 13 | #include "core/arm/interpreter/arm_interpreter.h" |
| 14 | 14 | ||
| 15 | #include "core/hle/kernel/thread.h" | ||
| 16 | |||
| 15 | namespace Core { | 17 | namespace Core { |
| 16 | 18 | ||
| 17 | ARM_Disasm* g_disasm = NULL; ///< ARM disassembler | 19 | ARM_Disasm* g_disasm = NULL; ///< ARM disassembler |
| @@ -21,14 +23,17 @@ ARM_Interface* g_sys_core = NULL; ///< ARM11 system (OS) core | |||
| 21 | /// Run the core CPU loop | 23 | /// Run the core CPU loop |
| 22 | void RunLoop() { | 24 | void RunLoop() { |
| 23 | for (;;){ | 25 | for (;;){ |
| 24 | g_app_core->Run(10000); | 26 | g_app_core->Run(100); |
| 25 | HW::Update(); | 27 | HW::Update(); |
| 28 | Kernel::Reschedule(); | ||
| 26 | } | 29 | } |
| 27 | } | 30 | } |
| 28 | 31 | ||
| 29 | /// Step the CPU one instruction | 32 | /// Step the CPU one instruction |
| 30 | void SingleStep() { | 33 | void SingleStep() { |
| 31 | g_app_core->Step(); | 34 | g_app_core->Step(); |
| 35 | HW::Update(); | ||
| 36 | Kernel::Reschedule(); | ||
| 32 | } | 37 | } |
| 33 | 38 | ||
| 34 | /// Halt the core | 39 | /// Halt the core |
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 41af5801d..f271d336e 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj | |||
| @@ -168,12 +168,15 @@ | |||
| 168 | <ClCompile Include="hle\config_mem.cpp" /> | 168 | <ClCompile Include="hle\config_mem.cpp" /> |
| 169 | <ClCompile Include="hle\coprocessor.cpp" /> | 169 | <ClCompile Include="hle\coprocessor.cpp" /> |
| 170 | <ClCompile Include="hle\hle.cpp" /> | 170 | <ClCompile Include="hle\hle.cpp" /> |
| 171 | <ClCompile Include="hle\kernel\kernel.cpp" /> | ||
| 172 | <ClCompile Include="hle\kernel\mutex.cpp" /> | ||
| 173 | <ClCompile Include="hle\kernel\thread.cpp" /> | ||
| 171 | <ClCompile Include="hle\service\apt.cpp" /> | 174 | <ClCompile Include="hle\service\apt.cpp" /> |
| 172 | <ClCompile Include="hle\service\gsp.cpp" /> | 175 | <ClCompile Include="hle\service\gsp.cpp" /> |
| 173 | <ClCompile Include="hle\service\hid.cpp" /> | 176 | <ClCompile Include="hle\service\hid.cpp" /> |
| 174 | <ClCompile Include="hle\service\service.cpp" /> | 177 | <ClCompile Include="hle\service\service.cpp" /> |
| 175 | <ClCompile Include="hle\service\srv.cpp" /> | 178 | <ClCompile Include="hle\service\srv.cpp" /> |
| 176 | <ClCompile Include="hle\syscall.cpp" /> | 179 | <ClCompile Include="hle\svc.cpp" /> |
| 177 | <ClCompile Include="hw\hw.cpp" /> | 180 | <ClCompile Include="hw\hw.cpp" /> |
| 178 | <ClCompile Include="hw\lcd.cpp" /> | 181 | <ClCompile Include="hw\lcd.cpp" /> |
| 179 | <ClCompile Include="hw\ndma.cpp" /> | 182 | <ClCompile Include="hw\ndma.cpp" /> |
| @@ -214,12 +217,15 @@ | |||
| 214 | <ClInclude Include="hle\coprocessor.h" /> | 217 | <ClInclude Include="hle\coprocessor.h" /> |
| 215 | <ClInclude Include="hle\function_wrappers.h" /> | 218 | <ClInclude Include="hle\function_wrappers.h" /> |
| 216 | <ClInclude Include="hle\hle.h" /> | 219 | <ClInclude Include="hle\hle.h" /> |
| 220 | <ClInclude Include="hle\kernel\kernel.h" /> | ||
| 221 | <ClInclude Include="hle\kernel\mutex.h" /> | ||
| 222 | <ClInclude Include="hle\kernel\thread.h" /> | ||
| 217 | <ClInclude Include="hle\service\apt.h" /> | 223 | <ClInclude Include="hle\service\apt.h" /> |
| 218 | <ClInclude Include="hle\service\gsp.h" /> | 224 | <ClInclude Include="hle\service\gsp.h" /> |
| 219 | <ClInclude Include="hle\service\hid.h" /> | 225 | <ClInclude Include="hle\service\hid.h" /> |
| 220 | <ClInclude Include="hle\service\service.h" /> | 226 | <ClInclude Include="hle\service\service.h" /> |
| 221 | <ClInclude Include="hle\service\srv.h" /> | 227 | <ClInclude Include="hle\service\srv.h" /> |
| 222 | <ClInclude Include="hle\syscall.h" /> | 228 | <ClInclude Include="hle\svc.h" /> |
| 223 | <ClInclude Include="hw\hw.h" /> | 229 | <ClInclude Include="hw\hw.h" /> |
| 224 | <ClInclude Include="hw\lcd.h" /> | 230 | <ClInclude Include="hw\lcd.h" /> |
| 225 | <ClInclude Include="hw\ndma.h" /> | 231 | <ClInclude Include="hw\ndma.h" /> |
diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index edf34ce2f..b6c1d5b93 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters | |||
| @@ -31,6 +31,9 @@ | |||
| 31 | <Filter Include="arm\interpreter\mmu"> | 31 | <Filter Include="arm\interpreter\mmu"> |
| 32 | <UniqueIdentifier>{13ef9860-2ba0-47e9-a93d-b4052adab269}</UniqueIdentifier> | 32 | <UniqueIdentifier>{13ef9860-2ba0-47e9-a93d-b4052adab269}</UniqueIdentifier> |
| 33 | </Filter> | 33 | </Filter> |
| 34 | <Filter Include="hle\kernel"> | ||
| 35 | <UniqueIdentifier>{8089d94b-5faa-43dc-854b-ffd2fa2e7fe3}</UniqueIdentifier> | ||
| 36 | </Filter> | ||
| 34 | </ItemGroup> | 37 | </ItemGroup> |
| 35 | <ItemGroup> | 38 | <ItemGroup> |
| 36 | <ClCompile Include="arm\disassembler\arm_disasm.cpp"> | 39 | <ClCompile Include="arm\disassembler\arm_disasm.cpp"> |
| @@ -81,9 +84,6 @@ | |||
| 81 | <ClCompile Include="hle\hle.cpp"> | 84 | <ClCompile Include="hle\hle.cpp"> |
| 82 | <Filter>hle</Filter> | 85 | <Filter>hle</Filter> |
| 83 | </ClCompile> | 86 | </ClCompile> |
| 84 | <ClCompile Include="hle\syscall.cpp"> | ||
| 85 | <Filter>hle</Filter> | ||
| 86 | </ClCompile> | ||
| 87 | <ClCompile Include="hle\service\service.cpp"> | 87 | <ClCompile Include="hle\service\service.cpp"> |
| 88 | <Filter>hle\service</Filter> | 88 | <Filter>hle\service</Filter> |
| 89 | </ClCompile> | 89 | </ClCompile> |
| @@ -147,12 +147,24 @@ | |||
| 147 | <ClCompile Include="arm\interpreter\mmu\wb.cpp"> | 147 | <ClCompile Include="arm\interpreter\mmu\wb.cpp"> |
| 148 | <Filter>arm\interpreter\mmu</Filter> | 148 | <Filter>arm\interpreter\mmu</Filter> |
| 149 | </ClCompile> | 149 | </ClCompile> |
| 150 | <ClCompile Include="arm\interpreter\armcopro.cpp"> | ||
| 151 | <Filter>arm</Filter> | ||
| 152 | </ClCompile> | ||
| 153 | <ClCompile Include="arm\interpreter\mmu\maverick.cpp"> | 150 | <ClCompile Include="arm\interpreter\mmu\maverick.cpp"> |
| 154 | <Filter>arm\interpreter\mmu</Filter> | 151 | <Filter>arm\interpreter\mmu</Filter> |
| 155 | </ClCompile> | 152 | </ClCompile> |
| 153 | <ClCompile Include="hle\kernel\kernel.cpp"> | ||
| 154 | <Filter>hle\kernel</Filter> | ||
| 155 | </ClCompile> | ||
| 156 | <ClCompile Include="hle\kernel\thread.cpp"> | ||
| 157 | <Filter>hle\kernel</Filter> | ||
| 158 | </ClCompile> | ||
| 159 | <ClCompile Include="hle\svc.cpp"> | ||
| 160 | <Filter>hle</Filter> | ||
| 161 | </ClCompile> | ||
| 162 | <ClCompile Include="hle\kernel\mutex.cpp"> | ||
| 163 | <Filter>hle\kernel</Filter> | ||
| 164 | </ClCompile> | ||
| 165 | <ClCompile Include="arm\interpreter\armcopro.cpp"> | ||
| 166 | <Filter>arm\interpreter</Filter> | ||
| 167 | </ClCompile> | ||
| 156 | </ItemGroup> | 168 | </ItemGroup> |
| 157 | <ItemGroup> | 169 | <ItemGroup> |
| 158 | <ClInclude Include="arm\disassembler\arm_disasm.h"> | 170 | <ClInclude Include="arm\disassembler\arm_disasm.h"> |
| @@ -217,9 +229,6 @@ | |||
| 217 | <ClInclude Include="hle\service\service.h"> | 229 | <ClInclude Include="hle\service\service.h"> |
| 218 | <Filter>hle\service</Filter> | 230 | <Filter>hle\service</Filter> |
| 219 | </ClInclude> | 231 | </ClInclude> |
| 220 | <ClInclude Include="hle\syscall.h"> | ||
| 221 | <Filter>hle</Filter> | ||
| 222 | </ClInclude> | ||
| 223 | <ClInclude Include="hle\service\apt.h"> | 232 | <ClInclude Include="hle\service\apt.h"> |
| 224 | <Filter>hle\service</Filter> | 233 | <Filter>hle\service</Filter> |
| 225 | </ClInclude> | 234 | </ClInclude> |
| @@ -274,6 +283,18 @@ | |||
| 274 | <ClInclude Include="arm\interpreter\mmu\sa_mmu.h"> | 283 | <ClInclude Include="arm\interpreter\mmu\sa_mmu.h"> |
| 275 | <Filter>arm\interpreter\mmu</Filter> | 284 | <Filter>arm\interpreter\mmu</Filter> |
| 276 | </ClInclude> | 285 | </ClInclude> |
| 286 | <ClInclude Include="hle\kernel\kernel.h"> | ||
| 287 | <Filter>hle\kernel</Filter> | ||
| 288 | </ClInclude> | ||
| 289 | <ClInclude Include="hle\kernel\thread.h"> | ||
| 290 | <Filter>hle\kernel</Filter> | ||
| 291 | </ClInclude> | ||
| 292 | <ClInclude Include="hle\svc.h"> | ||
| 293 | <Filter>hle</Filter> | ||
| 294 | </ClInclude> | ||
| 295 | <ClInclude Include="hle\kernel\mutex.h"> | ||
| 296 | <Filter>hle\kernel</Filter> | ||
| 297 | </ClInclude> | ||
| 277 | </ItemGroup> | 298 | </ItemGroup> |
| 278 | <ItemGroup> | 299 | <ItemGroup> |
| 279 | <Text Include="CMakeLists.txt" /> | 300 | <Text Include="CMakeLists.txt" /> |
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index d934eafb4..801865d49 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h | |||
| @@ -719,17 +719,27 @@ template<int func(void*, u32)> void WrapI_VU(){ | |||
| 719 | RETURN(retval); | 719 | RETURN(retval); |
| 720 | } | 720 | } |
| 721 | 721 | ||
| 722 | template<int func(void*, void*, u32)> void WrapI_VVU(){ | ||
| 723 | u32 retval = func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2)); | ||
| 724 | RETURN(retval); | ||
| 725 | } | ||
| 726 | |||
| 722 | template<int func(void*, u32, void*, int)> void WrapI_VUVI(){ | 727 | template<int func(void*, u32, void*, int)> void WrapI_VUVI(){ |
| 723 | u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)), PARAM(3)); | 728 | u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)), PARAM(3)); |
| 724 | RETURN(retval); | 729 | RETURN(retval); |
| 725 | } | 730 | } |
| 726 | 731 | ||
| 727 | template<int func(void*, u32, u32, u32, u32, u32)> void WrapI_VUUUUU(){ | 732 | template<int func(void*, u32, u32, u32, u32, u32)> void WrapI_VUUUUU(){ |
| 728 | u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5)); | 733 | u32 retval = func(NULL, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); |
| 729 | RETURN(retval); | 734 | RETURN(retval); |
| 730 | } | 735 | } |
| 731 | 736 | ||
| 732 | template<int func(u32, s64)> void WrapI_US64() { | 737 | template<int func(u32, s64)> void WrapI_US64() { |
| 733 | int retval = func(PARAM(0), PARAM64(2)); | 738 | int retval = func(PARAM(0), PARAM64(1)); |
| 739 | RETURN(retval); | ||
| 740 | } | ||
| 741 | |||
| 742 | template<int func(void*, void*, u32, u32, s64)> void WrapI_VVUUS64() { | ||
| 743 | int retval = func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2), PARAM(3), PARAM(4)); | ||
| 734 | RETURN(retval); | 744 | RETURN(retval); |
| 735 | } | 745 | } |
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index be151665b..080c36abf 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "core/mem_map.h" | 7 | #include "core/mem_map.h" |
| 8 | #include "core/hle/hle.h" | 8 | #include "core/hle/hle.h" |
| 9 | #include "core/hle/syscall.h" | 9 | #include "core/hle/svc.h" |
| 10 | #include "core/hle/service/service.h" | 10 | #include "core/hle/service/service.h" |
| 11 | 11 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -15,17 +15,17 @@ namespace HLE { | |||
| 15 | 15 | ||
| 16 | static std::vector<ModuleDef> g_module_db; | 16 | static std::vector<ModuleDef> g_module_db; |
| 17 | 17 | ||
| 18 | const FunctionDef* GetSyscallInfo(u32 opcode) { | 18 | const FunctionDef* GetSVCInfo(u32 opcode) { |
| 19 | u32 func_num = opcode & 0xFFFFFF; // 8 bits | 19 | u32 func_num = opcode & 0xFFFFFF; // 8 bits |
| 20 | if (func_num > 0xFF) { | 20 | if (func_num > 0xFF) { |
| 21 | ERROR_LOG(HLE,"Unknown syscall: 0x%02X", func_num); | 21 | ERROR_LOG(HLE,"Unknown SVC: 0x%02X", func_num); |
| 22 | return NULL; | 22 | return NULL; |
| 23 | } | 23 | } |
| 24 | return &g_module_db[0].func_table[func_num]; | 24 | return &g_module_db[0].func_table[func_num]; |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | void CallSyscall(u32 opcode) { | 27 | void CallSVC(u32 opcode) { |
| 28 | const FunctionDef *info = GetSyscallInfo(opcode); | 28 | const FunctionDef *info = GetSVCInfo(opcode); |
| 29 | 29 | ||
| 30 | if (!info) { | 30 | if (!info) { |
| 31 | return; | 31 | return; |
| @@ -33,17 +33,28 @@ void CallSyscall(u32 opcode) { | |||
| 33 | if (info->func) { | 33 | if (info->func) { |
| 34 | info->func(); | 34 | info->func(); |
| 35 | } else { | 35 | } else { |
| 36 | ERROR_LOG(HLE, "Unimplemented SysCall function %s(..)", info->name.c_str()); | 36 | ERROR_LOG(HLE, "Unimplemented SVC function %s(..)", info->name.c_str()); |
| 37 | } | 37 | } |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | void EatCycles(u32 cycles) { | ||
| 41 | // TODO: ImplementMe | ||
| 42 | } | ||
| 43 | |||
| 44 | void ReSchedule(const char *reason) { | ||
| 45 | #ifdef _DEBUG | ||
| 46 | _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "ReSchedule: Invalid or too long reason."); | ||
| 47 | #endif | ||
| 48 | // TODO: ImplementMe | ||
| 49 | } | ||
| 50 | |||
| 40 | void RegisterModule(std::string name, int num_functions, const FunctionDef* func_table) { | 51 | void RegisterModule(std::string name, int num_functions, const FunctionDef* func_table) { |
| 41 | ModuleDef module = {name, num_functions, func_table}; | 52 | ModuleDef module = {name, num_functions, func_table}; |
| 42 | g_module_db.push_back(module); | 53 | g_module_db.push_back(module); |
| 43 | } | 54 | } |
| 44 | 55 | ||
| 45 | void RegisterAllModules() { | 56 | void RegisterAllModules() { |
| 46 | Syscall::Register(); | 57 | SVC::Register(); |
| 47 | } | 58 | } |
| 48 | 59 | ||
| 49 | void Init() { | 60 | void Init() { |
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h index 42f37e29c..c075147c3 100644 --- a/src/core/hle/hle.h +++ b/src/core/hle/hle.h | |||
| @@ -34,7 +34,11 @@ struct ModuleDef { | |||
| 34 | 34 | ||
| 35 | void RegisterModule(std::string name, int num_functions, const FunctionDef *func_table); | 35 | void RegisterModule(std::string name, int num_functions, const FunctionDef *func_table); |
| 36 | 36 | ||
| 37 | void CallSyscall(u32 opcode); | 37 | void CallSVC(u32 opcode); |
| 38 | |||
| 39 | void EatCycles(u32 cycles); | ||
| 40 | |||
| 41 | void ReSchedule(const char *reason); | ||
| 38 | 42 | ||
| 39 | void Init(); | 43 | void Init(); |
| 40 | 44 | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp new file mode 100644 index 000000000..de80de893 --- /dev/null +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -0,0 +1,158 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string.h> | ||
| 8 | |||
| 9 | #include "common/common.h" | ||
| 10 | |||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/hle/kernel/kernel.h" | ||
| 13 | #include "core/hle/kernel/thread.h" | ||
| 14 | |||
| 15 | namespace Kernel { | ||
| 16 | |||
| 17 | ObjectPool g_object_pool; | ||
| 18 | |||
| 19 | ObjectPool::ObjectPool() { | ||
| 20 | memset(occupied, 0, sizeof(bool) * MAX_COUNT); | ||
| 21 | next_id = INITIAL_NEXT_ID; | ||
| 22 | } | ||
| 23 | |||
| 24 | Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) { | ||
| 25 | if (range_top > MAX_COUNT) { | ||
| 26 | range_top = MAX_COUNT; | ||
| 27 | } | ||
| 28 | if (next_id >= range_bottom && next_id < range_top) { | ||
| 29 | range_bottom = next_id++; | ||
| 30 | } | ||
| 31 | for (int i = range_bottom; i < range_top; i++) { | ||
| 32 | if (!occupied[i]) { | ||
| 33 | occupied[i] = true; | ||
| 34 | pool[i] = obj; | ||
| 35 | pool[i]->handle = i + HANDLE_OFFSET; | ||
| 36 | return i + HANDLE_OFFSET; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use."); | ||
| 40 | return 0; | ||
| 41 | } | ||
| 42 | |||
| 43 | bool ObjectPool::IsValid(Handle handle) { | ||
| 44 | int index = handle - HANDLE_OFFSET; | ||
| 45 | if (index < 0) | ||
| 46 | return false; | ||
| 47 | if (index >= MAX_COUNT) | ||
| 48 | return false; | ||
| 49 | |||
| 50 | return occupied[index]; | ||
| 51 | } | ||
| 52 | |||
| 53 | void ObjectPool::Clear() { | ||
| 54 | for (int i = 0; i < MAX_COUNT; i++) { | ||
| 55 | //brutally clear everything, no validation | ||
| 56 | if (occupied[i]) | ||
| 57 | delete pool[i]; | ||
| 58 | occupied[i] = false; | ||
| 59 | } | ||
| 60 | memset(pool, 0, sizeof(Object*)*MAX_COUNT); | ||
| 61 | next_id = INITIAL_NEXT_ID; | ||
| 62 | } | ||
| 63 | |||
| 64 | Object* &ObjectPool::operator [](Handle handle) | ||
| 65 | { | ||
| 66 | _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); | ||
| 67 | return pool[handle - HANDLE_OFFSET]; | ||
| 68 | } | ||
| 69 | |||
| 70 | void ObjectPool::List() { | ||
| 71 | for (int i = 0; i < MAX_COUNT; i++) { | ||
| 72 | if (occupied[i]) { | ||
| 73 | if (pool[i]) { | ||
| 74 | INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName(), | ||
| 75 | pool[i]->GetName()); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | int ObjectPool::GetCount() { | ||
| 82 | int count = 0; | ||
| 83 | for (int i = 0; i < MAX_COUNT; i++) { | ||
| 84 | if (occupied[i]) | ||
| 85 | count++; | ||
| 86 | } | ||
| 87 | return count; | ||
| 88 | } | ||
| 89 | |||
| 90 | Object* ObjectPool::CreateByIDType(int type) { | ||
| 91 | // Used for save states. This is ugly, but what other way is there? | ||
| 92 | switch (type) { | ||
| 93 | //case SCE_KERNEL_TMID_Alarm: | ||
| 94 | // return __KernelAlarmObject(); | ||
| 95 | //case SCE_KERNEL_TMID_EventFlag: | ||
| 96 | // return __KernelEventFlagObject(); | ||
| 97 | //case SCE_KERNEL_TMID_Mbox: | ||
| 98 | // return __KernelMbxObject(); | ||
| 99 | //case SCE_KERNEL_TMID_Fpl: | ||
| 100 | // return __KernelMemoryFPLObject(); | ||
| 101 | //case SCE_KERNEL_TMID_Vpl: | ||
| 102 | // return __KernelMemoryVPLObject(); | ||
| 103 | //case PPSSPP_KERNEL_TMID_PMB: | ||
| 104 | // return __KernelMemoryPMBObject(); | ||
| 105 | //case PPSSPP_KERNEL_TMID_Module: | ||
| 106 | // return __KernelModuleObject(); | ||
| 107 | //case SCE_KERNEL_TMID_Mpipe: | ||
| 108 | // return __KernelMsgPipeObject(); | ||
| 109 | //case SCE_KERNEL_TMID_Mutex: | ||
| 110 | // return __KernelMutexObject(); | ||
| 111 | //case SCE_KERNEL_TMID_LwMutex: | ||
| 112 | // return __KernelLwMutexObject(); | ||
| 113 | //case SCE_KERNEL_TMID_Semaphore: | ||
| 114 | // return __KernelSemaphoreObject(); | ||
| 115 | //case SCE_KERNEL_TMID_Callback: | ||
| 116 | // return __KernelCallbackObject(); | ||
| 117 | //case SCE_KERNEL_TMID_Thread: | ||
| 118 | // return __KernelThreadObject(); | ||
| 119 | //case SCE_KERNEL_TMID_VTimer: | ||
| 120 | // return __KernelVTimerObject(); | ||
| 121 | //case SCE_KERNEL_TMID_Tlspl: | ||
| 122 | // return __KernelTlsplObject(); | ||
| 123 | //case PPSSPP_KERNEL_TMID_File: | ||
| 124 | // return __KernelFileNodeObject(); | ||
| 125 | //case PPSSPP_KERNEL_TMID_DirList: | ||
| 126 | // return __KernelDirListingObject(); | ||
| 127 | |||
| 128 | default: | ||
| 129 | ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type); | ||
| 130 | return NULL; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | void Init() { | ||
| 135 | Kernel::ThreadingInit(); | ||
| 136 | } | ||
| 137 | |||
| 138 | void Shutdown() { | ||
| 139 | Kernel::ThreadingShutdown(); | ||
| 140 | } | ||
| 141 | |||
| 142 | /** | ||
| 143 | * Loads executable stored at specified address | ||
| 144 | * @entry_point Entry point in memory of loaded executable | ||
| 145 | * @return True on success, otherwise false | ||
| 146 | */ | ||
| 147 | bool LoadExec(u32 entry_point) { | ||
| 148 | Init(); | ||
| 149 | |||
| 150 | Core::g_app_core->SetPC(entry_point); | ||
| 151 | |||
| 152 | // 0x30 is the typical main thread priority I've seen used so far | ||
| 153 | Handle thread = Kernel::SetupMainThread(0x30); | ||
| 154 | |||
| 155 | return true; | ||
| 156 | } | ||
| 157 | |||
| 158 | } // namespace | ||
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h new file mode 100644 index 000000000..7cd79c2c4 --- /dev/null +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -0,0 +1,154 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common.h" | ||
| 8 | |||
| 9 | typedef u32 Handle; | ||
| 10 | typedef s32 Result; | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | enum class HandleType : u32 { | ||
| 15 | Unknown = 0, | ||
| 16 | Port = 1, | ||
| 17 | Service = 2, | ||
| 18 | Event = 3, | ||
| 19 | Mutex = 4, | ||
| 20 | SharedMemory = 5, | ||
| 21 | Redirection = 6, | ||
| 22 | Thread = 7, | ||
| 23 | Process = 8, | ||
| 24 | Arbiter = 9, | ||
| 25 | File = 10, | ||
| 26 | Semaphore = 11, | ||
| 27 | }; | ||
| 28 | |||
| 29 | enum { | ||
| 30 | MAX_NAME_LENGTH = 0x100, | ||
| 31 | DEFAULT_STACK_SIZE = 0x4000, | ||
| 32 | }; | ||
| 33 | |||
| 34 | class ObjectPool; | ||
| 35 | |||
| 36 | class Object : NonCopyable { | ||
| 37 | friend class ObjectPool; | ||
| 38 | u32 handle; | ||
| 39 | public: | ||
| 40 | virtual ~Object() {} | ||
| 41 | Handle GetHandle() const { return handle; } | ||
| 42 | virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; } | ||
| 43 | virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; } | ||
| 44 | virtual Kernel::HandleType GetHandleType() const = 0; | ||
| 45 | }; | ||
| 46 | |||
| 47 | class ObjectPool : NonCopyable { | ||
| 48 | public: | ||
| 49 | ObjectPool(); | ||
| 50 | ~ObjectPool() {} | ||
| 51 | |||
| 52 | // Allocates a handle within the range and inserts the object into the map. | ||
| 53 | Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); | ||
| 54 | |||
| 55 | static Object* CreateByIDType(int type); | ||
| 56 | |||
| 57 | template <class T> | ||
| 58 | u32 Destroy(Handle handle) { | ||
| 59 | u32 error; | ||
| 60 | if (Get<T>(handle, error)) { | ||
| 61 | occupied[handle - HANDLE_OFFSET] = false; | ||
| 62 | delete pool[handle - HANDLE_OFFSET]; | ||
| 63 | } | ||
| 64 | return error; | ||
| 65 | }; | ||
| 66 | |||
| 67 | bool IsValid(Handle handle); | ||
| 68 | |||
| 69 | template <class T> | ||
| 70 | T* Get(Handle handle, u32& outError) { | ||
| 71 | if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { | ||
| 72 | // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP | ||
| 73 | if (handle != 0 && (u32)handle != 0x80020001) { | ||
| 74 | WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); | ||
| 75 | } | ||
| 76 | outError = 0;//T::GetMissingErrorCode(); | ||
| 77 | return 0; | ||
| 78 | } else { | ||
| 79 | // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally, | ||
| 80 | // it just acted as a static case and everything worked. This means that we will never | ||
| 81 | // see the Wrong type object error below, but we'll just have to live with that danger. | ||
| 82 | T* t = static_cast<T*>(pool[handle - HANDLE_OFFSET]); | ||
| 83 | if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) { | ||
| 84 | WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle); | ||
| 85 | outError = 0;//T::GetMissingErrorCode(); | ||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | outError = 0;//SCE_KERNEL_ERROR_OK; | ||
| 89 | return t; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | // ONLY use this when you know the handle is valid. | ||
| 94 | template <class T> | ||
| 95 | T *GetFast(Handle handle) { | ||
| 96 | const Handle realHandle = handle - HANDLE_OFFSET; | ||
| 97 | _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); | ||
| 98 | return static_cast<T*>(pool[realHandle]); | ||
| 99 | } | ||
| 100 | |||
| 101 | template <class T, typename ArgT> | ||
| 102 | void Iterate(bool func(T*, ArgT), ArgT arg) { | ||
| 103 | int type = T::GetStaticIDType(); | ||
| 104 | for (int i = 0; i < MAX_COUNT; i++) | ||
| 105 | { | ||
| 106 | if (!occupied[i]) | ||
| 107 | continue; | ||
| 108 | T* t = static_cast<T*>(pool[i]); | ||
| 109 | if (t->GetIDType() == type) { | ||
| 110 | if (!func(t, arg)) | ||
| 111 | break; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | bool GetIDType(Handle handle, HandleType* type) const { | ||
| 117 | if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || | ||
| 118 | !occupied[handle - HANDLE_OFFSET]) { | ||
| 119 | ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); | ||
| 120 | return false; | ||
| 121 | } | ||
| 122 | Object* t = pool[handle - HANDLE_OFFSET]; | ||
| 123 | *type = t->GetHandleType(); | ||
| 124 | return true; | ||
| 125 | } | ||
| 126 | |||
| 127 | Object* &operator [](Handle handle); | ||
| 128 | void List(); | ||
| 129 | void Clear(); | ||
| 130 | int GetCount(); | ||
| 131 | |||
| 132 | private: | ||
| 133 | |||
| 134 | enum { | ||
| 135 | MAX_COUNT = 0x1000, | ||
| 136 | HANDLE_OFFSET = 0x100, | ||
| 137 | INITIAL_NEXT_ID = 0x10, | ||
| 138 | }; | ||
| 139 | |||
| 140 | Object* pool[MAX_COUNT]; | ||
| 141 | bool occupied[MAX_COUNT]; | ||
| 142 | int next_id; | ||
| 143 | }; | ||
| 144 | |||
| 145 | extern ObjectPool g_object_pool; | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Loads executable stored at specified address | ||
| 149 | * @entry_point Entry point in memory of loaded executable | ||
| 150 | * @return True on success, otherwise false | ||
| 151 | */ | ||
| 152 | bool LoadExec(u32 entry_point); | ||
| 153 | |||
| 154 | } // namespace | ||
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp new file mode 100644 index 000000000..019efbc78 --- /dev/null +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <map> | ||
| 6 | #include <vector> | ||
| 7 | |||
| 8 | #include "common/common.h" | ||
| 9 | |||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/kernel/thread.h" | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | |||
| 15 | class Mutex : public Object { | ||
| 16 | public: | ||
| 17 | const char* GetTypeName() { return "Mutex"; } | ||
| 18 | |||
| 19 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; } | ||
| 20 | Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; } | ||
| 21 | |||
| 22 | bool initial_locked; ///< Initial lock state when mutex was created | ||
| 23 | bool locked; ///< Current locked state | ||
| 24 | Handle lock_thread; ///< Handle to thread that currently has mutex | ||
| 25 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex | ||
| 26 | }; | ||
| 27 | |||
| 28 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 29 | |||
| 30 | typedef std::multimap<Handle, Handle> MutexMap; | ||
| 31 | static MutexMap g_mutex_held_locks; | ||
| 32 | |||
| 33 | void MutexAcquireLock(Mutex* mutex, Handle thread) { | ||
| 34 | g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); | ||
| 35 | mutex->lock_thread = thread; | ||
| 36 | } | ||
| 37 | |||
| 38 | void MutexAcquireLock(Mutex* mutex) { | ||
| 39 | Handle thread = GetCurrentThreadHandle(); | ||
| 40 | MutexAcquireLock(mutex, thread); | ||
| 41 | } | ||
| 42 | |||
| 43 | void MutexEraseLock(Mutex* mutex) { | ||
| 44 | Handle handle = mutex->GetHandle(); | ||
| 45 | auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); | ||
| 46 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { | ||
| 47 | if ((*iter).second == handle) { | ||
| 48 | g_mutex_held_locks.erase(iter); | ||
| 49 | break; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | mutex->lock_thread = -1; | ||
| 53 | } | ||
| 54 | |||
| 55 | bool LockMutex(Mutex* mutex) { | ||
| 56 | // Mutex alread locked? | ||
| 57 | if (mutex->locked) { | ||
| 58 | return false; | ||
| 59 | } | ||
| 60 | MutexAcquireLock(mutex); | ||
| 61 | return true; | ||
| 62 | } | ||
| 63 | |||
| 64 | bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { | ||
| 65 | MutexAcquireLock(mutex, thread); | ||
| 66 | Kernel::ResumeThreadFromWait(thread); | ||
| 67 | return true; | ||
| 68 | } | ||
| 69 | |||
| 70 | bool ReleaseMutex(Mutex* mutex) { | ||
| 71 | MutexEraseLock(mutex); | ||
| 72 | bool woke_threads = false; | ||
| 73 | auto iter = mutex->waiting_threads.begin(); | ||
| 74 | |||
| 75 | // Find the next waiting thread for the mutex... | ||
| 76 | while (!woke_threads && !mutex->waiting_threads.empty()) { | ||
| 77 | woke_threads |= ReleaseMutexForThread(mutex, *iter); | ||
| 78 | mutex->waiting_threads.erase(iter); | ||
| 79 | } | ||
| 80 | // Reset mutex lock thread handle, nothing is waiting | ||
| 81 | if (!woke_threads) { | ||
| 82 | mutex->locked = false; | ||
| 83 | mutex->lock_thread = -1; | ||
| 84 | } | ||
| 85 | return woke_threads; | ||
| 86 | } | ||
| 87 | |||
| 88 | /** | ||
| 89 | * Releases a mutex | ||
| 90 | * @param handle Handle to mutex to release | ||
| 91 | */ | ||
| 92 | Result ReleaseMutex(Handle handle) { | ||
| 93 | Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle); | ||
| 94 | if (!ReleaseMutex(mutex)) { | ||
| 95 | return -1; | ||
| 96 | } | ||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Creates a mutex | ||
| 102 | * @param handle Reference to handle for the newly created mutex | ||
| 103 | * @param initial_locked Specifies if the mutex should be locked initially | ||
| 104 | */ | ||
| 105 | Mutex* CreateMutex(Handle& handle, bool initial_locked) { | ||
| 106 | Mutex* mutex = new Mutex; | ||
| 107 | handle = Kernel::g_object_pool.Create(mutex); | ||
| 108 | |||
| 109 | mutex->locked = mutex->initial_locked = initial_locked; | ||
| 110 | |||
| 111 | // Acquire mutex with current thread if initialized as locked... | ||
| 112 | if (mutex->locked) { | ||
| 113 | MutexAcquireLock(mutex); | ||
| 114 | |||
| 115 | // Otherwise, reset lock thread handle | ||
| 116 | } else { | ||
| 117 | mutex->lock_thread = -1; | ||
| 118 | } | ||
| 119 | return mutex; | ||
| 120 | } | ||
| 121 | |||
| 122 | /** | ||
| 123 | * Creates a mutex | ||
| 124 | * @param initial_locked Specifies if the mutex should be locked initially | ||
| 125 | */ | ||
| 126 | Handle CreateMutex(bool initial_locked) { | ||
| 127 | Handle handle; | ||
| 128 | Mutex* mutex = CreateMutex(handle, initial_locked); | ||
| 129 | return handle; | ||
| 130 | } | ||
| 131 | |||
| 132 | } // namespace | ||
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h new file mode 100644 index 000000000..871e2e562 --- /dev/null +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 10 | |||
| 11 | namespace Kernel { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Releases a mutex | ||
| 15 | * @param handle Handle to mutex to release | ||
| 16 | */ | ||
| 17 | Result ReleaseMutex(Handle handle); | ||
| 18 | |||
| 19 | /** | ||
| 20 | * Creates a mutex | ||
| 21 | * @param handle Reference to handle for the newly created mutex | ||
| 22 | * @param initial_locked Specifies if the mutex should be locked initially | ||
| 23 | */ | ||
| 24 | Handle CreateMutex(bool initial_locked); | ||
| 25 | |||
| 26 | } // namespace | ||
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp new file mode 100644 index 000000000..bf4c8353c --- /dev/null +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -0,0 +1,323 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | |||
| 7 | #include <list> | ||
| 8 | #include <vector> | ||
| 9 | #include <map> | ||
| 10 | #include <string> | ||
| 11 | |||
| 12 | #include "common/common.h" | ||
| 13 | #include "common/thread_queue_list.h" | ||
| 14 | |||
| 15 | #include "core/core.h" | ||
| 16 | #include "core/mem_map.h" | ||
| 17 | #include "core/hle/hle.h" | ||
| 18 | #include "core/hle/svc.h" | ||
| 19 | #include "core/hle/kernel/kernel.h" | ||
| 20 | #include "core/hle/kernel/thread.h" | ||
| 21 | |||
| 22 | namespace Kernel { | ||
| 23 | |||
| 24 | class Thread : public Kernel::Object { | ||
| 25 | public: | ||
| 26 | |||
| 27 | const char* GetName() { return name; } | ||
| 28 | const char* GetTypeName() { return "Thread"; } | ||
| 29 | |||
| 30 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } | ||
| 31 | Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; } | ||
| 32 | |||
| 33 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } | ||
| 34 | inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } | ||
| 35 | inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; } | ||
| 36 | inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } | ||
| 37 | inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } | ||
| 38 | |||
| 39 | ThreadContext context; | ||
| 40 | |||
| 41 | u32 status; | ||
| 42 | u32 entry_point; | ||
| 43 | u32 stack_top; | ||
| 44 | u32 stack_size; | ||
| 45 | |||
| 46 | s32 initial_priority; | ||
| 47 | s32 current_priority; | ||
| 48 | |||
| 49 | s32 processor_id; | ||
| 50 | |||
| 51 | WaitType wait_type; | ||
| 52 | |||
| 53 | char name[Kernel::MAX_NAME_LENGTH + 1]; | ||
| 54 | }; | ||
| 55 | |||
| 56 | // Lists all thread ids that aren't deleted/etc. | ||
| 57 | std::vector<Handle> g_thread_queue; | ||
| 58 | |||
| 59 | // Lists only ready thread ids. | ||
| 60 | Common::ThreadQueueList<Handle> g_thread_ready_queue; | ||
| 61 | |||
| 62 | Handle g_current_thread_handle; | ||
| 63 | Thread* g_current_thread; | ||
| 64 | |||
| 65 | |||
| 66 | /// Gets the current thread | ||
| 67 | inline Thread* GetCurrentThread() { | ||
| 68 | return g_current_thread; | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Gets the current thread handle | ||
| 72 | Handle GetCurrentThreadHandle() { | ||
| 73 | return GetCurrentThread()->GetHandle(); | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Sets the current thread | ||
| 77 | inline void SetCurrentThread(Thread* t) { | ||
| 78 | g_current_thread = t; | ||
| 79 | g_current_thread_handle = t->GetHandle(); | ||
| 80 | } | ||
| 81 | |||
| 82 | /// Saves the current CPU context | ||
| 83 | void SaveContext(ThreadContext& ctx) { | ||
| 84 | Core::g_app_core->SaveContext(ctx); | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Loads a CPU context | ||
| 88 | void LoadContext(ThreadContext& ctx) { | ||
| 89 | Core::g_app_core->LoadContext(ctx); | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Resets a thread | ||
| 93 | void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | ||
| 94 | memset(&t->context, 0, sizeof(ThreadContext)); | ||
| 95 | |||
| 96 | t->context.cpu_registers[0] = arg; | ||
| 97 | t->context.pc = t->entry_point; | ||
| 98 | t->context.sp = t->stack_top; | ||
| 99 | t->context.cpsr = 0x1F; // Usermode | ||
| 100 | |||
| 101 | if (t->current_priority < lowest_priority) { | ||
| 102 | t->current_priority = t->initial_priority; | ||
| 103 | } | ||
| 104 | |||
| 105 | t->wait_type = WAITTYPE_NONE; | ||
| 106 | } | ||
| 107 | |||
| 108 | /// Change a thread to "ready" state | ||
| 109 | void ChangeReadyState(Thread* t, bool ready) { | ||
| 110 | Handle handle = t->GetHandle(); | ||
| 111 | if (t->IsReady()) { | ||
| 112 | if (!ready) { | ||
| 113 | g_thread_ready_queue.remove(t->current_priority, handle); | ||
| 114 | } | ||
| 115 | } else if (ready) { | ||
| 116 | if (t->IsRunning()) { | ||
| 117 | g_thread_ready_queue.push_front(t->current_priority, handle); | ||
| 118 | } else { | ||
| 119 | g_thread_ready_queue.push_back(t->current_priority, handle); | ||
| 120 | } | ||
| 121 | t->status = THREADSTATUS_READY; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | /// Changes a threads state | ||
| 126 | void ChangeThreadState(Thread* t, ThreadStatus new_status) { | ||
| 127 | if (!t || t->status == new_status) { | ||
| 128 | return; | ||
| 129 | } | ||
| 130 | ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); | ||
| 131 | t->status = new_status; | ||
| 132 | |||
| 133 | if (new_status == THREADSTATUS_WAIT) { | ||
| 134 | if (t->wait_type == WAITTYPE_NONE) { | ||
| 135 | printf("ERROR: Waittype none not allowed here\n"); | ||
| 136 | } | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) | ||
| 141 | void CallThread(Thread* t) { | ||
| 142 | // Stop waiting | ||
| 143 | if (t->wait_type != WAITTYPE_NONE) { | ||
| 144 | t->wait_type = WAITTYPE_NONE; | ||
| 145 | } | ||
| 146 | ChangeThreadState(t, THREADSTATUS_READY); | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Switches CPU context to that of the specified thread | ||
| 150 | void SwitchContext(Thread* t) { | ||
| 151 | Thread* cur = GetCurrentThread(); | ||
| 152 | |||
| 153 | // Save context for current thread | ||
| 154 | if (cur) { | ||
| 155 | SaveContext(cur->context); | ||
| 156 | |||
| 157 | if (cur->IsRunning()) { | ||
| 158 | ChangeReadyState(cur, true); | ||
| 159 | } | ||
| 160 | } | ||
| 161 | // Load context of new thread | ||
| 162 | if (t) { | ||
| 163 | SetCurrentThread(t); | ||
| 164 | ChangeReadyState(t, false); | ||
| 165 | t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; | ||
| 166 | t->wait_type = WAITTYPE_NONE; | ||
| 167 | LoadContext(t->context); | ||
| 168 | } else { | ||
| 169 | SetCurrentThread(NULL); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | /// Gets the next thread that is ready to be run by priority | ||
| 174 | Thread* NextThread() { | ||
| 175 | Handle next; | ||
| 176 | Thread* cur = GetCurrentThread(); | ||
| 177 | |||
| 178 | if (cur && cur->IsRunning()) { | ||
| 179 | next = g_thread_ready_queue.pop_first_better(cur->current_priority); | ||
| 180 | } else { | ||
| 181 | next = g_thread_ready_queue.pop_first(); | ||
| 182 | } | ||
| 183 | if (next == 0) { | ||
| 184 | return NULL; | ||
| 185 | } | ||
| 186 | return Kernel::g_object_pool.GetFast<Thread>(next); | ||
| 187 | } | ||
| 188 | |||
| 189 | /// Puts the current thread in the wait state for the given type | ||
| 190 | void WaitCurrentThread(WaitType wait_type) { | ||
| 191 | Thread* t = GetCurrentThread(); | ||
| 192 | t->wait_type = wait_type; | ||
| 193 | ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); | ||
| 194 | } | ||
| 195 | |||
| 196 | /// Resumes a thread from waiting by marking it as "ready" | ||
| 197 | void ResumeThreadFromWait(Handle handle) { | ||
| 198 | u32 error; | ||
| 199 | Thread* t = Kernel::g_object_pool.Get<Thread>(handle, error); | ||
| 200 | if (t) { | ||
| 201 | t->status &= ~THREADSTATUS_WAIT; | ||
| 202 | if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | ||
| 203 | ChangeReadyState(t, true); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | /// Creates a new thread | ||
| 209 | Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, | ||
| 210 | s32 processor_id, u32 stack_top, int stack_size) { | ||
| 211 | |||
| 212 | _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), | ||
| 213 | "CreateThread priority=%d, outside of allowable range!", priority) | ||
| 214 | |||
| 215 | Thread* t = new Thread; | ||
| 216 | |||
| 217 | handle = Kernel::g_object_pool.Create(t); | ||
| 218 | |||
| 219 | g_thread_queue.push_back(handle); | ||
| 220 | g_thread_ready_queue.prepare(priority); | ||
| 221 | |||
| 222 | t->status = THREADSTATUS_DORMANT; | ||
| 223 | t->entry_point = entry_point; | ||
| 224 | t->stack_top = stack_top; | ||
| 225 | t->stack_size = stack_size; | ||
| 226 | t->initial_priority = t->current_priority = priority; | ||
| 227 | t->processor_id = processor_id; | ||
| 228 | t->wait_type = WAITTYPE_NONE; | ||
| 229 | |||
| 230 | strncpy(t->name, name, Kernel::MAX_NAME_LENGTH); | ||
| 231 | t->name[Kernel::MAX_NAME_LENGTH] = '\0'; | ||
| 232 | |||
| 233 | return t; | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Creates a new thread - wrapper for external user | ||
| 237 | Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, | ||
| 238 | u32 stack_top, int stack_size) { | ||
| 239 | if (name == NULL) { | ||
| 240 | ERROR_LOG(KERNEL, "CreateThread(): NULL name"); | ||
| 241 | return -1; | ||
| 242 | } | ||
| 243 | if ((u32)stack_size < 0x200) { | ||
| 244 | ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name, | ||
| 245 | stack_size); | ||
| 246 | return -1; | ||
| 247 | } | ||
| 248 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | ||
| 249 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | ||
| 250 | WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", | ||
| 251 | name, priority, new_priority); | ||
| 252 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | ||
| 253 | // validity of this | ||
| 254 | priority = new_priority; | ||
| 255 | } | ||
| 256 | if (!Memory::GetPointer(entry_point)) { | ||
| 257 | ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); | ||
| 258 | return -1; | ||
| 259 | } | ||
| 260 | Handle handle; | ||
| 261 | Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, | ||
| 262 | stack_size); | ||
| 263 | |||
| 264 | ResetThread(t, arg, 0); | ||
| 265 | |||
| 266 | HLE::EatCycles(32000); | ||
| 267 | |||
| 268 | // This won't schedule to the new thread, but it may to one woken from eating cycles. | ||
| 269 | // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. | ||
| 270 | HLE::ReSchedule("thread created"); | ||
| 271 | |||
| 272 | CallThread(t); | ||
| 273 | |||
| 274 | return handle; | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Sets up the primary application thread | ||
| 278 | Handle SetupMainThread(s32 priority, int stack_size) { | ||
| 279 | Handle handle; | ||
| 280 | |||
| 281 | // Initialize new "main" thread | ||
| 282 | Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, | ||
| 283 | THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); | ||
| 284 | |||
| 285 | ResetThread(t, 0, 0); | ||
| 286 | |||
| 287 | // If running another thread already, set it to "ready" state | ||
| 288 | Thread* cur = GetCurrentThread(); | ||
| 289 | if (cur && cur->IsRunning()) { | ||
| 290 | ChangeReadyState(cur, true); | ||
| 291 | } | ||
| 292 | |||
| 293 | // Run new "main" thread | ||
| 294 | SetCurrentThread(t); | ||
| 295 | t->status = THREADSTATUS_RUNNING; | ||
| 296 | LoadContext(t->context); | ||
| 297 | |||
| 298 | return handle; | ||
| 299 | } | ||
| 300 | |||
| 301 | /// Reschedules to the next available thread (call after current thread is suspended) | ||
| 302 | void Reschedule() { | ||
| 303 | Thread* prev = GetCurrentThread(); | ||
| 304 | Thread* next = NextThread(); | ||
| 305 | if (next > 0) { | ||
| 306 | SwitchContext(next); | ||
| 307 | |||
| 308 | // Hack - automatically change previous thread (which would have been in "wait" state) to | ||
| 309 | // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to | ||
| 310 | // actually wait for whatever event it is supposed to be waiting on. | ||
| 311 | ChangeReadyState(prev, true); | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 315 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 316 | |||
| 317 | void ThreadingInit() { | ||
| 318 | } | ||
| 319 | |||
| 320 | void ThreadingShutdown() { | ||
| 321 | } | ||
| 322 | |||
| 323 | } // namespace | ||
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h new file mode 100644 index 000000000..9628f165d --- /dev/null +++ b/src/core/hle/kernel/thread.h | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project / PPSSPP Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "core/hle/kernel/kernel.h" | ||
| 9 | |||
| 10 | enum ThreadPriority { | ||
| 11 | THREADPRIO_HIGHEST = 0, ///< Highest thread priority | ||
| 12 | THREADPRIO_DEFAULT = 16, ///< Default thread priority for userland apps | ||
| 13 | THREADPRIO_LOW = 31, ///< Low range of thread priority for userland apps | ||
| 14 | THREADPRIO_LOWEST = 63, ///< Thread priority max checked by svcCreateThread | ||
| 15 | }; | ||
| 16 | |||
| 17 | enum ThreadProcessorId { | ||
| 18 | THREADPROCESSORID_0 = 0xFFFFFFFE, ///< Enables core appcode | ||
| 19 | THREADPROCESSORID_1 = 0xFFFFFFFD, ///< Enables core syscore | ||
| 20 | THREADPROCESSORID_ALL = 0xFFFFFFFC, ///< Enables both cores | ||
| 21 | }; | ||
| 22 | |||
| 23 | enum ThreadStatus { | ||
| 24 | THREADSTATUS_RUNNING = 1, | ||
| 25 | THREADSTATUS_READY = 2, | ||
| 26 | THREADSTATUS_WAIT = 4, | ||
| 27 | THREADSTATUS_SUSPEND = 8, | ||
| 28 | THREADSTATUS_DORMANT = 16, | ||
| 29 | THREADSTATUS_DEAD = 32, | ||
| 30 | THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND | ||
| 31 | }; | ||
| 32 | |||
| 33 | enum WaitType { | ||
| 34 | WAITTYPE_NONE, | ||
| 35 | WAITTYPE_SLEEP, | ||
| 36 | WAITTYPE_SEMA, | ||
| 37 | WAITTYPE_EVENTFLAG, | ||
| 38 | WAITTYPE_THREADEND, | ||
| 39 | WAITTYPE_VBLANK, | ||
| 40 | WAITTYPE_MUTEX, | ||
| 41 | WAITTYPE_SYNCH, | ||
| 42 | }; | ||
| 43 | |||
| 44 | namespace Kernel { | ||
| 45 | |||
| 46 | /// Creates a new thread - wrapper for external user | ||
| 47 | Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, | ||
| 48 | u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE); | ||
| 49 | |||
| 50 | /// Sets up the primary application thread | ||
| 51 | Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); | ||
| 52 | |||
| 53 | /// Reschedules to the next available thread (call after current thread is suspended) | ||
| 54 | void Reschedule(); | ||
| 55 | |||
| 56 | /// Puts the current thread in the wait state for the given type | ||
| 57 | void WaitCurrentThread(WaitType wait_type); | ||
| 58 | |||
| 59 | /// Resumes a thread from waiting by marking it as "ready" | ||
| 60 | void ResumeThreadFromWait(Handle handle); | ||
| 61 | |||
| 62 | /// Gets the current thread handle | ||
| 63 | Handle GetCurrentThreadHandle(); | ||
| 64 | |||
| 65 | /// Put current thread in a wait state - on WaitSynchronization | ||
| 66 | void WaitThread_Synchronization(); | ||
| 67 | |||
| 68 | /// Initialize threading | ||
| 69 | void ThreadingInit(); | ||
| 70 | |||
| 71 | /// Shutdown threading | ||
| 72 | void ThreadingShutdown(); | ||
| 73 | |||
| 74 | } // namespace | ||
diff --git a/src/core/hle/service/apt.cpp b/src/core/hle/service/apt.cpp index 709ac5493..32759a087 100644 --- a/src/core/hle/service/apt.cpp +++ b/src/core/hle/service/apt.cpp | |||
| @@ -3,9 +3,10 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | 5 | ||
| 6 | #include "common/log.h" | 6 | #include "common/common.h" |
| 7 | 7 | ||
| 8 | #include "core/hle/hle.h" | 8 | #include "core/hle/hle.h" |
| 9 | #include "core/hle/kernel/mutex.h" | ||
| 9 | #include "core/hle/service/apt.h" | 10 | #include "core/hle/service/apt.h" |
| 10 | 11 | ||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -19,7 +20,10 @@ void Initialize(Service::Interface* self) { | |||
| 19 | 20 | ||
| 20 | void GetLockHandle(Service::Interface* self) { | 21 | void GetLockHandle(Service::Interface* self) { |
| 21 | u32* cmd_buff = Service::GetCommandBuffer(); | 22 | u32* cmd_buff = Service::GetCommandBuffer(); |
| 22 | cmd_buff[5] = 0x00000000; // TODO: This should be an actual mutex handle | 23 | u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field |
| 24 | cmd_buff[1] = 0; // No error | ||
| 25 | cmd_buff[5] = Kernel::CreateMutex(false); | ||
| 26 | DEBUG_LOG(KERNEL, "APT_U::GetLockHandle called : created handle 0x%08X", cmd_buff[5]); | ||
| 23 | } | 27 | } |
| 24 | 28 | ||
| 25 | const Interface::FunctionInfo FunctionTable[] = { | 29 | const Interface::FunctionInfo FunctionTable[] = { |
diff --git a/src/core/hle/service/apt.h b/src/core/hle/service/apt.h index 4c7dd07e7..dca3097ed 100644 --- a/src/core/hle/service/apt.h +++ b/src/core/hle/service/apt.h | |||
| @@ -29,7 +29,7 @@ public: | |||
| 29 | * Gets the string port name used by CTROS for the service | 29 | * Gets the string port name used by CTROS for the service |
| 30 | * @return Port name of service | 30 | * @return Port name of service |
| 31 | */ | 31 | */ |
| 32 | std::string GetPortName() const { | 32 | const char *GetPortName() const { |
| 33 | return "APT:U"; | 33 | return "APT:U"; |
| 34 | } | 34 | } |
| 35 | }; | 35 | }; |
diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp index 12c7dabcd..50cee2c41 100644 --- a/src/core/hle/service/gsp.cpp +++ b/src/core/hle/service/gsp.cpp | |||
| @@ -27,7 +27,7 @@ union GX_CmdBufferHeader { | |||
| 27 | // <=15 when writing a command to shared memory. This is incremented by the application when | 27 | // <=15 when writing a command to shared memory. This is incremented by the application when |
| 28 | // writing a command to shared memory, after increasing this value TriggerCmdReqQueue is only | 28 | // writing a command to shared memory, after increasing this value TriggerCmdReqQueue is only |
| 29 | // used if this field is value 1. | 29 | // used if this field is value 1. |
| 30 | BitField<8,8,u32> number_commands; | 30 | BitField<8,8,u32> number_commands; |
| 31 | 31 | ||
| 32 | }; | 32 | }; |
| 33 | 33 | ||
| @@ -101,9 +101,7 @@ void RegisterInterruptRelayQueue(Service::Interface* self) { | |||
| 101 | u32* cmd_buff = Service::GetCommandBuffer(); | 101 | u32* cmd_buff = Service::GetCommandBuffer(); |
| 102 | u32 flags = cmd_buff[1]; | 102 | u32 flags = cmd_buff[1]; |
| 103 | u32 event_handle = cmd_buff[3]; // TODO(bunnei): Implement event handling | 103 | u32 event_handle = cmd_buff[3]; // TODO(bunnei): Implement event handling |
| 104 | |||
| 105 | cmd_buff[2] = g_thread_id; // ThreadID | 104 | cmd_buff[2] = g_thread_id; // ThreadID |
| 106 | cmd_buff[4] = self->NewHandle(); | ||
| 107 | } | 105 | } |
| 108 | 106 | ||
| 109 | /// This triggers handling of the GX command written to the command buffer in shared memory. | 107 | /// This triggers handling of the GX command written to the command buffer in shared memory. |
diff --git a/src/core/hle/service/gsp.h b/src/core/hle/service/gsp.h index 5ba09ab70..eb5786cd1 100644 --- a/src/core/hle/service/gsp.h +++ b/src/core/hle/service/gsp.h | |||
| @@ -23,7 +23,7 @@ public: | |||
| 23 | * Gets the string port name used by CTROS for the service | 23 | * Gets the string port name used by CTROS for the service |
| 24 | * @return Port name of service | 24 | * @return Port name of service |
| 25 | */ | 25 | */ |
| 26 | std::string GetPortName() const { | 26 | const char *GetPortName() const { |
| 27 | return "gsp::Gpu"; | 27 | return "gsp::Gpu"; |
| 28 | } | 28 | } |
| 29 | 29 | ||
diff --git a/src/core/hle/service/hid.h b/src/core/hle/service/hid.h index b17fcfa86..81c29eb2e 100644 --- a/src/core/hle/service/hid.h +++ b/src/core/hle/service/hid.h | |||
| @@ -25,7 +25,7 @@ public: | |||
| 25 | * Gets the string port name used by CTROS for the service | 25 | * Gets the string port name used by CTROS for the service |
| 26 | * @return Port name of service | 26 | * @return Port name of service |
| 27 | */ | 27 | */ |
| 28 | std::string GetPortName() const { | 28 | const char *GetPortName() const { |
| 29 | return "hid:USER"; | 29 | return "hid:USER"; |
| 30 | } | 30 | } |
| 31 | 31 | ||
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index e6605a398..08d0c43ff 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -7,12 +7,15 @@ | |||
| 7 | #include "common/string_util.h" | 7 | #include "common/string_util.h" |
| 8 | 8 | ||
| 9 | #include "core/hle/hle.h" | 9 | #include "core/hle/hle.h" |
| 10 | |||
| 10 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 11 | #include "core/hle/service/apt.h" | 12 | #include "core/hle/service/apt.h" |
| 12 | #include "core/hle/service/gsp.h" | 13 | #include "core/hle/service/gsp.h" |
| 13 | #include "core/hle/service/hid.h" | 14 | #include "core/hle/service/hid.h" |
| 14 | #include "core/hle/service/srv.h" | 15 | #include "core/hle/service/srv.h" |
| 15 | 16 | ||
| 17 | #include "core/hle/kernel/kernel.h" | ||
| 18 | |||
| 16 | namespace Service { | 19 | namespace Service { |
| 17 | 20 | ||
| 18 | Manager* g_manager = NULL; ///< Service manager | 21 | Manager* g_manager = NULL; ///< Service manager |
| @@ -31,32 +34,21 @@ Manager::~Manager() { | |||
| 31 | 34 | ||
| 32 | /// Add a service to the manager (does not create it though) | 35 | /// Add a service to the manager (does not create it though) |
| 33 | void Manager::AddService(Interface* service) { | 36 | void Manager::AddService(Interface* service) { |
| 34 | int index = m_services.size(); | 37 | m_port_map[service->GetPortName()] = Kernel::g_object_pool.Create(service); |
| 35 | u32 new_uid = GetUIDFromIndex(index); | ||
| 36 | |||
| 37 | m_services.push_back(service); | 38 | m_services.push_back(service); |
| 38 | |||
| 39 | m_port_map[service->GetPortName()] = new_uid; | ||
| 40 | service->m_uid = new_uid; | ||
| 41 | } | 39 | } |
| 42 | 40 | ||
| 43 | /// Removes a service from the manager, also frees memory | 41 | /// Removes a service from the manager, also frees memory |
| 44 | void Manager::DeleteService(std::string port_name) { | 42 | void Manager::DeleteService(std::string port_name) { |
| 45 | auto service = FetchFromPortName(port_name); | 43 | Interface* service = FetchFromPortName(port_name); |
| 46 | 44 | m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end()); | |
| 47 | m_services.erase(m_services.begin() + GetIndexFromUID(service->m_uid)); | ||
| 48 | m_port_map.erase(port_name); | 45 | m_port_map.erase(port_name); |
| 49 | |||
| 50 | delete service; | 46 | delete service; |
| 51 | } | 47 | } |
| 52 | 48 | ||
| 53 | /// Get a Service Interface from its UID | 49 | /// Get a Service Interface from its Handle |
| 54 | Interface* Manager::FetchFromUID(u32 uid) { | 50 | Interface* Manager::FetchFromHandle(Handle handle) { |
| 55 | int index = GetIndexFromUID(uid); | 51 | return Kernel::g_object_pool.GetFast<Interface>(handle); |
| 56 | if (index < (int)m_services.size()) { | ||
| 57 | return m_services[index]; | ||
| 58 | } | ||
| 59 | return NULL; | ||
| 60 | } | 52 | } |
| 61 | 53 | ||
| 62 | /// Get a Service Interface from its port | 54 | /// Get a Service Interface from its port |
| @@ -65,7 +57,7 @@ Interface* Manager::FetchFromPortName(std::string port_name) { | |||
| 65 | if (itr == m_port_map.end()) { | 57 | if (itr == m_port_map.end()) { |
| 66 | return NULL; | 58 | return NULL; |
| 67 | } | 59 | } |
| 68 | return FetchFromUID(itr->second); | 60 | return FetchFromHandle(itr->second); |
| 69 | } | 61 | } |
| 70 | 62 | ||
| 71 | 63 | ||
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index b260a290a..fab51753f 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -4,22 +4,22 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | ||
| 7 | #include <vector> | 8 | #include <vector> |
| 8 | #include <map> | 9 | #include <map> |
| 9 | #include <string> | 10 | #include <string> |
| 10 | 11 | ||
| 11 | #include "common/common.h" | 12 | #include "common/common.h" |
| 12 | #include "common/common_types.h" | ||
| 13 | #include "core/mem_map.h" | 13 | #include "core/mem_map.h" |
| 14 | #include "core/hle/syscall.h" | 14 | |
| 15 | #include "core/hle/kernel/kernel.h" | ||
| 16 | #include "core/hle/svc.h" | ||
| 15 | 17 | ||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 17 | // Namespace Service | 19 | // Namespace Service |
| 18 | 20 | ||
| 19 | namespace Service { | 21 | namespace Service { |
| 20 | 22 | ||
| 21 | typedef s32 NativeUID; ///< Native handle for a service | ||
| 22 | |||
| 23 | static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters) | 23 | static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters) |
| 24 | static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header | 24 | static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header |
| 25 | 25 | ||
| @@ -35,15 +35,15 @@ inline static u32* GetCommandBuffer(const int offset=0) { | |||
| 35 | class Manager; | 35 | class Manager; |
| 36 | 36 | ||
| 37 | /// Interface to a CTROS service | 37 | /// Interface to a CTROS service |
| 38 | class Interface : NonCopyable { | 38 | class Interface : public Kernel::Object { |
| 39 | friend class Manager; | 39 | friend class Manager; |
| 40 | public: | 40 | public: |
| 41 | |||
| 42 | const char *GetName() { return GetPortName(); } | ||
| 43 | const char *GetTypeName() { return GetPortName(); } | ||
| 41 | 44 | ||
| 42 | Interface() { | 45 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; } |
| 43 | } | 46 | Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Service; } |
| 44 | |||
| 45 | virtual ~Interface() { | ||
| 46 | } | ||
| 47 | 47 | ||
| 48 | typedef void (*Function)(Interface*); | 48 | typedef void (*Function)(Interface*); |
| 49 | 49 | ||
| @@ -54,54 +54,43 @@ public: | |||
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | /** | 56 | /** |
| 57 | * Gets the UID for the serice | ||
| 58 | * @return UID of service in native format | ||
| 59 | */ | ||
| 60 | NativeUID GetUID() const { | ||
| 61 | return (NativeUID)m_uid; | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Gets the string name used by CTROS for a service | 57 | * Gets the string name used by CTROS for a service |
| 66 | * @return Port name of service | 58 | * @return Port name of service |
| 67 | */ | 59 | */ |
| 68 | virtual std::string GetPortName() const { | 60 | virtual const char *GetPortName() const { |
| 69 | return "[UNKNOWN SERVICE PORT]"; | 61 | return "[UNKNOWN SERVICE PORT]"; |
| 70 | } | 62 | } |
| 71 | 63 | ||
| 72 | /// Allocates a new handle for the service | 64 | /// Allocates a new handle for the service |
| 73 | Syscall::Handle NewHandle() { | 65 | Handle CreateHandle(Kernel::Object *obj) { |
| 74 | Syscall::Handle handle = (m_handles.size() << 16) | m_uid; | 66 | Handle handle = Kernel::g_object_pool.Create(obj); |
| 75 | m_handles.push_back(handle); | 67 | m_handles.push_back(handle); |
| 76 | return handle; | 68 | return handle; |
| 77 | } | 69 | } |
| 78 | 70 | ||
| 79 | /// Frees a handle from the service | 71 | /// Frees a handle from the service |
| 80 | void DeleteHandle(Syscall::Handle handle) { | 72 | template <class T> |
| 81 | for(auto iter = m_handles.begin(); iter != m_handles.end(); ++iter) { | 73 | void DeleteHandle(const Handle handle) { |
| 82 | if(*iter == handle) { | 74 | Kernel::g_object_pool.Destroy<T>(handle); |
| 83 | m_handles.erase(iter); | 75 | m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); |
| 84 | break; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | 76 | } |
| 88 | 77 | ||
| 89 | /** | 78 | /** |
| 90 | * Called when svcSendSyncRequest is called, loads command buffer and executes comand | 79 | * Called when svcSendSyncRequest is called, loads command buffer and executes comand |
| 91 | * @return Return result of svcSendSyncRequest passed back to user app | 80 | * @return Return result of svcSendSyncRequest passed back to user app |
| 92 | */ | 81 | */ |
| 93 | Syscall::Result Sync() { | 82 | Result Sync() { |
| 94 | u32* cmd_buff = GetCommandBuffer(); | 83 | u32* cmd_buff = GetCommandBuffer(); |
| 95 | auto itr = m_functions.find(cmd_buff[0]); | 84 | auto itr = m_functions.find(cmd_buff[0]); |
| 96 | 85 | ||
| 97 | if (itr == m_functions.end()) { | 86 | if (itr == m_functions.end()) { |
| 98 | ERROR_LOG(OSHLE, "Unknown/unimplemented function: port = %s, command = 0x%08X!", | 87 | ERROR_LOG(OSHLE, "Unknown/unimplemented function: port = %s, command = 0x%08X!", |
| 99 | GetPortName().c_str(), cmd_buff[0]); | 88 | GetPortName(), cmd_buff[0]); |
| 100 | return -1; | 89 | return -1; |
| 101 | } | 90 | } |
| 102 | if (itr->second.func == NULL) { | 91 | if (itr->second.func == NULL) { |
| 103 | ERROR_LOG(OSHLE, "Unimplemented function: port = %s, name = %s!", | 92 | ERROR_LOG(OSHLE, "Unimplemented function: port = %s, name = %s!", |
| 104 | GetPortName().c_str(), itr->second.name.c_str()); | 93 | GetPortName(), itr->second.name.c_str()); |
| 105 | return -1; | 94 | return -1; |
| 106 | } | 95 | } |
| 107 | 96 | ||
| @@ -122,10 +111,10 @@ protected: | |||
| 122 | } | 111 | } |
| 123 | 112 | ||
| 124 | private: | 113 | private: |
| 125 | u32 m_uid; | 114 | |
| 126 | 115 | std::vector<Handle> m_handles; | |
| 127 | std::vector<Syscall::Handle> m_handles; | 116 | std::map<u32, FunctionInfo> m_functions; |
| 128 | std::map<u32, FunctionInfo> m_functions; | 117 | |
| 129 | }; | 118 | }; |
| 130 | 119 | ||
| 131 | /// Simple class to manage accessing services from ports and UID handles | 120 | /// Simple class to manage accessing services from ports and UID handles |
| @@ -143,25 +132,16 @@ public: | |||
| 143 | void DeleteService(std::string port_name); | 132 | void DeleteService(std::string port_name); |
| 144 | 133 | ||
| 145 | /// Get a Service Interface from its UID | 134 | /// Get a Service Interface from its UID |
| 146 | Interface* FetchFromUID(u32 uid); | 135 | Interface* FetchFromHandle(u32 uid); |
| 147 | 136 | ||
| 148 | /// Get a Service Interface from its port | 137 | /// Get a Service Interface from its port |
| 149 | Interface* FetchFromPortName(std::string port_name); | 138 | Interface* FetchFromPortName(std::string port_name); |
| 150 | 139 | ||
| 151 | private: | 140 | private: |
| 152 | 141 | ||
| 153 | /// Convert an index into m_services vector into a UID | ||
| 154 | static u32 GetUIDFromIndex(const int index) { | ||
| 155 | return index | 0x10000000; | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Convert a UID into an index into m_services | ||
| 159 | static int GetIndexFromUID(const u32 uid) { | ||
| 160 | return uid & 0x0FFFFFFF; | ||
| 161 | } | ||
| 162 | |||
| 163 | std::vector<Interface*> m_services; | 142 | std::vector<Interface*> m_services; |
| 164 | std::map<std::string, u32> m_port_map; | 143 | std::map<std::string, u32> m_port_map; |
| 144 | |||
| 165 | }; | 145 | }; |
| 166 | 146 | ||
| 167 | /// Initialize ServiceManager | 147 | /// Initialize ServiceManager |
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 071741444..ff6da8f1c 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp | |||
| @@ -16,18 +16,24 @@ void Initialize(Service::Interface* self) { | |||
| 16 | NOTICE_LOG(OSHLE, "SRV::Sync - Initialize"); | 16 | NOTICE_LOG(OSHLE, "SRV::Sync - Initialize"); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | void GetProcSemaphore(Service::Interface* self) { | ||
| 20 | // Get process semaphore? | ||
| 21 | u32* cmd_buff = Service::GetCommandBuffer(); | ||
| 22 | cmd_buff[3] = 0xDEADBEEF; // Return something... 0 == NULL, raises an exception | ||
| 23 | } | ||
| 24 | |||
| 19 | void GetServiceHandle(Service::Interface* self) { | 25 | void GetServiceHandle(Service::Interface* self) { |
| 20 | Syscall::Result res = 0; | 26 | Result res = 0; |
| 21 | u32* cmd_buff = Service::GetCommandBuffer(); | 27 | u32* cmd_buff = Service::GetCommandBuffer(); |
| 22 | 28 | ||
| 23 | std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); | 29 | std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); |
| 24 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); | 30 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); |
| 25 | 31 | ||
| 26 | NOTICE_LOG(OSHLE, "SRV::Sync - GetHandle - port: %s, handle: 0x%08X", port_name.c_str(), | 32 | NOTICE_LOG(OSHLE, "SRV::Sync - GetHandle - port: %s, handle: 0x%08X", port_name.c_str(), |
| 27 | service->GetUID()); | 33 | service->GetHandle()); |
| 28 | 34 | ||
| 29 | if (NULL != service) { | 35 | if (NULL != service) { |
| 30 | cmd_buff[3] = service->GetUID(); | 36 | cmd_buff[3] = service->GetHandle(); |
| 31 | } else { | 37 | } else { |
| 32 | ERROR_LOG(OSHLE, "Service %s does not exist", port_name.c_str()); | 38 | ERROR_LOG(OSHLE, "Service %s does not exist", port_name.c_str()); |
| 33 | res = -1; | 39 | res = -1; |
| @@ -39,7 +45,7 @@ void GetServiceHandle(Service::Interface* self) { | |||
| 39 | 45 | ||
| 40 | const Interface::FunctionInfo FunctionTable[] = { | 46 | const Interface::FunctionInfo FunctionTable[] = { |
| 41 | {0x00010002, Initialize, "Initialize"}, | 47 | {0x00010002, Initialize, "Initialize"}, |
| 42 | {0x00020000, NULL, "GetProcSemaphore"}, | 48 | {0x00020000, GetProcSemaphore, "GetProcSemaphore"}, |
| 43 | {0x00030100, NULL, "RegisterService"}, | 49 | {0x00030100, NULL, "RegisterService"}, |
| 44 | {0x000400C0, NULL, "UnregisterService"}, | 50 | {0x000400C0, NULL, "UnregisterService"}, |
| 45 | {0x00050100, GetServiceHandle, "GetServiceHandle"}, | 51 | {0x00050100, GetServiceHandle, "GetServiceHandle"}, |
diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h index 760c976b4..1e35032ba 100644 --- a/src/core/hle/service/srv.h +++ b/src/core/hle/service/srv.h | |||
| @@ -22,7 +22,7 @@ public: | |||
| 22 | * Gets the string name used by CTROS for the service | 22 | * Gets the string name used by CTROS for the service |
| 23 | * @return Port name of service | 23 | * @return Port name of service |
| 24 | */ | 24 | */ |
| 25 | std::string GetPortName() const { | 25 | const char *GetPortName() const { |
| 26 | return "srv:"; | 26 | return "srv:"; |
| 27 | } | 27 | } |
| 28 | 28 | ||
| @@ -30,7 +30,7 @@ public: | |||
| 30 | * Called when svcSendSyncRequest is called, loads command buffer and executes comand | 30 | * Called when svcSendSyncRequest is called, loads command buffer and executes comand |
| 31 | * @return Return result of svcSendSyncRequest passed back to user app | 31 | * @return Return result of svcSendSyncRequest passed back to user app |
| 32 | */ | 32 | */ |
| 33 | Syscall::Result Sync(); | 33 | Result Sync(); |
| 34 | 34 | ||
| 35 | }; | 35 | }; |
| 36 | 36 | ||
diff --git a/src/core/hle/syscall.cpp b/src/core/hle/svc.cpp index d47df6038..90c05cb74 100644 --- a/src/core/hle/syscall.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -3,17 +3,25 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <map> | 5 | #include <map> |
| 6 | #include <string> | ||
| 7 | |||
| 8 | #include "common/symbols.h" | ||
| 6 | 9 | ||
| 7 | #include "core/mem_map.h" | 10 | #include "core/mem_map.h" |
| 8 | 11 | ||
| 12 | #include "core/hle/kernel/kernel.h" | ||
| 13 | #include "core/hle/kernel/mutex.h" | ||
| 14 | #include "core/hle/kernel/thread.h" | ||
| 15 | |||
| 9 | #include "core/hle/function_wrappers.h" | 16 | #include "core/hle/function_wrappers.h" |
| 10 | #include "core/hle/syscall.h" | 17 | #include "core/hle/svc.h" |
| 11 | #include "core/hle/service/service.h" | 18 | #include "core/hle/service/service.h" |
| 19 | #include "core/hle/kernel/thread.h" | ||
| 12 | 20 | ||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 21 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 14 | // Namespace Syscall | 22 | // Namespace SVC |
| 15 | 23 | ||
| 16 | namespace Syscall { | 24 | namespace SVC { |
| 17 | 25 | ||
| 18 | enum ControlMemoryOperation { | 26 | enum ControlMemoryOperation { |
| 19 | MEMORY_OPERATION_HEAP = 0x00000003, | 27 | MEMORY_OPERATION_HEAP = 0x00000003, |
| @@ -26,7 +34,8 @@ enum MapMemoryPermission { | |||
| 26 | }; | 34 | }; |
| 27 | 35 | ||
| 28 | /// Map application or GSP heap memory | 36 | /// Map application or GSP heap memory |
| 29 | Result ControlMemory(u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { | 37 | Result ControlMemory(void* _outaddr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { |
| 38 | u32* outaddr = (u32*)_outaddr; | ||
| 30 | u32 virtual_address = 0x00000000; | 39 | u32 virtual_address = 0x00000000; |
| 31 | 40 | ||
| 32 | DEBUG_LOG(SVC, "ControlMemory called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", | 41 | 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 | |||
| 48 | default: | 57 | default: |
| 49 | ERROR_LOG(SVC, "ControlMemory unknown operation=0x%08X", operation); | 58 | ERROR_LOG(SVC, "ControlMemory unknown operation=0x%08X", operation); |
| 50 | } | 59 | } |
| 51 | 60 | if (NULL != outaddr) { | |
| 61 | *outaddr = virtual_address; | ||
| 62 | } | ||
| 52 | Core::g_app_core->SetReg(1, virtual_address); | 63 | Core::g_app_core->SetReg(1, virtual_address); |
| 53 | 64 | ||
| 54 | return 0; | 65 | return 0; |
| @@ -72,17 +83,20 @@ Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherper | |||
| 72 | 83 | ||
| 73 | /// Connect to an OS service given the port name, returns the handle to the port to out | 84 | /// Connect to an OS service given the port name, returns the handle to the port to out |
| 74 | Result ConnectToPort(void* out, const char* port_name) { | 85 | Result ConnectToPort(void* out, const char* port_name) { |
| 75 | |||
| 76 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); | 86 | Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); |
| 77 | Core::g_app_core->SetReg(1, service->GetUID()); | 87 | if (service) { |
| 88 | Core::g_app_core->SetReg(1, service->GetHandle()); | ||
| 89 | } else { | ||
| 90 | PanicYesNo("ConnectToPort called port_name=%s, but it is not implemented!", port_name); | ||
| 91 | } | ||
| 78 | DEBUG_LOG(SVC, "ConnectToPort called port_name=%s", port_name); | 92 | DEBUG_LOG(SVC, "ConnectToPort called port_name=%s", port_name); |
| 79 | return 0; | 93 | return 0; |
| 80 | } | 94 | } |
| 81 | 95 | ||
| 82 | /// Synchronize to an OS service | 96 | /// Synchronize to an OS service |
| 83 | Result SendSyncRequest(Handle session) { | 97 | Result SendSyncRequest(Handle handle) { |
| 84 | DEBUG_LOG(SVC, "SendSyncRequest called session=0x%08X"); | 98 | DEBUG_LOG(SVC, "SendSyncRequest called handle=0x%08X"); |
| 85 | Service::Interface* service = Service::g_manager->FetchFromUID(session); | 99 | Service::Interface* service = Service::g_manager->FetchFromHandle(handle); |
| 86 | service->Sync(); | 100 | service->Sync(); |
| 87 | return 0; | 101 | return 0; |
| 88 | } | 102 | } |
| @@ -95,10 +109,25 @@ Result CloseHandle(Handle handle) { | |||
| 95 | } | 109 | } |
| 96 | 110 | ||
| 97 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds | 111 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds |
| 98 | Result WaitSynchronization1(Handle handle, s64 nanoseconds) { | 112 | Result WaitSynchronization1(Handle handle, s64 nano_seconds) { |
| 99 | // ImplementMe | ||
| 100 | DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronization1 called handle=0x%08X, nanoseconds=%d", | 113 | DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronization1 called handle=0x%08X, nanoseconds=%d", |
| 101 | handle, nanoseconds); | 114 | handle, nano_seconds); |
| 115 | Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? | ||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | ||
| 120 | Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wait_all, s64 nano_seconds) { | ||
| 121 | s32* out = (s32*)_out; | ||
| 122 | Handle* handles = (Handle*)_handles; | ||
| 123 | |||
| 124 | DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronizationN called handle_count=%d, wait_all=%s, nanoseconds=%d %s", | ||
| 125 | handle_count, (wait_all ? "true" : "false"), nano_seconds); | ||
| 126 | |||
| 127 | for (u32 i = 0; i < handle_count; i++) { | ||
| 128 | DEBUG_LOG(SVC, "\thandle[%d]=0x%08X", i, handles[i]); | ||
| 129 | } | ||
| 130 | Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? | ||
| 102 | return 0; | 131 | return 0; |
| 103 | } | 132 | } |
| 104 | 133 | ||
| @@ -106,7 +135,7 @@ Result WaitSynchronization1(Handle handle, s64 nanoseconds) { | |||
| 106 | Result CreateAddressArbiter(void* arbiter) { | 135 | Result CreateAddressArbiter(void* arbiter) { |
| 107 | // ImplementMe | 136 | // ImplementMe |
| 108 | DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateAddressArbiter called"); | 137 | DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateAddressArbiter called"); |
| 109 | Core::g_app_core->SetReg(1, 0xDEADBEEF); | 138 | Core::g_app_core->SetReg(1, 0xFABBDADD); |
| 110 | return 0; | 139 | return 0; |
| 111 | } | 140 | } |
| 112 | 141 | ||
| @@ -134,16 +163,79 @@ Result GetResourceLimitCurrentValues(void* _values, Handle resource_limit, void* | |||
| 134 | return 0; | 163 | return 0; |
| 135 | } | 164 | } |
| 136 | 165 | ||
| 137 | const HLE::FunctionDef Syscall_Table[] = { | 166 | /// Creates a new thread |
| 167 | Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { | ||
| 168 | std::string name; | ||
| 169 | if (Symbols::HasSymbol(entry_point)) { | ||
| 170 | TSymbol symbol = Symbols::GetSymbol(entry_point); | ||
| 171 | name = symbol.name; | ||
| 172 | } else { | ||
| 173 | char buff[100]; | ||
| 174 | sprintf(buff, "%s", "unknown-%08X", entry_point); | ||
| 175 | name = buff; | ||
| 176 | } | ||
| 177 | |||
| 178 | Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, arg, processor_id, | ||
| 179 | stack_top); | ||
| 180 | |||
| 181 | Core::g_app_core->SetReg(1, thread); | ||
| 182 | |||
| 183 | DEBUG_LOG(SVC, "CreateThread called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | ||
| 184 | "threadpriority=0x%08X, processorid=0x%08X : created handle 0x%08X", entry_point, | ||
| 185 | name.c_str(), arg, stack_top, priority, processor_id, thread); | ||
| 186 | |||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | /// Create a mutex | ||
| 191 | Result CreateMutex(void* _mutex, u32 initial_locked) { | ||
| 192 | Handle* mutex = (Handle*)_mutex; | ||
| 193 | *mutex = Kernel::CreateMutex((initial_locked != 0)); | ||
| 194 | Core::g_app_core->SetReg(1, *mutex); | ||
| 195 | DEBUG_LOG(SVC, "CreateMutex called initial_locked=%s : created handle 0x%08X", | ||
| 196 | initial_locked ? "true" : "false", *mutex); | ||
| 197 | return 0; | ||
| 198 | } | ||
| 199 | |||
| 200 | /// Release a mutex | ||
| 201 | Result ReleaseMutex(Handle handle) { | ||
| 202 | DEBUG_LOG(SVC, "ReleaseMutex called handle=0x%08X", handle); | ||
| 203 | Kernel::ReleaseMutex(handle); | ||
| 204 | return 0; | ||
| 205 | } | ||
| 206 | |||
| 207 | /// Get current thread ID | ||
| 208 | Result GetThreadId(void* thread_id, u32 thread) { | ||
| 209 | DEBUG_LOG(SVC, "(UNIMPLEMENTED) GetThreadId called thread=0x%08X", thread); | ||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | /// Query memory | ||
| 214 | Result QueryMemory(void *_info, void *_out, u32 addr) { | ||
| 215 | MemoryInfo* info = (MemoryInfo*) _info; | ||
| 216 | PageInfo* out = (PageInfo*) _out; | ||
| 217 | DEBUG_LOG(SVC, "(UNIMPLEMENTED) QueryMemory called addr=0x%08X", addr); | ||
| 218 | return 0; | ||
| 219 | } | ||
| 220 | |||
| 221 | /// Create an event | ||
| 222 | Result CreateEvent(void* _event, u32 reset_type) { | ||
| 223 | Handle* event = (Handle*)_event; | ||
| 224 | DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateEvent called reset_type=0x%08X", reset_type); | ||
| 225 | Core::g_app_core->SetReg(1, 0xBADC0DE0); | ||
| 226 | return 0; | ||
| 227 | } | ||
| 228 | |||
| 229 | const HLE::FunctionDef SVC_Table[] = { | ||
| 138 | {0x00, NULL, "Unknown"}, | 230 | {0x00, NULL, "Unknown"}, |
| 139 | {0x01, WrapI_UUUUU<ControlMemory>, "ControlMemory"}, | 231 | {0x01, WrapI_VUUUUU<ControlMemory>, "ControlMemory"}, |
| 140 | {0x02, NULL, "QueryMemory"}, | 232 | {0x02, WrapI_VVU<QueryMemory>, "QueryMemory"}, |
| 141 | {0x03, NULL, "ExitProcess"}, | 233 | {0x03, NULL, "ExitProcess"}, |
| 142 | {0x04, NULL, "GetProcessAffinityMask"}, | 234 | {0x04, NULL, "GetProcessAffinityMask"}, |
| 143 | {0x05, NULL, "SetProcessAffinityMask"}, | 235 | {0x05, NULL, "SetProcessAffinityMask"}, |
| 144 | {0x06, NULL, "GetProcessIdealProcessor"}, | 236 | {0x06, NULL, "GetProcessIdealProcessor"}, |
| 145 | {0x07, NULL, "SetProcessIdealProcessor"}, | 237 | {0x07, NULL, "SetProcessIdealProcessor"}, |
| 146 | {0x08, NULL, "CreateThread"}, | 238 | {0x08, WrapI_UUUUU<CreateThread>, "CreateThread"}, |
| 147 | {0x09, NULL, "ExitThread"}, | 239 | {0x09, NULL, "ExitThread"}, |
| 148 | {0x0A, NULL, "SleepThread"}, | 240 | {0x0A, NULL, "SleepThread"}, |
| 149 | {0x0B, NULL, "GetThreadPriority"}, | 241 | {0x0B, NULL, "GetThreadPriority"}, |
| @@ -154,11 +246,11 @@ const HLE::FunctionDef Syscall_Table[] = { | |||
| 154 | {0x10, NULL, "SetThreadIdealProcessor"}, | 246 | {0x10, NULL, "SetThreadIdealProcessor"}, |
| 155 | {0x11, NULL, "GetCurrentProcessorNumber"}, | 247 | {0x11, NULL, "GetCurrentProcessorNumber"}, |
| 156 | {0x12, NULL, "Run"}, | 248 | {0x12, NULL, "Run"}, |
| 157 | {0x13, NULL, "CreateMutex"}, | 249 | {0x13, WrapI_VU<CreateMutex>, "CreateMutex"}, |
| 158 | {0x14, NULL, "ReleaseMutex"}, | 250 | {0x14, WrapI_U<ReleaseMutex>, "ReleaseMutex"}, |
| 159 | {0x15, NULL, "CreateSemaphore"}, | 251 | {0x15, NULL, "CreateSemaphore"}, |
| 160 | {0x16, NULL, "ReleaseSemaphore"}, | 252 | {0x16, NULL, "ReleaseSemaphore"}, |
| 161 | {0x17, NULL, "CreateEvent"}, | 253 | {0x17, WrapI_VU<CreateEvent>, "CreateEvent"}, |
| 162 | {0x18, NULL, "SignalEvent"}, | 254 | {0x18, NULL, "SignalEvent"}, |
| 163 | {0x19, NULL, "ClearEvent"}, | 255 | {0x19, NULL, "ClearEvent"}, |
| 164 | {0x1A, NULL, "CreateTimer"}, | 256 | {0x1A, NULL, "CreateTimer"}, |
| @@ -172,7 +264,7 @@ const HLE::FunctionDef Syscall_Table[] = { | |||
| 172 | {0x22, NULL, "ArbitrateAddress"}, | 264 | {0x22, NULL, "ArbitrateAddress"}, |
| 173 | {0x23, WrapI_U<CloseHandle>, "CloseHandle"}, | 265 | {0x23, WrapI_U<CloseHandle>, "CloseHandle"}, |
| 174 | {0x24, WrapI_US64<WaitSynchronization1>, "WaitSynchronization1"}, | 266 | {0x24, WrapI_US64<WaitSynchronization1>, "WaitSynchronization1"}, |
| 175 | {0x25, NULL, "WaitSynchronizationN"}, | 267 | {0x25, WrapI_VVUUS64<WaitSynchronizationN>, "WaitSynchronizationN"}, |
| 176 | {0x26, NULL, "SignalAndWait"}, | 268 | {0x26, NULL, "SignalAndWait"}, |
| 177 | {0x27, NULL, "DuplicateHandle"}, | 269 | {0x27, NULL, "DuplicateHandle"}, |
| 178 | {0x28, NULL, "GetSystemTick"}, | 270 | {0x28, NULL, "GetSystemTick"}, |
| @@ -190,7 +282,7 @@ const HLE::FunctionDef Syscall_Table[] = { | |||
| 190 | {0x34, NULL, "OpenThread"}, | 282 | {0x34, NULL, "OpenThread"}, |
| 191 | {0x35, NULL, "GetProcessId"}, | 283 | {0x35, NULL, "GetProcessId"}, |
| 192 | {0x36, NULL, "GetProcessIdOfThread"}, | 284 | {0x36, NULL, "GetProcessIdOfThread"}, |
| 193 | {0x37, NULL, "GetThreadId"}, | 285 | {0x37, WrapI_VU<GetThreadId>, "GetThreadId"}, |
| 194 | {0x38, WrapI_VU<GetResourceLimit>, "GetResourceLimit"}, | 286 | {0x38, WrapI_VU<GetResourceLimit>, "GetResourceLimit"}, |
| 195 | {0x39, NULL, "GetResourceLimitLimitValues"}, | 287 | {0x39, NULL, "GetResourceLimitLimitValues"}, |
| 196 | {0x3A, WrapI_VUVI<GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues"}, | 288 | {0x3A, WrapI_VUVI<GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues"}, |
| @@ -264,7 +356,7 @@ const HLE::FunctionDef Syscall_Table[] = { | |||
| 264 | }; | 356 | }; |
| 265 | 357 | ||
| 266 | void Register() { | 358 | void Register() { |
| 267 | HLE::RegisterModule("SyscallTable", ARRAY_SIZE(Syscall_Table), Syscall_Table); | 359 | HLE::RegisterModule("SVC_Table", ARRAY_SIZE(SVC_Table), SVC_Table); |
| 268 | } | 360 | } |
| 269 | 361 | ||
| 270 | } // namespace | 362 | } // namespace |
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h new file mode 100644 index 000000000..5c35977d1 --- /dev/null +++ b/src/core/hle/svc.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // SVC types | ||
| 11 | |||
| 12 | struct MemoryInfo { | ||
| 13 | u32 base_address; | ||
| 14 | u32 size; | ||
| 15 | u32 permission; | ||
| 16 | u32 state; | ||
| 17 | }; | ||
| 18 | |||
| 19 | struct PageInfo { | ||
| 20 | u32 flags; | ||
| 21 | }; | ||
| 22 | |||
| 23 | struct ThreadContext { | ||
| 24 | u32 cpu_registers[13]; | ||
| 25 | u32 sp; | ||
| 26 | u32 lr; | ||
| 27 | u32 pc; | ||
| 28 | u32 cpsr; | ||
| 29 | u32 fpu_registers[32]; | ||
| 30 | u32 fpscr; | ||
| 31 | u32 fpexc; | ||
| 32 | }; | ||
| 33 | |||
| 34 | enum ResetType { | ||
| 35 | RESETTYPE_ONESHOT, | ||
| 36 | RESETTYPE_STICKY, | ||
| 37 | RESETTYPE_PULSE, | ||
| 38 | RESETTYPE_MAX_BIT = (1u << 31), | ||
| 39 | }; | ||
| 40 | |||
| 41 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 42 | // Namespace SVC | ||
| 43 | |||
| 44 | namespace SVC { | ||
| 45 | |||
| 46 | void Register(); | ||
| 47 | |||
| 48 | } // namespace | ||
diff --git a/src/core/hle/syscall.h b/src/core/hle/syscall.h deleted file mode 100644 index 7a94e0136..000000000 --- a/src/core/hle/syscall.h +++ /dev/null | |||
| @@ -1,19 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 10 | // Namespace Syscall | ||
| 11 | |||
| 12 | namespace Syscall { | ||
| 13 | |||
| 14 | typedef u32 Handle; | ||
| 15 | typedef s32 Result; | ||
| 16 | |||
| 17 | void Register(); | ||
| 18 | |||
| 19 | } // namespace | ||
diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp index 6468053f2..b57563a73 100644 --- a/src/core/hw/lcd.cpp +++ b/src/core/hw/lcd.cpp | |||
| @@ -11,6 +11,8 @@ | |||
| 11 | 11 | ||
| 12 | #include "video_core/video_core.h" | 12 | #include "video_core/video_core.h" |
| 13 | 13 | ||
| 14 | #include "core/hle/kernel/thread.h" | ||
| 15 | |||
| 14 | namespace LCD { | 16 | namespace LCD { |
| 15 | 17 | ||
| 16 | Registers g_regs; | 18 | Registers g_regs; |
| @@ -130,9 +132,11 @@ template void Write<u8>(u32 addr, const u8 data); | |||
| 130 | void Update() { | 132 | void Update() { |
| 131 | u64 current_ticks = Core::g_app_core->GetTicks(); | 133 | u64 current_ticks = Core::g_app_core->GetTicks(); |
| 132 | 134 | ||
| 135 | // Fake a vertical blank | ||
| 133 | if ((current_ticks - g_last_ticks) >= kFrameTicks) { | 136 | if ((current_ticks - g_last_ticks) >= kFrameTicks) { |
| 134 | g_last_ticks = current_ticks; | 137 | g_last_ticks = current_ticks; |
| 135 | VideoCore::g_renderer->SwapBuffers(); | 138 | VideoCore::g_renderer->SwapBuffers(); |
| 139 | Kernel::WaitCurrentThread(WAITTYPE_VBLANK); | ||
| 136 | } | 140 | } |
| 137 | } | 141 | } |
| 138 | 142 | ||
diff --git a/src/core/loader.cpp b/src/core/loader.cpp index 8756588ae..ff1c873bb 100644 --- a/src/core/loader.cpp +++ b/src/core/loader.cpp | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/file_sys/directory_file_system.h" | 11 | #include "core/file_sys/directory_file_system.h" |
| 12 | #include "core/elf/elf_reader.h" | 12 | #include "core/elf/elf_reader.h" |
| 13 | 13 | #include "core/hle/kernel/kernel.h" | |
| 14 | #include "core/mem_map.h" | 14 | #include "core/mem_map.h" |
| 15 | 15 | ||
| 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -56,7 +56,7 @@ bool Load_ELF(std::string &filename) { | |||
| 56 | elf_reader = new ElfReader(buffer); | 56 | elf_reader = new ElfReader(buffer); |
| 57 | elf_reader->LoadInto(0x00100000); | 57 | elf_reader->LoadInto(0x00100000); |
| 58 | 58 | ||
| 59 | Core::g_app_core->SetPC(elf_reader->GetEntryPoint()); | 59 | Kernel::LoadExec(elf_reader->GetEntryPoint()); |
| 60 | 60 | ||
| 61 | delete[] buffer; | 61 | delete[] buffer; |
| 62 | delete elf_reader; | 62 | delete elf_reader; |
| @@ -89,11 +89,11 @@ bool Load_DAT(std::string &filename) { | |||
| 89 | * but for the sake of making it easier... we'll temporarily/hackishly | 89 | * but for the sake of making it easier... we'll temporarily/hackishly |
| 90 | * allow it. No sense in making a proper reader for this. | 90 | * allow it. No sense in making a proper reader for this. |
| 91 | */ | 91 | */ |
| 92 | u32 entrypoint = 0x00100000; // write to same entrypoint as elf | 92 | u32 entry_point = 0x00100000; // write to same entrypoint as elf |
| 93 | u32 payload_offset = 0xA150; | 93 | u32 payload_offset = 0xA150; |
| 94 | 94 | ||
| 95 | const u8 *src = &buffer[payload_offset]; | 95 | const u8 *src = &buffer[payload_offset]; |
| 96 | u8 *dst = Memory::GetPointer(entrypoint); | 96 | u8 *dst = Memory::GetPointer(entry_point); |
| 97 | u32 srcSize = size - payload_offset; //just load everything... | 97 | u32 srcSize = size - payload_offset; //just load everything... |
| 98 | u32 *s = (u32*)src; | 98 | u32 *s = (u32*)src; |
| 99 | u32 *d = (u32*)dst; | 99 | u32 *d = (u32*)dst; |
| @@ -102,7 +102,8 @@ bool Load_DAT(std::string &filename) { | |||
| 102 | *d++ = (*s++); | 102 | *d++ = (*s++); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | Core::g_app_core->SetPC(entrypoint); | 105 | Kernel::LoadExec(entry_point); |
| 106 | |||
| 106 | 107 | ||
| 107 | delete[] buffer; | 108 | delete[] buffer; |
| 108 | } | 109 | } |
| @@ -131,10 +132,10 @@ bool Load_BIN(std::string &filename) { | |||
| 131 | 132 | ||
| 132 | f.ReadBytes(buffer, size); | 133 | f.ReadBytes(buffer, size); |
| 133 | 134 | ||
| 134 | u32 entrypoint = 0x00100000; // Hardcoded, read from exheader | 135 | u32 entry_point = 0x00100000; // Hardcoded, read from exheader |
| 135 | 136 | ||
| 136 | const u8 *src = buffer; | 137 | const u8 *src = buffer; |
| 137 | u8 *dst = Memory::GetPointer(entrypoint); | 138 | u8 *dst = Memory::GetPointer(entry_point); |
| 138 | u32 srcSize = size; | 139 | u32 srcSize = size; |
| 139 | u32 *s = (u32*)src; | 140 | u32 *s = (u32*)src; |
| 140 | u32 *d = (u32*)dst; | 141 | u32 *d = (u32*)dst; |
| @@ -143,7 +144,7 @@ bool Load_BIN(std::string &filename) { | |||
| 143 | *d++ = (*s++); | 144 | *d++ = (*s++); |
| 144 | } | 145 | } |
| 145 | 146 | ||
| 146 | Core::g_app_core->SetPC(entrypoint); | 147 | Kernel::LoadExec(entry_point); |
| 147 | 148 | ||
| 148 | delete[] buffer; | 149 | delete[] buffer; |
| 149 | } | 150 | } |
| @@ -186,6 +187,9 @@ FileType IdentifyFile(std::string &filename) { | |||
| 186 | else if (!strcasecmp(extension.c_str(), ".elf")) { | 187 | else if (!strcasecmp(extension.c_str(), ".elf")) { |
| 187 | return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p | 188 | return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p |
| 188 | } | 189 | } |
| 190 | else if (!strcasecmp(extension.c_str(), ".axf")) { | ||
| 191 | return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p | ||
| 192 | } | ||
| 189 | else if (!strcasecmp(extension.c_str(), ".bin")) { | 193 | else if (!strcasecmp(extension.c_str(), ".bin")) { |
| 190 | return FILETYPE_CTR_BIN; | 194 | return FILETYPE_CTR_BIN; |
| 191 | } | 195 | } |
diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp index 59560b87d..c45746be9 100644 --- a/src/core/mem_map.cpp +++ b/src/core/mem_map.cpp | |||
| @@ -17,6 +17,7 @@ u8* g_base = NULL; ///< The base pointer to the aut | |||
| 17 | MemArena g_arena; ///< The MemArena class | 17 | MemArena g_arena; ///< The MemArena class |
| 18 | 18 | ||
| 19 | u8* g_exefs_code = NULL; ///< ExeFS:/.code is loaded here | 19 | u8* g_exefs_code = NULL; ///< ExeFS:/.code is loaded here |
| 20 | u8* g_system_mem = NULL; ///< System memory | ||
| 20 | u8* g_heap = NULL; ///< Application heap (main memory) | 21 | u8* g_heap = NULL; ///< Application heap (main memory) |
| 21 | u8* g_heap_gsp = NULL; ///< GSP heap (main memory) | 22 | u8* g_heap_gsp = NULL; ///< GSP heap (main memory) |
| 22 | u8* g_vram = NULL; ///< Video memory (VRAM) pointer | 23 | u8* g_vram = NULL; ///< Video memory (VRAM) pointer |
| @@ -27,6 +28,7 @@ u8* g_physical_bootrom = NULL; ///< Bootrom physical memory | |||
| 27 | u8* g_uncached_bootrom = NULL; | 28 | u8* g_uncached_bootrom = NULL; |
| 28 | 29 | ||
| 29 | u8* g_physical_exefs_code = NULL; ///< Phsical ExeFS:/.code is loaded here | 30 | u8* g_physical_exefs_code = NULL; ///< Phsical ExeFS:/.code is loaded here |
| 31 | u8* g_physical_system_mem = NULL; ///< System physical memory | ||
| 30 | u8* g_physical_fcram = NULL; ///< Main physical memory (FCRAM) | 32 | u8* g_physical_fcram = NULL; ///< Main physical memory (FCRAM) |
| 31 | u8* g_physical_heap_gsp = NULL; ///< GSP heap physical memory | 33 | u8* g_physical_heap_gsp = NULL; ///< GSP heap physical memory |
| 32 | u8* g_physical_vram = NULL; ///< Video physical memory (VRAM) | 34 | u8* g_physical_vram = NULL; ///< Video physical memory (VRAM) |
| @@ -39,6 +41,7 @@ static MemoryView g_views[] = { | |||
| 39 | {&g_vram, &g_physical_vram, VRAM_VADDR, VRAM_SIZE, 0}, | 41 | {&g_vram, &g_physical_vram, VRAM_VADDR, VRAM_SIZE, 0}, |
| 40 | {&g_heap, &g_physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM}, | 42 | {&g_heap, &g_physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM}, |
| 41 | {&g_shared_mem, &g_physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0}, | 43 | {&g_shared_mem, &g_physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0}, |
| 44 | {&g_system_mem, &g_physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0}, | ||
| 42 | {&g_kernel_mem, &g_physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0}, | 45 | {&g_kernel_mem, &g_physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0}, |
| 43 | {&g_heap_gsp, &g_physical_heap_gsp, HEAP_GSP_VADDR, HEAP_GSP_SIZE, 0}, | 46 | {&g_heap_gsp, &g_physical_heap_gsp, HEAP_GSP_VADDR, HEAP_GSP_SIZE, 0}, |
| 44 | }; | 47 | }; |
diff --git a/src/core/mem_map.h b/src/core/mem_map.h index af2212a5f..12d497ef3 100644 --- a/src/core/mem_map.h +++ b/src/core/mem_map.h | |||
| @@ -47,6 +47,12 @@ enum { | |||
| 47 | EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE), | 47 | EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE), |
| 48 | EXEFS_CODE_MASK = 0x03FFFFFF, | 48 | EXEFS_CODE_MASK = 0x03FFFFFF, |
| 49 | 49 | ||
| 50 | // Region of FCRAM used by system | ||
| 51 | SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB | ||
| 52 | SYSTEM_MEMORY_VADDR = 0x04000000, | ||
| 53 | SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE), | ||
| 54 | SYSTEM_MEMORY_MASK = 0x03FFFFFF, | ||
| 55 | |||
| 50 | HEAP_SIZE = FCRAM_SIZE, ///< Application heap size | 56 | HEAP_SIZE = FCRAM_SIZE, ///< Application heap size |
| 51 | //HEAP_PADDR = HEAP_GSP_SIZE, | 57 | //HEAP_PADDR = HEAP_GSP_SIZE, |
| 52 | //HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE), | 58 | //HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE), |
| @@ -116,6 +122,7 @@ extern u8* g_heap; ///< Application heap (main memory) | |||
| 116 | extern u8* g_vram; ///< Video memory (VRAM) | 122 | extern u8* g_vram; ///< Video memory (VRAM) |
| 117 | extern u8* g_shared_mem; ///< Shared memory | 123 | extern u8* g_shared_mem; ///< Shared memory |
| 118 | extern u8* g_kernel_mem; ///< Kernel memory | 124 | extern u8* g_kernel_mem; ///< Kernel memory |
| 125 | extern u8* g_system_mem; ///< System memory | ||
| 119 | extern u8* g_exefs_code; ///< ExeFS:/.code is loaded here | 126 | extern u8* g_exefs_code; ///< ExeFS:/.code is loaded here |
| 120 | 127 | ||
| 121 | void Init(); | 128 | void Init(); |
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp index 8ab647714..86e9eaa20 100644 --- a/src/core/mem_map_funcs.cpp +++ b/src/core/mem_map_funcs.cpp | |||
| @@ -73,6 +73,10 @@ inline void _Read(T &var, const u32 addr) { | |||
| 73 | } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { | 73 | } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { |
| 74 | var = *((const T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK]); | 74 | var = *((const T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK]); |
| 75 | 75 | ||
| 76 | // System memory | ||
| 77 | } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { | ||
| 78 | var = *((const T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK]); | ||
| 79 | |||
| 76 | // Config memory | 80 | // Config memory |
| 77 | } else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) { | 81 | } else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) { |
| 78 | ConfigMem::Read<T>(var, vaddr); | 82 | ConfigMem::Read<T>(var, vaddr); |
| @@ -115,6 +119,10 @@ inline void _Write(u32 addr, const T data) { | |||
| 115 | } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { | 119 | } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { |
| 116 | *(T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK] = data; | 120 | *(T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK] = data; |
| 117 | 121 | ||
| 122 | // System memory | ||
| 123 | } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { | ||
| 124 | *(T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK] = data; | ||
| 125 | |||
| 118 | // VRAM | 126 | // VRAM |
| 119 | } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { | 127 | } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { |
| 120 | *(T*)&g_vram[vaddr & VRAM_MASK] = data; | 128 | *(T*)&g_vram[vaddr & VRAM_MASK] = data; |
| @@ -153,9 +161,13 @@ u8 *GetPointer(const u32 addr) { | |||
| 153 | return g_heap + (vaddr & HEAP_MASK); | 161 | return g_heap + (vaddr & HEAP_MASK); |
| 154 | 162 | ||
| 155 | // Shared memory | 163 | // Shared memory |
| 156 | } else if ((vaddr > SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { | 164 | } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { |
| 157 | return g_shared_mem + (vaddr & SHARED_MEMORY_MASK); | 165 | return g_shared_mem + (vaddr & SHARED_MEMORY_MASK); |
| 158 | 166 | ||
| 167 | // System memory | ||
| 168 | } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { | ||
| 169 | return g_system_mem + (vaddr & SYSTEM_MEMORY_MASK); | ||
| 170 | |||
| 159 | // VRAM | 171 | // VRAM |
| 160 | } else if ((vaddr > VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { | 172 | } else if ((vaddr > VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { |
| 161 | return g_vram + (vaddr & VRAM_MASK); | 173 | return g_vram + (vaddr & VRAM_MASK); |