diff options
Diffstat (limited to 'src')
68 files changed, 1368 insertions, 1274 deletions
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index bbb3374f2..713f49193 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt | |||
| @@ -16,20 +16,6 @@ create_directory_groups(${SRCS} ${HEADERS}) | |||
| 16 | add_executable(citra ${SRCS} ${HEADERS}) | 16 | add_executable(citra ${SRCS} ${HEADERS}) |
| 17 | target_link_libraries(citra core common video_core) | 17 | target_link_libraries(citra core common video_core) |
| 18 | target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih) | 18 | target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih) |
| 19 | 19 | target_link_libraries(citra ${PLATFORM_LIBRARIES}) | |
| 20 | if (UNIX) | ||
| 21 | target_link_libraries(citra -pthread) | ||
| 22 | endif() | ||
| 23 | |||
| 24 | if (APPLE) | ||
| 25 | target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY}) | ||
| 26 | elseif (WIN32) | ||
| 27 | target_link_libraries(citra winmm wsock32 ws2_32) | ||
| 28 | if (MINGW) # GCC does not support codecvt, so use iconv instead | ||
| 29 | target_link_libraries(citra iconv) | ||
| 30 | endif() | ||
| 31 | else() # Unix | ||
| 32 | target_link_libraries(citra rt) | ||
| 33 | endif() | ||
| 34 | 20 | ||
| 35 | #install(TARGETS citra RUNTIME DESTINATION ${bindir}) | 21 | #install(TARGETS citra RUNTIME DESTINATION ${bindir}) |
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index a0ba252b3..bbc521f8a 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -60,17 +60,6 @@ endif() | |||
| 60 | add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) | 60 | add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) |
| 61 | target_link_libraries(citra-qt core common video_core qhexedit) | 61 | target_link_libraries(citra-qt core common video_core qhexedit) |
| 62 | target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) | 62 | target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) |
| 63 | 63 | target_link_libraries(citra-qt ${PLATFORM_LIBRARIES}) | |
| 64 | if (UNIX) | ||
| 65 | target_link_libraries(citra-qt -pthread) | ||
| 66 | endif() | ||
| 67 | |||
| 68 | if (APPLE) | ||
| 69 | target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY}) | ||
| 70 | elseif (WIN32) | ||
| 71 | target_link_libraries(citra-qt winmm wsock32 ws2_32) | ||
| 72 | else() # Unix | ||
| 73 | target_link_libraries(citra-qt rt) | ||
| 74 | endif() | ||
| 75 | 64 | ||
| 76 | #install(TARGETS citra-qt RUNTIME DESTINATION ${bindir}) | 65 | #install(TARGETS citra-qt RUNTIME DESTINATION ${bindir}) |
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp index ab317a723..025a5896b 100644 --- a/src/citra_qt/debugger/callstack.cpp +++ b/src/citra_qt/debugger/callstack.cpp | |||
| @@ -38,6 +38,9 @@ void CallstackWidget::OnDebugModeEntered() | |||
| 38 | { | 38 | { |
| 39 | ret_addr = Memory::Read32(addr); | 39 | ret_addr = Memory::Read32(addr); |
| 40 | call_addr = ret_addr - 4; //get call address??? | 40 | call_addr = ret_addr - 4; //get call address??? |
| 41 | |||
| 42 | if (Memory::GetPointer(call_addr) == nullptr) | ||
| 43 | break; | ||
| 41 | 44 | ||
| 42 | /* TODO (mattvail) clean me, move to debugger interface */ | 45 | /* TODO (mattvail) clean me, move to debugger interface */ |
| 43 | u32 insn = Memory::Read32(call_addr); | 46 | u32 insn = Memory::Read32(call_addr); |
diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp index 3a1940015..c61ace925 100644 --- a/src/citra_qt/debugger/disassembler.cpp +++ b/src/citra_qt/debugger/disassembler.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "core/core.h" | 13 | #include "core/core.h" |
| 14 | #include "common/break_points.h" | 14 | #include "common/break_points.h" |
| 15 | #include "common/symbols.h" | 15 | #include "common/symbols.h" |
| 16 | #include "core/arm/arm_interface.h" | ||
| 16 | #include "core/arm/skyeye_common/armdefs.h" | 17 | #include "core/arm/skyeye_common/armdefs.h" |
| 17 | #include "core/arm/disassembler/arm_disasm.h" | 18 | #include "core/arm/disassembler/arm_disasm.h" |
| 18 | 19 | ||
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp index caa6896f9..a9423d6c7 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.cpp +++ b/src/citra_qt/debugger/graphics_framebuffer.cpp | |||
| @@ -158,17 +158,17 @@ void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value) | |||
| 158 | } | 158 | } |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | void GraphicsFramebufferWidget::OnFramebufferWidthChanged(unsigned int new_value) | 161 | void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value) |
| 162 | { | 162 | { |
| 163 | if (framebuffer_width != new_value) { | 163 | if (framebuffer_width != static_cast<unsigned>(new_value)) { |
| 164 | framebuffer_width = new_value; | 164 | framebuffer_width = static_cast<unsigned>(new_value); |
| 165 | 165 | ||
| 166 | framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); | 166 | framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); |
| 167 | emit Update(); | 167 | emit Update(); |
| 168 | } | 168 | } |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | void GraphicsFramebufferWidget::OnFramebufferHeightChanged(unsigned int new_value) | 171 | void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value) |
| 172 | { | 172 | { |
| 173 | if (framebuffer_height != new_value) { | 173 | if (framebuffer_height != new_value) { |
| 174 | framebuffer_height = new_value; | 174 | framebuffer_height = new_value; |
diff --git a/src/citra_qt/debugger/graphics_framebuffer.h b/src/citra_qt/debugger/graphics_framebuffer.h index 02813525c..56215761e 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.h +++ b/src/citra_qt/debugger/graphics_framebuffer.h | |||
| @@ -62,8 +62,8 @@ public: | |||
| 62 | public slots: | 62 | public slots: |
| 63 | void OnFramebufferSourceChanged(int new_value); | 63 | void OnFramebufferSourceChanged(int new_value); |
| 64 | void OnFramebufferAddressChanged(qint64 new_value); | 64 | void OnFramebufferAddressChanged(qint64 new_value); |
| 65 | void OnFramebufferWidthChanged(unsigned int new_value); | 65 | void OnFramebufferWidthChanged(int new_value); |
| 66 | void OnFramebufferHeightChanged(unsigned int new_value); | 66 | void OnFramebufferHeightChanged(int new_value); |
| 67 | void OnFramebufferFormatChanged(int new_value); | 67 | void OnFramebufferFormatChanged(int new_value); |
| 68 | void OnUpdate(); | 68 | void OnUpdate(); |
| 69 | 69 | ||
diff --git a/src/common/common.h b/src/common/common.h index ba33373ae..3246c7797 100644 --- a/src/common/common.h +++ b/src/common/common.h | |||
| @@ -11,13 +11,6 @@ | |||
| 11 | #include <cstdio> | 11 | #include <cstdio> |
| 12 | #include <cstring> | 12 | #include <cstring> |
| 13 | 13 | ||
| 14 | // Force enable logging in the right modes. For some reason, something had changed | ||
| 15 | // so that debugfast no longer logged. | ||
| 16 | #if defined(_DEBUG) || defined(DEBUGFAST) | ||
| 17 | #undef LOGGING | ||
| 18 | #define LOGGING 1 | ||
| 19 | #endif | ||
| 20 | |||
| 21 | #define STACKALIGN | 14 | #define STACKALIGN |
| 22 | 15 | ||
| 23 | // An inheritable class to disallow the copy constructor and operator= functions | 16 | // An inheritable class to disallow the copy constructor and operator= functions |
| @@ -154,16 +147,10 @@ enum EMUSTATE_CHANGE | |||
| 154 | 147 | ||
| 155 | 148 | ||
| 156 | #ifdef _MSC_VER | 149 | #ifdef _MSC_VER |
| 157 | #ifndef _XBOX | ||
| 158 | inline unsigned long long bswap64(unsigned long long x) { return _byteswap_uint64(x); } | 150 | inline unsigned long long bswap64(unsigned long long x) { return _byteswap_uint64(x); } |
| 159 | inline unsigned int bswap32(unsigned int x) { return _byteswap_ulong(x); } | 151 | inline unsigned int bswap32(unsigned int x) { return _byteswap_ulong(x); } |
| 160 | inline unsigned short bswap16(unsigned short x) { return _byteswap_ushort(x); } | 152 | inline unsigned short bswap16(unsigned short x) { return _byteswap_ushort(x); } |
| 161 | #else | 153 | #else |
| 162 | inline unsigned long long bswap64(unsigned long long x) { return __loaddoublewordbytereverse(0, &x); } | ||
| 163 | inline unsigned int bswap32(unsigned int x) { return __loadwordbytereverse(0, &x); } | ||
| 164 | inline unsigned short bswap16(unsigned short x) { return __loadshortbytereverse(0, &x); } | ||
| 165 | #endif | ||
| 166 | #else | ||
| 167 | // TODO: speedup | 154 | // TODO: speedup |
| 168 | inline unsigned short bswap16(unsigned short x) { return (x << 8) | (x >> 8); } | 155 | inline unsigned short bswap16(unsigned short x) { return (x << 8) | (x >> 8); } |
| 169 | inline unsigned int bswap32(unsigned int x) { return (x >> 24) | ((x & 0xFF0000) >> 8) | ((x & 0xFF00) << 8) | (x << 24);} | 156 | inline unsigned int bswap32(unsigned int x) { return (x >> 24) | ((x & 0xFF0000) >> 8) | ((x & 0xFF00) << 8) | (x << 24);} |
diff --git a/src/common/common_paths.h b/src/common/common_paths.h index e692e5492..0ecf2d9de 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h | |||
| @@ -35,26 +35,23 @@ | |||
| 35 | #define JAP_DIR "JAP" | 35 | #define JAP_DIR "JAP" |
| 36 | 36 | ||
| 37 | // Subdirs in the User dir returned by GetUserPath(D_USER_IDX) | 37 | // Subdirs in the User dir returned by GetUserPath(D_USER_IDX) |
| 38 | #define CONFIG_DIR "config" | 38 | #define CONFIG_DIR "config" |
| 39 | #define GAMECONFIG_DIR "game_config" | 39 | #define GAMECONFIG_DIR "game_config" |
| 40 | #define MAPS_DIR "maps" | 40 | #define MAPS_DIR "maps" |
| 41 | #define CACHE_DIR "cache" | 41 | #define CACHE_DIR "cache" |
| 42 | #define SDMC_DIR "sdmc" | 42 | #define SDMC_DIR "sdmc" |
| 43 | #define EXTSAVEDATA_DIR "extsavedata" | 43 | #define NAND_DIR "nand" |
| 44 | #define SAVEDATA_DIR "savedata" | 44 | #define SYSDATA_DIR "sysdata" |
| 45 | #define SAVEDATACHECK_DIR "savedatacheck" | 45 | #define SHADERCACHE_DIR "shader_cache" |
| 46 | #define SYSDATA_DIR "sysdata" | 46 | #define STATESAVES_DIR "state_saves" |
| 47 | #define SYSSAVEDATA_DIR "syssavedata" | 47 | #define SCREENSHOTS_DIR "screenShots" |
| 48 | #define SHADERCACHE_DIR "shader_cache" | 48 | #define DUMP_DIR "dump" |
| 49 | #define STATESAVES_DIR "state_saves" | 49 | #define DUMP_TEXTURES_DIR "textures" |
| 50 | #define SCREENSHOTS_DIR "screenShots" | 50 | #define DUMP_FRAMES_DIR "frames" |
| 51 | #define DUMP_DIR "dump" | 51 | #define DUMP_AUDIO_DIR "audio" |
| 52 | #define DUMP_TEXTURES_DIR "textures" | 52 | #define LOGS_DIR "logs" |
| 53 | #define DUMP_FRAMES_DIR "frames" | 53 | #define SHADERS_DIR "shaders" |
| 54 | #define DUMP_AUDIO_DIR "audio" | 54 | #define SYSCONF_DIR "sysconf" |
| 55 | #define LOGS_DIR "logs" | ||
| 56 | #define SHADERS_DIR "shaders" | ||
| 57 | #define SYSCONF_DIR "sysconf" | ||
| 58 | 55 | ||
| 59 | // Filenames | 56 | // Filenames |
| 60 | // Files in the directory returned by GetUserPath(D_CONFIG_IDX) | 57 | // Files in the directory returned by GetUserPath(D_CONFIG_IDX) |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 0a6cd80c8..706e7c842 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -676,11 +676,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 676 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | 676 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; |
| 677 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | 677 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; |
| 678 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | 678 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; |
| 679 | paths[D_EXTSAVEDATA] = paths[D_USER_IDX] + EXTSAVEDATA_DIR DIR_SEP; | 679 | paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; |
| 680 | paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | ||
| 681 | paths[D_SAVEDATACHECK_IDX] = paths[D_USER_IDX] + SAVEDATACHECK_DIR DIR_SEP; | ||
| 682 | paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; | 680 | paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; |
| 683 | paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP; | ||
| 684 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | 681 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; |
| 685 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | 682 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; |
| 686 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | 683 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; |
| @@ -722,10 +719,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 722 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | 719 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; |
| 723 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | 720 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; |
| 724 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | 721 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; |
| 725 | paths[D_EXTSAVEDATA] = paths[D_USER_IDX] + EXTSAVEDATA_DIR DIR_SEP; | 722 | paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; |
| 726 | paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | ||
| 727 | paths[D_SAVEDATACHECK_IDX] = paths[D_USER_IDX] + SAVEDATACHECK_DIR DIR_SEP; | ||
| 728 | paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP; | ||
| 729 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | 723 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; |
| 730 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | 724 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; |
| 731 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | 725 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; |
diff --git a/src/common/file_util.h b/src/common/file_util.h index c83ecd87d..86aab2e3d 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -27,11 +27,8 @@ enum { | |||
| 27 | D_STATESAVES_IDX, | 27 | D_STATESAVES_IDX, |
| 28 | D_SCREENSHOTS_IDX, | 28 | D_SCREENSHOTS_IDX, |
| 29 | D_SDMC_IDX, | 29 | D_SDMC_IDX, |
| 30 | D_EXTSAVEDATA, | 30 | D_NAND_IDX, |
| 31 | D_SAVEDATA_IDX, | ||
| 32 | D_SAVEDATACHECK_IDX, | ||
| 33 | D_SYSDATA_IDX, | 31 | D_SYSDATA_IDX, |
| 34 | D_SYSSAVEDATA_IDX, | ||
| 35 | D_HIRESTEXTURES_IDX, | 32 | D_HIRESTEXTURES_IDX, |
| 36 | D_DUMP_IDX, | 33 | D_DUMP_IDX, |
| 37 | D_DUMPFRAMES_IDX, | 34 | D_DUMPFRAMES_IDX, |
diff --git a/src/common/log.h b/src/common/log.h index 667f2fbb9..b397cf14d 100644 --- a/src/common/log.h +++ b/src/common/log.h | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | #endif | 14 | #endif |
| 15 | #endif | 15 | #endif |
| 16 | 16 | ||
| 17 | #if _DEBUG | 17 | #ifdef _DEBUG |
| 18 | #define _dbg_assert_(_t_, _a_) \ | 18 | #define _dbg_assert_(_t_, _a_) \ |
| 19 | if (!(_a_)) {\ | 19 | if (!(_a_)) {\ |
| 20 | LOG_CRITICAL(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \ | 20 | LOG_CRITICAL(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \ |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 7ac30ad50..83ebb42d9 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -22,6 +22,7 @@ static std::shared_ptr<Logger> global_logger; | |||
| 22 | SUB(Common, Memory) \ | 22 | SUB(Common, Memory) \ |
| 23 | CLS(Core) \ | 23 | CLS(Core) \ |
| 24 | SUB(Core, ARM11) \ | 24 | SUB(Core, ARM11) \ |
| 25 | SUB(Core, Timing) \ | ||
| 25 | CLS(Config) \ | 26 | CLS(Config) \ |
| 26 | CLS(Debug) \ | 27 | CLS(Debug) \ |
| 27 | SUB(Debug, Emulated) \ | 28 | SUB(Debug, Emulated) \ |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 06b99b07a..3d94bf0d9 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -41,6 +41,7 @@ enum class Class : ClassType { | |||
| 41 | Common_Memory, ///< Memory mapping and management functions | 41 | Common_Memory, ///< Memory mapping and management functions |
| 42 | Core, ///< LLE emulation core | 42 | Core, ///< LLE emulation core |
| 43 | Core_ARM11, ///< ARM11 CPU core | 43 | Core_ARM11, ///< ARM11 CPU core |
| 44 | Core_Timing, ///< CoreTiming functions | ||
| 44 | Config, ///< Emulator configuration (including commandline) | 45 | Config, ///< Emulator configuration (including commandline) |
| 45 | Debug, ///< Debugging tools | 46 | Debug, ///< Debugging tools |
| 46 | Debug_Emulated, ///< Debug messages from the emulated programs | 47 | Debug_Emulated, ///< Debug messages from the emulated programs |
| @@ -73,17 +74,6 @@ enum class Class : ClassType { | |||
| 73 | }; | 74 | }; |
| 74 | 75 | ||
| 75 | /** | 76 | /** |
| 76 | * Level below which messages are simply discarded without buffering regardless of the display | ||
| 77 | * settings. | ||
| 78 | */ | ||
| 79 | const Level MINIMUM_LEVEL = | ||
| 80 | #ifdef _DEBUG | ||
| 81 | Level::Trace; | ||
| 82 | #else | ||
| 83 | Level::Debug; | ||
| 84 | #endif | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Logs a message to the global logger. This proxy exists to avoid exposing the details of the | 77 | * Logs a message to the global logger. This proxy exists to avoid exposing the details of the |
| 88 | * Logger class, including the ConcurrentRingBuffer template, to all files that desire to log | 78 | * Logger class, including the ConcurrentRingBuffer template, to all files that desire to log |
| 89 | * messages, reducing unecessary recompilations. | 79 | * messages, reducing unecessary recompilations. |
| @@ -102,13 +92,15 @@ void LogMessage(Class log_class, Level log_level, | |||
| 102 | } // namespace Log | 92 | } // namespace Log |
| 103 | 93 | ||
| 104 | #define LOG_GENERIC(log_class, log_level, ...) \ | 94 | #define LOG_GENERIC(log_class, log_level, ...) \ |
| 105 | do { \ | 95 | ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \ |
| 106 | if (::Log::Level::log_level >= ::Log::MINIMUM_LEVEL) \ | 96 | __FILE__, __LINE__, __func__, __VA_ARGS__) |
| 107 | ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \ | ||
| 108 | __FILE__, __LINE__, __func__, __VA_ARGS__); \ | ||
| 109 | } while (0) | ||
| 110 | 97 | ||
| 98 | #ifdef _DEBUG | ||
| 111 | #define LOG_TRACE( log_class, ...) LOG_GENERIC(log_class, Trace, __VA_ARGS__) | 99 | #define LOG_TRACE( log_class, ...) LOG_GENERIC(log_class, Trace, __VA_ARGS__) |
| 100 | #else | ||
| 101 | #define LOG_TRACE( log_class, ...) (void(0)) | ||
| 102 | #endif | ||
| 103 | |||
| 112 | #define LOG_DEBUG( log_class, ...) LOG_GENERIC(log_class, Debug, __VA_ARGS__) | 104 | #define LOG_DEBUG( log_class, ...) LOG_GENERIC(log_class, Debug, __VA_ARGS__) |
| 113 | #define LOG_INFO( log_class, ...) LOG_GENERIC(log_class, Info, __VA_ARGS__) | 105 | #define LOG_INFO( log_class, ...) LOG_GENERIC(log_class, Info, __VA_ARGS__) |
| 114 | #define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning, __VA_ARGS__) | 106 | #define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning, __VA_ARGS__) |
diff --git a/src/common/mem_arena.cpp b/src/common/mem_arena.cpp index 9904d2472..a20361d6f 100644 --- a/src/common/mem_arena.cpp +++ b/src/common/mem_arena.cpp | |||
| @@ -29,10 +29,6 @@ | |||
| 29 | #endif | 29 | #endif |
| 30 | #endif | 30 | #endif |
| 31 | 31 | ||
| 32 | #ifdef IOS | ||
| 33 | void* globalbase = nullptr; | ||
| 34 | #endif | ||
| 35 | |||
| 36 | #ifdef ANDROID | 32 | #ifdef ANDROID |
| 37 | 33 | ||
| 38 | // Hopefully this ABI will never change... | 34 | // Hopefully this ABI will never change... |
| @@ -95,7 +91,7 @@ int ashmem_unpin_region(int fd, size_t offset, size_t len) | |||
| 95 | #endif // Android | 91 | #endif // Android |
| 96 | 92 | ||
| 97 | 93 | ||
| 98 | #if defined(_WIN32) && !defined(_XBOX) | 94 | #if defined(_WIN32) |
| 99 | SYSTEM_INFO sysInfo; | 95 | SYSTEM_INFO sysInfo; |
| 100 | #endif | 96 | #endif |
| 101 | 97 | ||
| @@ -103,11 +99,7 @@ SYSTEM_INFO sysInfo; | |||
| 103 | // Windows mappings need to be on 64K boundaries, due to Alpha legacy. | 99 | // Windows mappings need to be on 64K boundaries, due to Alpha legacy. |
| 104 | #ifdef _WIN32 | 100 | #ifdef _WIN32 |
| 105 | size_t roundup(size_t x) { | 101 | size_t roundup(size_t x) { |
| 106 | #ifndef _XBOX | ||
| 107 | int gran = sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x10000; | 102 | int gran = sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x10000; |
| 108 | #else | ||
| 109 | int gran = 0x10000; // 64k in 360 | ||
| 110 | #endif | ||
| 111 | return (x + gran - 1) & ~(gran - 1); | 103 | return (x + gran - 1) & ~(gran - 1); |
| 112 | } | 104 | } |
| 113 | #else | 105 | #else |
| @@ -120,10 +112,8 @@ size_t roundup(size_t x) { | |||
| 120 | void MemArena::GrabLowMemSpace(size_t size) | 112 | void MemArena::GrabLowMemSpace(size_t size) |
| 121 | { | 113 | { |
| 122 | #ifdef _WIN32 | 114 | #ifdef _WIN32 |
| 123 | #ifndef _XBOX | ||
| 124 | hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, (DWORD)(size), nullptr); | 115 | hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, (DWORD)(size), nullptr); |
| 125 | GetSystemInfo(&sysInfo); | 116 | GetSystemInfo(&sysInfo); |
| 126 | #endif | ||
| 127 | #elif defined(ANDROID) | 117 | #elif defined(ANDROID) |
| 128 | // Use ashmem so we don't have to allocate a file on disk! | 118 | // Use ashmem so we don't have to allocate a file on disk! |
| 129 | fd = ashmem_create_region("PPSSPP_RAM", size); | 119 | fd = ashmem_create_region("PPSSPP_RAM", size); |
| @@ -163,9 +153,6 @@ void MemArena::ReleaseSpace() | |||
| 163 | #ifdef _WIN32 | 153 | #ifdef _WIN32 |
| 164 | CloseHandle(hMemoryMapping); | 154 | CloseHandle(hMemoryMapping); |
| 165 | hMemoryMapping = 0; | 155 | hMemoryMapping = 0; |
| 166 | #elif defined(__SYMBIAN32__) | ||
| 167 | memmap->Close(); | ||
| 168 | delete memmap; | ||
| 169 | #else | 156 | #else |
| 170 | close(fd); | 157 | close(fd); |
| 171 | #endif | 158 | #endif |
| @@ -175,22 +162,13 @@ void MemArena::ReleaseSpace() | |||
| 175 | void *MemArena::CreateView(s64 offset, size_t size, void *base) | 162 | void *MemArena::CreateView(s64 offset, size_t size, void *base) |
| 176 | { | 163 | { |
| 177 | #ifdef _WIN32 | 164 | #ifdef _WIN32 |
| 178 | #ifdef _XBOX | ||
| 179 | size = roundup(size); | ||
| 180 | // use 64kb pages | ||
| 181 | void * ptr = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); | ||
| 182 | return ptr; | ||
| 183 | #else | ||
| 184 | size = roundup(size); | 165 | size = roundup(size); |
| 185 | void *ptr = MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base); | 166 | void *ptr = MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base); |
| 186 | return ptr; | 167 | return ptr; |
| 187 | #endif | ||
| 188 | #else | 168 | #else |
| 189 | void *retval = mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED | | 169 | void *retval = mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED | |
| 190 | // Do not sync memory to underlying file. Linux has this by default. | 170 | // Do not sync memory to underlying file. Linux has this by default. |
| 191 | #ifdef BLACKBERRY | 171 | #ifdef __FreeBSD__ |
| 192 | MAP_NOSYNCFILE | | ||
| 193 | #elif defined(__FreeBSD__) | ||
| 194 | MAP_NOSYNC | | 172 | MAP_NOSYNC | |
| 195 | #endif | 173 | #endif |
| 196 | ((base == nullptr) ? 0 : MAP_FIXED), fd, offset); | 174 | ((base == nullptr) ? 0 : MAP_FIXED), fd, offset); |
| @@ -208,17 +186,12 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base) | |||
| 208 | void MemArena::ReleaseView(void* view, size_t size) | 186 | void MemArena::ReleaseView(void* view, size_t size) |
| 209 | { | 187 | { |
| 210 | #ifdef _WIN32 | 188 | #ifdef _WIN32 |
| 211 | #ifndef _XBOX | ||
| 212 | UnmapViewOfFile(view); | 189 | UnmapViewOfFile(view); |
| 213 | #endif | ||
| 214 | #elif defined(__SYMBIAN32__) | ||
| 215 | memmap->Decommit(((int)view - (int)memmap->Base()) & 0x3FFFFFFF, size); | ||
| 216 | #else | 190 | #else |
| 217 | munmap(view, size); | 191 | munmap(view, size); |
| 218 | #endif | 192 | #endif |
| 219 | } | 193 | } |
| 220 | 194 | ||
| 221 | #ifndef __SYMBIAN32__ | ||
| 222 | u8* MemArena::Find4GBBase() | 195 | u8* MemArena::Find4GBBase() |
| 223 | { | 196 | { |
| 224 | #ifdef _M_X64 | 197 | #ifdef _M_X64 |
| @@ -242,20 +215,6 @@ u8* MemArena::Find4GBBase() | |||
| 242 | } | 215 | } |
| 243 | return base; | 216 | return base; |
| 244 | #else | 217 | #else |
| 245 | #ifdef IOS | ||
| 246 | void* base = nullptr; | ||
| 247 | if (globalbase == nullptr){ | ||
| 248 | base = mmap(0, 0x08000000, PROT_READ | PROT_WRITE, | ||
| 249 | MAP_ANON | MAP_SHARED, -1, 0); | ||
| 250 | if (base == MAP_FAILED) { | ||
| 251 | PanicAlert("Failed to map 128 MB of memory space: %s", strerror(errno)); | ||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | munmap(base, 0x08000000); | ||
| 255 | globalbase = base; | ||
| 256 | } | ||
| 257 | else{ base = globalbase; } | ||
| 258 | #else | ||
| 259 | void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE, | 218 | void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE, |
| 260 | MAP_ANON | MAP_SHARED, -1, 0); | 219 | MAP_ANON | MAP_SHARED, -1, 0); |
| 261 | if (base == MAP_FAILED) { | 220 | if (base == MAP_FAILED) { |
| @@ -263,12 +222,10 @@ u8* MemArena::Find4GBBase() | |||
| 263 | return 0; | 222 | return 0; |
| 264 | } | 223 | } |
| 265 | munmap(base, 0x10000000); | 224 | munmap(base, 0x10000000); |
| 266 | #endif | ||
| 267 | return static_cast<u8*>(base); | 225 | return static_cast<u8*>(base); |
| 268 | #endif | 226 | #endif |
| 269 | #endif | 227 | #endif |
| 270 | } | 228 | } |
| 271 | #endif | ||
| 272 | 229 | ||
| 273 | 230 | ||
| 274 | // yeah, this could also be done in like two bitwise ops... | 231 | // yeah, this could also be done in like two bitwise ops... |
| @@ -284,10 +241,6 @@ static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 | |||
| 284 | size_t position = 0; | 241 | size_t position = 0; |
| 285 | size_t last_position = 0; | 242 | size_t last_position = 0; |
| 286 | 243 | ||
| 287 | #if defined(_XBOX) | ||
| 288 | void *ptr; | ||
| 289 | #endif | ||
| 290 | |||
| 291 | // Zero all the pointers to be sure. | 244 | // Zero all the pointers to be sure. |
| 292 | for (int i = 0; i < num_views; i++) | 245 | for (int i = 0; i < num_views; i++) |
| 293 | { | 246 | { |
| @@ -308,18 +261,6 @@ static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 | |||
| 308 | position = last_position; | 261 | position = last_position; |
| 309 | } | 262 | } |
| 310 | else { | 263 | else { |
| 311 | #ifdef __SYMBIAN32__ | ||
| 312 | *(view.out_ptr_low) = (u8*)((int)arena->memmap->Base() + view.virtual_address); | ||
| 313 | arena->memmap->Commit(view.virtual_address & 0x3FFFFFFF, view.size); | ||
| 314 | } | ||
| 315 | *(view.out_ptr) = (u8*)((int)arena->memmap->Base() + view.virtual_address & 0x3FFFFFFF); | ||
| 316 | #elif defined(_XBOX) | ||
| 317 | *(view.out_ptr_low) = (u8*)(base + view.virtual_address); | ||
| 318 | //arena->memmap->Commit(view.virtual_address & 0x3FFFFFFF, view.size); | ||
| 319 | ptr = VirtualAlloc(base + (view.virtual_address & 0x3FFFFFFF), view.size, MEM_COMMIT, PAGE_READWRITE); | ||
| 320 | } | ||
| 321 | *(view.out_ptr) = (u8*)base + (view.virtual_address & 0x3FFFFFFF); | ||
| 322 | #else | ||
| 323 | *(view.out_ptr_low) = (u8*)arena->CreateView(position, view.size); | 264 | *(view.out_ptr_low) = (u8*)arena->CreateView(position, view.size); |
| 324 | if (!*view.out_ptr_low) | 265 | if (!*view.out_ptr_low) |
| 325 | goto bail; | 266 | goto bail; |
| @@ -340,7 +281,6 @@ static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 | |||
| 340 | } | 281 | } |
| 341 | #endif | 282 | #endif |
| 342 | 283 | ||
| 343 | #endif | ||
| 344 | last_position = position; | 284 | last_position = position; |
| 345 | position += roundup(view.size); | 285 | position += roundup(view.size); |
| 346 | } | 286 | } |
| @@ -389,9 +329,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
| 389 | total_mem += roundup(views[i].size); | 329 | total_mem += roundup(views[i].size); |
| 390 | } | 330 | } |
| 391 | // Grab some pagefile backed memory out of the void ... | 331 | // Grab some pagefile backed memory out of the void ... |
| 392 | #ifndef __SYMBIAN32__ | ||
| 393 | arena->GrabLowMemSpace(total_mem); | 332 | arena->GrabLowMemSpace(total_mem); |
| 394 | #endif | ||
| 395 | 333 | ||
| 396 | // Now, create views in high memory where there's plenty of space. | 334 | // Now, create views in high memory where there's plenty of space. |
| 397 | #ifdef _M_X64 | 335 | #ifdef _M_X64 |
| @@ -403,15 +341,6 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
| 403 | PanicAlert("MemoryMap_Setup: Failed finding a memory base."); | 341 | PanicAlert("MemoryMap_Setup: Failed finding a memory base."); |
| 404 | return 0; | 342 | return 0; |
| 405 | } | 343 | } |
| 406 | #elif defined(_XBOX) | ||
| 407 | // Reserve 256MB | ||
| 408 | u8 *base = (u8*)VirtualAlloc(0, 0x10000000, MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE); | ||
| 409 | if (!Memory_TryBase(base, views, num_views, flags, arena)) | ||
| 410 | { | ||
| 411 | PanicAlert("MemoryMap_Setup: Failed finding a memory base."); | ||
| 412 | exit(0); | ||
| 413 | return 0; | ||
| 414 | } | ||
| 415 | #elif defined(_WIN32) | 344 | #elif defined(_WIN32) |
| 416 | // Try a whole range of possible bases. Return once we got a valid one. | 345 | // Try a whole range of possible bases. Return once we got a valid one. |
| 417 | u32 max_base_addr = 0x7FFF0000 - 0x10000000; | 346 | u32 max_base_addr = 0x7FFF0000 - 0x10000000; |
| @@ -428,15 +357,6 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena | |||
| 428 | break; | 357 | break; |
| 429 | } | 358 | } |
| 430 | } | 359 | } |
| 431 | #elif defined(__SYMBIAN32__) | ||
| 432 | arena->memmap = new RChunk(); | ||
| 433 | arena->memmap->CreateDisconnectedLocal(0, 0, 0x10000000); | ||
| 434 | if (!Memory_TryBase(arena->memmap->Base(), views, num_views, flags, arena)) | ||
| 435 | { | ||
| 436 | PanicAlert("MemoryMap_Setup: Failed finding a memory base."); | ||
| 437 | return 0; | ||
| 438 | } | ||
| 439 | u8* base = arena->memmap->Base(); | ||
| 440 | #else | 360 | #else |
| 441 | // Linux32 is fine with the x64 method, although limited to 32-bit with no automirrors. | 361 | // Linux32 is fine with the x64 method, although limited to 32-bit with no automirrors. |
| 442 | u8 *base = MemArena::Find4GBBase(); | 362 | u8 *base = MemArena::Find4GBBase(); |
diff --git a/src/common/mem_arena.h b/src/common/mem_arena.h index b5f0aa890..3379d2529 100644 --- a/src/common/mem_arena.h +++ b/src/common/mem_arena.h | |||
| @@ -21,10 +21,6 @@ | |||
| 21 | #include <windows.h> | 21 | #include <windows.h> |
| 22 | #endif | 22 | #endif |
| 23 | 23 | ||
| 24 | #ifdef __SYMBIAN32__ | ||
| 25 | #include <e32std.h> | ||
| 26 | #endif | ||
| 27 | |||
| 28 | #include "common/common.h" | 24 | #include "common/common.h" |
| 29 | 25 | ||
| 30 | // This class lets you create a block of anonymous RAM, and then arbitrarily map views into it. | 26 | // This class lets you create a block of anonymous RAM, and then arbitrarily map views into it. |
| @@ -39,12 +35,8 @@ public: | |||
| 39 | void *CreateView(s64 offset, size_t size, void *base = 0); | 35 | void *CreateView(s64 offset, size_t size, void *base = 0); |
| 40 | void ReleaseView(void *view, size_t size); | 36 | void ReleaseView(void *view, size_t size); |
| 41 | 37 | ||
| 42 | #ifdef __SYMBIAN32__ | ||
| 43 | RChunk* memmap; | ||
| 44 | #else | ||
| 45 | // This only finds 1 GB in 32-bit | 38 | // This only finds 1 GB in 32-bit |
| 46 | static u8 *Find4GBBase(); | 39 | static u8 *Find4GBBase(); |
| 47 | #endif | ||
| 48 | private: | 40 | private: |
| 49 | 41 | ||
| 50 | #ifdef _WIN32 | 42 | #ifdef _WIN32 |
diff --git a/src/common/platform.h b/src/common/platform.h index ce9cfd4a2..ba1109c9f 100644 --- a/src/common/platform.h +++ b/src/common/platform.h | |||
| @@ -35,7 +35,6 @@ | |||
| 35 | #define PLATFORM_MACOSX 2 | 35 | #define PLATFORM_MACOSX 2 |
| 36 | #define PLATFORM_LINUX 3 | 36 | #define PLATFORM_LINUX 3 |
| 37 | #define PLATFORM_ANDROID 4 | 37 | #define PLATFORM_ANDROID 4 |
| 38 | #define PLATFORM_IOS 5 | ||
| 39 | 38 | ||
| 40 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 39 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 41 | // Platform detection | 40 | // Platform detection |
diff --git a/src/common/swap.h b/src/common/swap.h index 4f8f39efb..e2d918362 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -48,11 +48,7 @@ | |||
| 48 | // MSVC | 48 | // MSVC |
| 49 | #elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN) | 49 | #elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN) |
| 50 | 50 | ||
| 51 | #ifdef _XBOX | ||
| 52 | #define COMMON_BIG_ENDIAN 1 | ||
| 53 | #else | ||
| 54 | #define COMMON_LITTLE_ENDIAN 1 | 51 | #define COMMON_LITTLE_ENDIAN 1 |
| 55 | #endif | ||
| 56 | 52 | ||
| 57 | #endif | 53 | #endif |
| 58 | 54 | ||
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h index 4e1c0a215..444abf115 100644 --- a/src/common/thread_queue_list.h +++ b/src/common/thread_queue_list.h | |||
| @@ -4,213 +4,143 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include <deque> | ||
| 9 | |||
| 10 | #include <boost/range/algorithm_ext/erase.hpp> | ||
| 11 | |||
| 7 | #include "common/common.h" | 12 | #include "common/common.h" |
| 8 | 13 | ||
| 9 | namespace Common { | 14 | namespace Common { |
| 10 | 15 | ||
| 11 | template<class IdType> | 16 | template<class T, unsigned int N> |
| 12 | struct ThreadQueueList { | 17 | struct ThreadQueueList { |
| 13 | // Number of queues (number of priority levels starting at 0.) | 18 | // TODO(yuriks): If performance proves to be a problem, the std::deques can be replaced with |
| 14 | static const int NUM_QUEUES = 128; | 19 | // (dynamically resizable) circular buffers to remove their overhead when |
| 20 | // inserting and popping. | ||
| 15 | 21 | ||
| 16 | // Initial number of threads a single queue can handle. | 22 | typedef unsigned int Priority; |
| 17 | static const int INITIAL_CAPACITY = 32; | ||
| 18 | 23 | ||
| 19 | struct Queue { | 24 | // Number of priority levels. (Valid levels are [0..NUM_QUEUES).) |
| 20 | // Next ever-been-used queue (worse priority.) | 25 | static const Priority NUM_QUEUES = N; |
| 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 | 26 | ||
| 32 | ThreadQueueList() { | 27 | ThreadQueueList() { |
| 33 | memset(queues, 0, sizeof(queues)); | 28 | first = nullptr; |
| 34 | first = invalid(); | ||
| 35 | } | ||
| 36 | |||
| 37 | ~ThreadQueueList() { | ||
| 38 | for (int i = 0; i < NUM_QUEUES; ++i) | ||
| 39 | { | ||
| 40 | if (queues[i].data != nullptr) | ||
| 41 | free(queues[i].data); | ||
| 42 | } | ||
| 43 | } | 29 | } |
| 44 | 30 | ||
| 45 | // Only for debugging, returns priority level. | 31 | // Only for debugging, returns priority level. |
| 46 | int contains(const IdType uid) { | 32 | Priority contains(const T& uid) { |
| 47 | for (int i = 0; i < NUM_QUEUES; ++i) | 33 | for (Priority i = 0; i < NUM_QUEUES; ++i) { |
| 48 | { | 34 | Queue& cur = queues[i]; |
| 49 | if (queues[i].data == nullptr) | 35 | if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) { |
| 50 | continue; | 36 | return i; |
| 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 | } | 37 | } |
| 58 | } | 38 | } |
| 59 | 39 | ||
| 60 | return -1; | 40 | return -1; |
| 61 | } | 41 | } |
| 62 | 42 | ||
| 63 | inline IdType pop_first() { | 43 | T pop_first() { |
| 64 | Queue *cur = first; | 44 | Queue *cur = first; |
| 65 | while (cur != invalid()) | 45 | while (cur != nullptr) { |
| 66 | { | 46 | if (!cur->data.empty()) { |
| 67 | if (cur->end - cur->first > 0) | 47 | auto tmp = std::move(cur->data.front()); |
| 68 | return cur->data[cur->first++]; | 48 | cur->data.pop_front(); |
| 69 | cur = cur->next; | 49 | return tmp; |
| 50 | } | ||
| 51 | cur = cur->next_nonempty; | ||
| 70 | } | 52 | } |
| 71 | 53 | ||
| 72 | //_dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty."); | 54 | return T(); |
| 73 | return 0; | ||
| 74 | } | 55 | } |
| 75 | 56 | ||
| 76 | inline IdType pop_first_better(u32 priority) { | 57 | T pop_first_better(Priority priority) { |
| 77 | Queue *cur = first; | 58 | Queue *cur = first; |
| 78 | Queue *stop = &queues[priority]; | 59 | Queue *stop = &queues[priority]; |
| 79 | while (cur < stop) | 60 | while (cur < stop) { |
| 80 | { | 61 | if (!cur->data.empty()) { |
| 81 | if (cur->end - cur->first > 0) | 62 | auto tmp = std::move(cur->data.front()); |
| 82 | return cur->data[cur->first++]; | 63 | cur->data.pop_front(); |
| 83 | cur = cur->next; | 64 | return tmp; |
| 65 | } | ||
| 66 | cur = cur->next_nonempty; | ||
| 84 | } | 67 | } |
| 85 | 68 | ||
| 86 | return 0; | 69 | return T(); |
| 87 | } | 70 | } |
| 88 | 71 | ||
| 89 | inline void push_front(u32 priority, const IdType threadID) { | 72 | void push_front(Priority priority, const T& thread_id) { |
| 90 | Queue *cur = &queues[priority]; | 73 | Queue *cur = &queues[priority]; |
| 91 | cur->data[--cur->first] = threadID; | 74 | cur->data.push_front(thread_id); |
| 92 | if (cur->first == 0) | ||
| 93 | rebalance(priority); | ||
| 94 | } | 75 | } |
| 95 | 76 | ||
| 96 | inline void push_back(u32 priority, const IdType threadID) { | 77 | void push_back(Priority priority, const T& thread_id) { |
| 97 | Queue *cur = &queues[priority]; | 78 | Queue *cur = &queues[priority]; |
| 98 | cur->data[cur->end++] = threadID; | 79 | cur->data.push_back(thread_id); |
| 99 | if (cur->end == cur->capacity) | ||
| 100 | rebalance(priority); | ||
| 101 | } | 80 | } |
| 102 | 81 | ||
| 103 | inline void remove(u32 priority, const IdType threadID) { | 82 | void remove(Priority priority, const T& thread_id) { |
| 104 | Queue *cur = &queues[priority]; | 83 | Queue *cur = &queues[priority]; |
| 105 | //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); | 84 | boost::remove_erase(cur->data, thread_id); |
| 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 | } | 85 | } |
| 120 | 86 | ||
| 121 | inline void rotate(u32 priority) { | 87 | void rotate(Priority priority) { |
| 122 | Queue *cur = &queues[priority]; | 88 | Queue *cur = &queues[priority]; |
| 123 | //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); | ||
| 124 | 89 | ||
| 125 | if (cur->end - cur->first > 1) | 90 | if (cur->data.size() > 1) { |
| 126 | { | 91 | cur->data.push_back(std::move(cur->data.front())); |
| 127 | cur->data[cur->end++] = cur->data[cur->first++]; | 92 | cur->data.pop_front(); |
| 128 | if (cur->end == cur->capacity) | ||
| 129 | rebalance(priority); | ||
| 130 | } | 93 | } |
| 131 | } | 94 | } |
| 132 | 95 | ||
| 133 | inline void clear() { | 96 | void clear() { |
| 134 | for (int i = 0; i < NUM_QUEUES; ++i) | 97 | queues.fill(Queue()); |
| 135 | { | 98 | first = nullptr; |
| 136 | if (queues[i].data != nullptr) | ||
| 137 | free(queues[i].data); | ||
| 138 | } | ||
| 139 | memset(queues, 0, sizeof(queues)); | ||
| 140 | first = invalid(); | ||
| 141 | } | 99 | } |
| 142 | 100 | ||
| 143 | inline bool empty(u32 priority) const { | 101 | bool empty(Priority priority) const { |
| 144 | const Queue *cur = &queues[priority]; | 102 | const Queue *cur = &queues[priority]; |
| 145 | return cur->first == cur->end; | 103 | return cur->data.empty(); |
| 146 | } | 104 | } |
| 147 | 105 | ||
| 148 | inline void prepare(u32 priority) { | 106 | void prepare(Priority priority) { |
| 149 | Queue *cur = &queues[priority]; | 107 | Queue* cur = &queues[priority]; |
| 150 | if (cur->next == nullptr) | 108 | if (cur->next_nonempty == UnlinkedTag()) |
| 151 | link(priority, INITIAL_CAPACITY); | 109 | link(priority); |
| 152 | } | 110 | } |
| 153 | 111 | ||
| 154 | private: | 112 | private: |
| 155 | Queue *invalid() const { | 113 | struct Queue { |
| 156 | return (Queue *) -1; | 114 | // Points to the next active priority, skipping over ones that have never been used. |
| 115 | Queue* next_nonempty = UnlinkedTag(); | ||
| 116 | // Double-ended queue of threads in this priority level | ||
| 117 | std::deque<T> data; | ||
| 118 | }; | ||
| 119 | |||
| 120 | /// Special tag used to mark priority levels that have never been used. | ||
| 121 | static Queue* UnlinkedTag() { | ||
| 122 | return reinterpret_cast<Queue*>(1); | ||
| 157 | } | 123 | } |
| 158 | 124 | ||
| 159 | void link(u32 priority, int size) { | 125 | void link(Priority priority) { |
| 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]; | 126 | Queue *cur = &queues[priority]; |
| 172 | cur->data = (IdType *) malloc(sizeof(IdType) * size); | 127 | |
| 173 | cur->capacity = size; | 128 | for (int i = priority - 1; i >= 0; --i) { |
| 174 | cur->first = size / 2; | 129 | if (queues[i].next_nonempty != UnlinkedTag()) { |
| 175 | cur->end = size / 2; | 130 | cur->next_nonempty = queues[i].next_nonempty; |
| 176 | 131 | queues[i].next_nonempty = cur; | |
| 177 | for (int i = (int) priority - 1; i >= 0; --i) | ||
| 178 | { | ||
| 179 | if (queues[i].next != nullptr) | ||
| 180 | { | ||
| 181 | cur->next = queues[i].next; | ||
| 182 | queues[i].next = cur; | ||
| 183 | return; | 132 | return; |
| 184 | } | 133 | } |
| 185 | } | 134 | } |
| 186 | 135 | ||
| 187 | cur->next = first; | 136 | cur->next_nonempty = first; |
| 188 | first = cur; | 137 | first = cur; |
| 189 | } | 138 | } |
| 190 | 139 | ||
| 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 != nullptr) { | ||
| 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. | 140 | // The first queue that's ever been used. |
| 211 | Queue *first; | 141 | Queue* first; |
| 212 | // The priority level queues of thread ids. | 142 | // The priority level queues of thread ids. |
| 213 | Queue queues[NUM_QUEUES]; | 143 | std::array<Queue, NUM_QUEUES> queues; |
| 214 | }; | 144 | }; |
| 215 | 145 | ||
| 216 | } // namespace | 146 | } // namespace |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b67226d8d..8723a471f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -31,6 +31,7 @@ set(SRCS | |||
| 31 | hle/kernel/mutex.cpp | 31 | hle/kernel/mutex.cpp |
| 32 | hle/kernel/semaphore.cpp | 32 | hle/kernel/semaphore.cpp |
| 33 | hle/kernel/shared_memory.cpp | 33 | hle/kernel/shared_memory.cpp |
| 34 | hle/kernel/timer.cpp | ||
| 34 | hle/kernel/thread.cpp | 35 | hle/kernel/thread.cpp |
| 35 | hle/service/ac_u.cpp | 36 | hle/service/ac_u.cpp |
| 36 | hle/service/act_u.cpp | 37 | hle/service/act_u.cpp |
| @@ -123,6 +124,7 @@ set(HEADERS | |||
| 123 | hle/kernel/semaphore.h | 124 | hle/kernel/semaphore.h |
| 124 | hle/kernel/session.h | 125 | hle/kernel/session.h |
| 125 | hle/kernel/shared_memory.h | 126 | hle/kernel/shared_memory.h |
| 127 | hle/kernel/timer.h | ||
| 126 | hle/kernel/thread.h | 128 | hle/kernel/thread.h |
| 127 | hle/service/ac_u.h | 129 | hle/service/ac_u.h |
| 128 | hle/service/act_u.h | 130 | hle/service/act_u.h |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 3b7209418..e612f7439 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -7,7 +7,9 @@ | |||
| 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" | 10 | namespace Core { |
| 11 | struct ThreadContext; | ||
| 12 | } | ||
| 11 | 13 | ||
| 12 | /// Generic ARM11 CPU interface | 14 | /// Generic ARM11 CPU interface |
| 13 | class ARM_Interface : NonCopyable { | 15 | class ARM_Interface : NonCopyable { |
| @@ -87,13 +89,13 @@ public: | |||
| 87 | * Saves the current CPU context | 89 | * Saves the current CPU context |
| 88 | * @param ctx Thread context to save | 90 | * @param ctx Thread context to save |
| 89 | */ | 91 | */ |
| 90 | virtual void SaveContext(ThreadContext& ctx) = 0; | 92 | virtual void SaveContext(Core::ThreadContext& ctx) = 0; |
| 91 | 93 | ||
| 92 | /** | 94 | /** |
| 93 | * Loads a CPU context | 95 | * Loads a CPU context |
| 94 | * @param ctx Thread context to load | 96 | * @param ctx Thread context to load |
| 95 | */ | 97 | */ |
| 96 | virtual void LoadContext(const ThreadContext& ctx) = 0; | 98 | virtual void LoadContext(const Core::ThreadContext& ctx) = 0; |
| 97 | 99 | ||
| 98 | /// Prepare core for thread reschedule (if needed to correctly handle state) | 100 | /// Prepare core for thread reschedule (if needed to correctly handle state) |
| 99 | virtual void PrepareReschedule() = 0; | 101 | virtual void PrepareReschedule() = 0; |
| @@ -103,6 +105,8 @@ public: | |||
| 103 | return num_instructions; | 105 | return num_instructions; |
| 104 | } | 106 | } |
| 105 | 107 | ||
| 108 | s64 down_count; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop | ||
| 109 | |||
| 106 | protected: | 110 | protected: |
| 107 | 111 | ||
| 108 | /** | 112 | /** |
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index a838fd25a..9c4cc90f2 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp | |||
| @@ -9,11 +9,14 @@ | |||
| 9 | #include "core/arm/dyncom/arm_dyncom.h" | 9 | #include "core/arm/dyncom/arm_dyncom.h" |
| 10 | #include "core/arm/dyncom/arm_dyncom_interpreter.h" | 10 | #include "core/arm/dyncom/arm_dyncom_interpreter.h" |
| 11 | 11 | ||
| 12 | #include "core/core.h" | ||
| 13 | #include "core/core_timing.h" | ||
| 14 | |||
| 12 | const static cpu_config_t s_arm11_cpu_info = { | 15 | const static cpu_config_t s_arm11_cpu_info = { |
| 13 | "armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE | 16 | "armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE |
| 14 | }; | 17 | }; |
| 15 | 18 | ||
| 16 | ARM_DynCom::ARM_DynCom() : ticks(0) { | 19 | ARM_DynCom::ARM_DynCom() { |
| 17 | state = std::unique_ptr<ARMul_State>(new ARMul_State); | 20 | state = std::unique_ptr<ARMul_State>(new ARMul_State); |
| 18 | 21 | ||
| 19 | ARMul_EmulateInit(); | 22 | ARMul_EmulateInit(); |
| @@ -72,11 +75,14 @@ void ARM_DynCom::SetCPSR(u32 cpsr) { | |||
| 72 | } | 75 | } |
| 73 | 76 | ||
| 74 | u64 ARM_DynCom::GetTicks() const { | 77 | u64 ARM_DynCom::GetTicks() const { |
| 75 | return ticks; | 78 | // TODO(Subv): Remove ARM_DynCom::GetTicks() and use CoreTiming::GetTicks() directly once ARMemu is gone |
| 79 | return CoreTiming::GetTicks(); | ||
| 76 | } | 80 | } |
| 77 | 81 | ||
| 78 | void ARM_DynCom::AddTicks(u64 ticks) { | 82 | void ARM_DynCom::AddTicks(u64 ticks) { |
| 79 | this->ticks += ticks; | 83 | down_count -= ticks; |
| 84 | if (down_count < 0) | ||
| 85 | CoreTiming::Advance(); | ||
| 80 | } | 86 | } |
| 81 | 87 | ||
| 82 | void ARM_DynCom::ExecuteInstructions(int num_instructions) { | 88 | void ARM_DynCom::ExecuteInstructions(int num_instructions) { |
| @@ -85,10 +91,11 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) { | |||
| 85 | // Dyncom only breaks on instruction dispatch. This only happens on every instruction when | 91 | // Dyncom only breaks on instruction dispatch. This only happens on every instruction when |
| 86 | // executing one instruction at a time. Otherwise, if a block is being executed, more | 92 | // executing one instruction at a time. Otherwise, if a block is being executed, more |
| 87 | // instructions may actually be executed than specified. | 93 | // instructions may actually be executed than specified. |
| 88 | ticks += InterpreterMainLoop(state.get()); | 94 | unsigned ticks_executed = InterpreterMainLoop(state.get()); |
| 95 | AddTicks(ticks_executed); | ||
| 89 | } | 96 | } |
| 90 | 97 | ||
| 91 | void ARM_DynCom::SaveContext(ThreadContext& ctx) { | 98 | void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { |
| 92 | memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); | 99 | memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); |
| 93 | memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); | 100 | memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); |
| 94 | 101 | ||
| @@ -104,7 +111,7 @@ void ARM_DynCom::SaveContext(ThreadContext& ctx) { | |||
| 104 | ctx.mode = state->NextInstr; | 111 | ctx.mode = state->NextInstr; |
| 105 | } | 112 | } |
| 106 | 113 | ||
| 107 | void ARM_DynCom::LoadContext(const ThreadContext& ctx) { | 114 | void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) { |
| 108 | memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); | 115 | memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); |
| 109 | memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); | 116 | memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); |
| 110 | 117 | ||
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 7284dcd07..f16fb070c 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h | |||
| @@ -71,13 +71,13 @@ public: | |||
| 71 | * Saves the current CPU context | 71 | * Saves the current CPU context |
| 72 | * @param ctx Thread context to save | 72 | * @param ctx Thread context to save |
| 73 | */ | 73 | */ |
| 74 | void SaveContext(ThreadContext& ctx) override; | 74 | void SaveContext(Core::ThreadContext& ctx) override; |
| 75 | 75 | ||
| 76 | /** | 76 | /** |
| 77 | * Loads a CPU context | 77 | * Loads a CPU context |
| 78 | * @param ctx Thread context to load | 78 | * @param ctx Thread context to load |
| 79 | */ | 79 | */ |
| 80 | void LoadContext(const ThreadContext& ctx) override; | 80 | void LoadContext(const Core::ThreadContext& ctx) override; |
| 81 | 81 | ||
| 82 | /// Prepare core for thread reschedule (if needed to correctly handle state) | 82 | /// Prepare core for thread reschedule (if needed to correctly handle state) |
| 83 | void PrepareReschedule() override; | 83 | void PrepareReschedule() override; |
| @@ -89,8 +89,5 @@ public: | |||
| 89 | void ExecuteInstructions(int num_instructions) override; | 89 | void ExecuteInstructions(int num_instructions) override; |
| 90 | 90 | ||
| 91 | private: | 91 | private: |
| 92 | |||
| 93 | std::unique_ptr<ARMul_State> state; | 92 | std::unique_ptr<ARMul_State> state; |
| 94 | u64 ticks; | ||
| 95 | |||
| 96 | }; | 93 | }; |
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 9b291862c..e3ca02e98 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp | |||
| @@ -947,6 +947,15 @@ typedef struct _smla_inst { | |||
| 947 | unsigned int Rn; | 947 | unsigned int Rn; |
| 948 | } smla_inst; | 948 | } smla_inst; |
| 949 | 949 | ||
| 950 | typedef struct smlalxy_inst { | ||
| 951 | unsigned int x; | ||
| 952 | unsigned int y; | ||
| 953 | unsigned int RdLo; | ||
| 954 | unsigned int RdHi; | ||
| 955 | unsigned int Rm; | ||
| 956 | unsigned int Rn; | ||
| 957 | } smlalxy_inst; | ||
| 958 | |||
| 950 | typedef struct ssat_inst { | 959 | typedef struct ssat_inst { |
| 951 | unsigned int Rn; | 960 | unsigned int Rn; |
| 952 | unsigned int Rd; | 961 | unsigned int Rd; |
| @@ -2403,7 +2412,25 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(smlal)(unsigned int inst, int index) | |||
| 2403 | return inst_base; | 2412 | return inst_base; |
| 2404 | } | 2413 | } |
| 2405 | 2414 | ||
| 2406 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALXY"); } | 2415 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) |
| 2416 | { | ||
| 2417 | arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlalxy_inst)); | ||
| 2418 | smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component; | ||
| 2419 | |||
| 2420 | inst_base->cond = BITS(inst, 28, 31); | ||
| 2421 | inst_base->idx = index; | ||
| 2422 | inst_base->br = NON_BRANCH; | ||
| 2423 | inst_base->load_r15 = 0; | ||
| 2424 | |||
| 2425 | inst_cream->x = BIT(inst, 5); | ||
| 2426 | inst_cream->y = BIT(inst, 6); | ||
| 2427 | inst_cream->RdLo = BITS(inst, 12, 15); | ||
| 2428 | inst_cream->RdHi = BITS(inst, 16, 19); | ||
| 2429 | inst_cream->Rn = BITS(inst, 0, 4); | ||
| 2430 | inst_cream->Rm = BITS(inst, 8, 11); | ||
| 2431 | |||
| 2432 | return inst_base; | ||
| 2433 | } | ||
| 2407 | 2434 | ||
| 2408 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index) | 2435 | ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index) |
| 2409 | { | 2436 | { |
| @@ -5686,6 +5713,34 @@ unsigned InterpreterMainLoop(ARMul_State* state) { | |||
| 5686 | } | 5713 | } |
| 5687 | 5714 | ||
| 5688 | SMLALXY_INST: | 5715 | SMLALXY_INST: |
| 5716 | { | ||
| 5717 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { | ||
| 5718 | smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component; | ||
| 5719 | |||
| 5720 | u64 operand1 = RN; | ||
| 5721 | u64 operand2 = RM; | ||
| 5722 | |||
| 5723 | if (inst_cream->x != 0) | ||
| 5724 | operand1 >>= 16; | ||
| 5725 | if (inst_cream->y != 0) | ||
| 5726 | operand2 >>= 16; | ||
| 5727 | operand1 &= 0xFFFF; | ||
| 5728 | if (operand1 & 0x8000) | ||
| 5729 | operand1 -= 65536; | ||
| 5730 | operand2 &= 0xFFFF; | ||
| 5731 | if (operand2 & 0x8000) | ||
| 5732 | operand2 -= 65536; | ||
| 5733 | |||
| 5734 | u64 dest = ((u64)RDHI << 32 | RDLO) + (operand1 * operand2); | ||
| 5735 | RDLO = (dest & 0xFFFFFFFF); | ||
| 5736 | RDHI = ((dest >> 32) & 0xFFFFFFFF); | ||
| 5737 | } | ||
| 5738 | |||
| 5739 | cpu->Reg[15] += GET_INST_SIZE(cpu); | ||
| 5740 | INC_PC(sizeof(smlalxy_inst)); | ||
| 5741 | FETCH_INST; | ||
| 5742 | GOTO_NEXT_INST; | ||
| 5743 | } | ||
| 5689 | 5744 | ||
| 5690 | SMLAW_INST: | 5745 | SMLAW_INST: |
| 5691 | { | 5746 | { |
| @@ -5836,16 +5891,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) { | |||
| 5836 | 5891 | ||
| 5837 | SMULW_INST: | 5892 | SMULW_INST: |
| 5838 | { | 5893 | { |
| 5839 | if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { | 5894 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { |
| 5840 | smlad_inst *inst_cream = (smlad_inst *)inst_base->component; | 5895 | smlad_inst* const inst_cream = (smlad_inst*)inst_base->component; |
| 5841 | int64_t rm = RM; | 5896 | |
| 5842 | int64_t rn = RN; | 5897 | s16 rm = (inst_cream->m == 1) ? ((RM >> 16) & 0xFFFF) : (RM & 0xFFFF); |
| 5843 | if (inst_cream->m) | 5898 | |
| 5844 | rm = BITS(rm, 16, 31); | 5899 | s64 result = (s64)rm * (s64)(s32)RN; |
| 5845 | else | 5900 | RD = BITS(result, 16, 47); |
| 5846 | rm = BITS(rm, 0, 15); | ||
| 5847 | int64_t rst = rm * rn; | ||
| 5848 | RD = BITS(rst, 16, 47); | ||
| 5849 | } | 5901 | } |
| 5850 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 5902 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| 5851 | INC_PC(sizeof(smlad_inst)); | 5903 | INC_PC(sizeof(smlad_inst)); |
| @@ -6267,6 +6319,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) { | |||
| 6267 | addr = RN; | 6319 | addr = RN; |
| 6268 | unsigned int value = Memory::Read8(addr); | 6320 | unsigned int value = Memory::Read8(addr); |
| 6269 | Memory::Write8(addr, (RM & 0xFF)); | 6321 | Memory::Write8(addr, (RM & 0xFF)); |
| 6322 | RD = value; | ||
| 6270 | } | 6323 | } |
| 6271 | cpu->Reg[15] += GET_INST_SIZE(cpu); | 6324 | cpu->Reg[15] += GET_INST_SIZE(cpu); |
| 6272 | INC_PC(sizeof(swp_inst)); | 6325 | INC_PC(sizeof(swp_inst)); |
| @@ -6643,10 +6696,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) { | |||
| 6643 | { | 6696 | { |
| 6644 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { | 6697 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { |
| 6645 | umaal_inst* const inst_cream = (umaal_inst*)inst_base->component; | 6698 | umaal_inst* const inst_cream = (umaal_inst*)inst_base->component; |
| 6646 | const u32 rm = RM; | 6699 | const u64 rm = RM; |
| 6647 | const u32 rn = RN; | 6700 | const u64 rn = RN; |
| 6648 | const u32 rd_lo = RDLO; | 6701 | const u64 rd_lo = RDLO; |
| 6649 | const u32 rd_hi = RDHI; | 6702 | const u64 rd_hi = RDHI; |
| 6650 | const u64 result = (rm * rn) + rd_lo + rd_hi; | 6703 | const u64 result = (rm * rn) + rd_lo + rd_hi; |
| 6651 | 6704 | ||
| 6652 | RDLO = (result & 0xFFFFFFFF); | 6705 | RDLO = (result & 0xFFFFFFFF); |
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp index 80ebc359e..c76d371a2 100644 --- a/src/core/arm/interpreter/arm_interpreter.cpp +++ b/src/core/arm/interpreter/arm_interpreter.cpp | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include "core/arm/interpreter/arm_interpreter.h" | 5 | #include "core/arm/interpreter/arm_interpreter.h" |
| 6 | 6 | ||
| 7 | #include "core/core.h" | ||
| 8 | |||
| 7 | const static cpu_config_t arm11_cpu_info = { | 9 | const static cpu_config_t arm11_cpu_info = { |
| 8 | "armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE | 10 | "armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE |
| 9 | }; | 11 | }; |
| @@ -75,7 +77,7 @@ void ARM_Interpreter::ExecuteInstructions(int num_instructions) { | |||
| 75 | ARMul_Emulate32(state); | 77 | ARMul_Emulate32(state); |
| 76 | } | 78 | } |
| 77 | 79 | ||
| 78 | void ARM_Interpreter::SaveContext(ThreadContext& ctx) { | 80 | void ARM_Interpreter::SaveContext(Core::ThreadContext& ctx) { |
| 79 | memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); | 81 | memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); |
| 80 | memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); | 82 | memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); |
| 81 | 83 | ||
| @@ -91,7 +93,7 @@ void ARM_Interpreter::SaveContext(ThreadContext& ctx) { | |||
| 91 | ctx.mode = state->NextInstr; | 93 | ctx.mode = state->NextInstr; |
| 92 | } | 94 | } |
| 93 | 95 | ||
| 94 | void ARM_Interpreter::LoadContext(const ThreadContext& ctx) { | 96 | void ARM_Interpreter::LoadContext(const Core::ThreadContext& ctx) { |
| 95 | memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); | 97 | memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); |
| 96 | memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); | 98 | memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); |
| 97 | 99 | ||
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h index 019dad5df..e5ecc69c2 100644 --- a/src/core/arm/interpreter/arm_interpreter.h +++ b/src/core/arm/interpreter/arm_interpreter.h | |||
| @@ -70,13 +70,13 @@ public: | |||
| 70 | * Saves the current CPU context | 70 | * Saves the current CPU context |
| 71 | * @param ctx Thread context to save | 71 | * @param ctx Thread context to save |
| 72 | */ | 72 | */ |
| 73 | void SaveContext(ThreadContext& ctx) override; | 73 | void SaveContext(Core::ThreadContext& ctx) override; |
| 74 | 74 | ||
| 75 | /** | 75 | /** |
| 76 | * Loads a CPU context | 76 | * Loads a CPU context |
| 77 | * @param ctx Thread context to load | 77 | * @param ctx Thread context to load |
| 78 | */ | 78 | */ |
| 79 | void LoadContext(const ThreadContext& ctx) override; | 79 | void LoadContext(const Core::ThreadContext& ctx) override; |
| 80 | 80 | ||
| 81 | /// Prepare core for thread reschedule (if needed to correctly handle state) | 81 | /// Prepare core for thread reschedule (if needed to correctly handle state) |
| 82 | void PrepareReschedule() override; | 82 | void PrepareReschedule() override; |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 8ac4481cc..e9e5c35cc 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -5,8 +5,10 @@ | |||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
| 6 | 6 | ||
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/core_timing.h" | ||
| 8 | 9 | ||
| 9 | #include "core/settings.h" | 10 | #include "core/settings.h" |
| 11 | #include "core/arm/arm_interface.h" | ||
| 10 | #include "core/arm/disassembler/arm_disasm.h" | 12 | #include "core/arm/disassembler/arm_disasm.h" |
| 11 | #include "core/arm/interpreter/arm_interpreter.h" | 13 | #include "core/arm/interpreter/arm_interpreter.h" |
| 12 | #include "core/arm/dyncom/arm_dyncom.h" | 14 | #include "core/arm/dyncom/arm_dyncom.h" |
| @@ -16,14 +18,22 @@ | |||
| 16 | 18 | ||
| 17 | namespace Core { | 19 | namespace Core { |
| 18 | 20 | ||
| 19 | static u64 last_ticks = 0; ///< Last CPU ticks | ||
| 20 | static ARM_Disasm* disasm = nullptr; ///< ARM disassembler | ||
| 21 | ARM_Interface* g_app_core = nullptr; ///< ARM11 application core | 21 | ARM_Interface* g_app_core = nullptr; ///< ARM11 application core |
| 22 | ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core | 22 | ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core |
| 23 | 23 | ||
| 24 | /// Run the core CPU loop | 24 | /// Run the core CPU loop |
| 25 | void RunLoop(int tight_loop) { | 25 | void RunLoop(int tight_loop) { |
| 26 | g_app_core->Run(tight_loop); | 26 | // If the current thread is an idle thread, then don't execute instructions, |
| 27 | // instead advance to the next event and try to yield to the next thread | ||
| 28 | if (Kernel::GetCurrentThread()->IsIdle()) { | ||
| 29 | LOG_TRACE(Core_ARM11, "Idling"); | ||
| 30 | CoreTiming::Idle(); | ||
| 31 | CoreTiming::Advance(); | ||
| 32 | HLE::Reschedule(__func__); | ||
| 33 | } else { | ||
| 34 | g_app_core->Run(tight_loop); | ||
| 35 | } | ||
| 36 | |||
| 27 | HW::Update(); | 37 | HW::Update(); |
| 28 | if (HLE::g_reschedule) { | 38 | if (HLE::g_reschedule) { |
| 29 | Kernel::Reschedule(); | 39 | Kernel::Reschedule(); |
| @@ -49,7 +59,6 @@ void Stop() { | |||
| 49 | int Init() { | 59 | int Init() { |
| 50 | LOG_DEBUG(Core, "initialized OK"); | 60 | LOG_DEBUG(Core, "initialized OK"); |
| 51 | 61 | ||
| 52 | disasm = new ARM_Disasm(); | ||
| 53 | g_sys_core = new ARM_Interpreter(); | 62 | g_sys_core = new ARM_Interpreter(); |
| 54 | 63 | ||
| 55 | switch (Settings::values.cpu_core) { | 64 | switch (Settings::values.cpu_core) { |
| @@ -62,13 +71,10 @@ int Init() { | |||
| 62 | break; | 71 | break; |
| 63 | } | 72 | } |
| 64 | 73 | ||
| 65 | last_ticks = Core::g_app_core->GetTicks(); | ||
| 66 | |||
| 67 | return 0; | 74 | return 0; |
| 68 | } | 75 | } |
| 69 | 76 | ||
| 70 | void Shutdown() { | 77 | void Shutdown() { |
| 71 | delete disasm; | ||
| 72 | delete g_app_core; | 78 | delete g_app_core; |
| 73 | delete g_sys_core; | 79 | delete g_sys_core; |
| 74 | 80 | ||
diff --git a/src/core/core.h b/src/core/core.h index ecd58a73a..2f5e8bc6b 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -4,8 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/arm/arm_interface.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/arm/skyeye_common/armdefs.h" | 8 | |
| 9 | class ARM_Interface; | ||
| 9 | 10 | ||
| 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 11 | 12 | ||
| @@ -16,6 +17,21 @@ enum CPUCore { | |||
| 16 | CPU_OldInterpreter, | 17 | CPU_OldInterpreter, |
| 17 | }; | 18 | }; |
| 18 | 19 | ||
| 20 | struct ThreadContext { | ||
| 21 | u32 cpu_registers[13]; | ||
| 22 | u32 sp; | ||
| 23 | u32 lr; | ||
| 24 | u32 pc; | ||
| 25 | u32 cpsr; | ||
| 26 | u32 fpu_registers[32]; | ||
| 27 | u32 fpscr; | ||
| 28 | u32 fpexc; | ||
| 29 | |||
| 30 | // These are not part of native ThreadContext, but needed by emu | ||
| 31 | u32 reg_15; | ||
| 32 | u32 mode; | ||
| 33 | }; | ||
| 34 | |||
| 19 | extern ARM_Interface* g_app_core; ///< ARM11 application core | 35 | extern ARM_Interface* g_app_core; ///< ARM11 application core |
| 20 | extern ARM_Interface* g_sys_core; ///< ARM11 system (OS) core | 36 | extern ARM_Interface* g_sys_core; ///< ARM11 system (OS) core |
| 21 | 37 | ||
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 321648b37..ec9d52a08 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -1,16 +1,16 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // Copyright (c) 2012- PPSSPP Project / Dolphin Project. |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <vector> | ||
| 6 | #include <cstdio> | ||
| 7 | #include <atomic> | 5 | #include <atomic> |
| 6 | #include <cstdio> | ||
| 8 | #include <mutex> | 7 | #include <mutex> |
| 8 | #include <vector> | ||
| 9 | 9 | ||
| 10 | #include "common/chunk_file.h" | 10 | #include "common/chunk_file.h" |
| 11 | #include "common/msg_handler.h" | 11 | #include "common/log.h" |
| 12 | #include "common/string_util.h" | ||
| 13 | 12 | ||
| 13 | #include "core/arm/arm_interface.h" | ||
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 16 | 16 | ||
| @@ -22,16 +22,15 @@ int g_clock_rate_arm11 = 268123480; | |||
| 22 | 22 | ||
| 23 | namespace CoreTiming | 23 | namespace CoreTiming |
| 24 | { | 24 | { |
| 25 | |||
| 26 | struct EventType | 25 | struct EventType |
| 27 | { | 26 | { |
| 28 | EventType() {} | 27 | EventType() {} |
| 29 | 28 | ||
| 30 | EventType(TimedCallback cb, const char *n) | 29 | EventType(TimedCallback cb, const char* n) |
| 31 | : callback(cb), name(n) {} | 30 | : callback(cb), name(n) {} |
| 32 | 31 | ||
| 33 | TimedCallback callback; | 32 | TimedCallback callback; |
| 34 | const char *name; | 33 | const char* name; |
| 35 | }; | 34 | }; |
| 36 | 35 | ||
| 37 | std::vector<EventType> event_types; | 36 | std::vector<EventType> event_types; |
| @@ -41,262 +40,247 @@ struct BaseEvent | |||
| 41 | s64 time; | 40 | s64 time; |
| 42 | u64 userdata; | 41 | u64 userdata; |
| 43 | int type; | 42 | int type; |
| 44 | // Event *next; | ||
| 45 | }; | 43 | }; |
| 46 | 44 | ||
| 47 | typedef LinkedListItem<BaseEvent> Event; | 45 | typedef LinkedListItem<BaseEvent> Event; |
| 48 | 46 | ||
| 49 | Event *first; | 47 | Event* first; |
| 50 | Event *tsFirst; | 48 | Event* ts_first; |
| 51 | Event *tsLast; | 49 | Event* ts_last; |
| 52 | 50 | ||
| 53 | // event pools | 51 | // event pools |
| 54 | Event *eventPool = 0; | 52 | Event* event_pool = 0; |
| 55 | Event *eventTsPool = 0; | 53 | Event* event_ts_pool = 0; |
| 56 | int allocatedTsEvents = 0; | 54 | int allocated_ts_events = 0; |
| 57 | // Optimization to skip MoveEvents when possible. | 55 | // Optimization to skip MoveEvents when possible. |
| 58 | std::atomic<u32> hasTsEvents; | 56 | std::atomic<bool> has_ts_events(false); |
| 59 | 57 | ||
| 60 | // Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block | 58 | int g_slice_length; |
| 61 | // as we can already reach that structure through a register. | ||
| 62 | int slicelength; | ||
| 63 | 59 | ||
| 64 | MEMORY_ALIGNED16(s64) globalTimer; | 60 | s64 global_timer; |
| 65 | s64 idledCycles; | 61 | s64 idled_cycles; |
| 62 | s64 last_global_time_ticks; | ||
| 63 | s64 last_global_time_us; | ||
| 66 | 64 | ||
| 67 | static std::recursive_mutex externalEventSection; | 65 | static std::recursive_mutex external_event_section; |
| 68 | 66 | ||
| 69 | // Warning: not included in save state. | 67 | // Warning: not included in save state. |
| 70 | void(*advanceCallback)(int cyclesExecuted) = nullptr; | 68 | using AdvanceCallback = void(int cycles_executed); |
| 69 | AdvanceCallback* advance_callback = nullptr; | ||
| 70 | std::vector<MHzChangeCallback> mhz_change_callbacks; | ||
| 71 | 71 | ||
| 72 | void SetClockFrequencyMHz(int cpuMhz) | 72 | void FireMhzChange() { |
| 73 | { | 73 | for (auto callback : mhz_change_callbacks) |
| 74 | g_clock_rate_arm11 = cpuMhz * 1000000; | 74 | callback(); |
| 75 | } | ||
| 76 | |||
| 77 | void SetClockFrequencyMHz(int cpu_mhz) { | ||
| 78 | // When the mhz changes, we keep track of what "time" it was before hand. | ||
| 79 | // This way, time always moves forward, even if mhz is changed. | ||
| 80 | last_global_time_us = GetGlobalTimeUs(); | ||
| 81 | last_global_time_ticks = GetTicks(); | ||
| 82 | |||
| 83 | g_clock_rate_arm11 = cpu_mhz * 1000000; | ||
| 75 | // TODO: Rescale times of scheduled events? | 84 | // TODO: Rescale times of scheduled events? |
| 85 | |||
| 86 | FireMhzChange(); | ||
| 76 | } | 87 | } |
| 77 | 88 | ||
| 78 | int GetClockFrequencyMHz() | 89 | int GetClockFrequencyMHz() { |
| 79 | { | ||
| 80 | return g_clock_rate_arm11 / 1000000; | 90 | return g_clock_rate_arm11 / 1000000; |
| 81 | } | 91 | } |
| 82 | 92 | ||
| 93 | u64 GetGlobalTimeUs() { | ||
| 94 | s64 ticks_since_last = GetTicks() - last_global_time_ticks; | ||
| 95 | int freq = GetClockFrequencyMHz(); | ||
| 96 | s64 us_since_last = ticks_since_last / freq; | ||
| 97 | return last_global_time_us + us_since_last; | ||
| 98 | } | ||
| 83 | 99 | ||
| 84 | Event* GetNewEvent() | 100 | Event* GetNewEvent() { |
| 85 | { | 101 | if (!event_pool) |
| 86 | if (!eventPool) | ||
| 87 | return new Event; | 102 | return new Event; |
| 88 | 103 | ||
| 89 | Event* ev = eventPool; | 104 | Event* event = event_pool; |
| 90 | eventPool = ev->next; | 105 | event_pool = event->next; |
| 91 | return ev; | 106 | return event; |
| 92 | } | 107 | } |
| 93 | 108 | ||
| 94 | Event* GetNewTsEvent() | 109 | Event* GetNewTsEvent() { |
| 95 | { | 110 | allocated_ts_events++; |
| 96 | allocatedTsEvents++; | ||
| 97 | 111 | ||
| 98 | if (!eventTsPool) | 112 | if (!event_ts_pool) |
| 99 | return new Event; | 113 | return new Event; |
| 100 | 114 | ||
| 101 | Event* ev = eventTsPool; | 115 | Event* event = event_ts_pool; |
| 102 | eventTsPool = ev->next; | 116 | event_ts_pool = event->next; |
| 103 | return ev; | 117 | return event; |
| 104 | } | 118 | } |
| 105 | 119 | ||
| 106 | void FreeEvent(Event* ev) | 120 | void FreeEvent(Event* event) { |
| 107 | { | 121 | event->next = event_pool; |
| 108 | ev->next = eventPool; | 122 | event_pool = event; |
| 109 | eventPool = ev; | ||
| 110 | } | 123 | } |
| 111 | 124 | ||
| 112 | void FreeTsEvent(Event* ev) | 125 | void FreeTsEvent(Event* event) { |
| 113 | { | 126 | event->next = event_ts_pool; |
| 114 | ev->next = eventTsPool; | 127 | event_ts_pool = event; |
| 115 | eventTsPool = ev; | 128 | allocated_ts_events--; |
| 116 | allocatedTsEvents--; | ||
| 117 | } | 129 | } |
| 118 | 130 | ||
| 119 | int RegisterEvent(const char *name, TimedCallback callback) | 131 | int RegisterEvent(const char* name, TimedCallback callback) { |
| 120 | { | ||
| 121 | event_types.push_back(EventType(callback, name)); | 132 | event_types.push_back(EventType(callback, name)); |
| 122 | return (int)event_types.size() - 1; | 133 | return (int)event_types.size() - 1; |
| 123 | } | 134 | } |
| 124 | 135 | ||
| 125 | void AntiCrashCallback(u64 userdata, int cyclesLate) | 136 | void AntiCrashCallback(u64 userdata, int cycles_late) { |
| 126 | { | 137 | LOG_CRITICAL(Core_Timing, "Savestate broken: an unregistered event was called."); |
| 127 | LOG_CRITICAL(Core, "Savestate broken: an unregistered event was called."); | ||
| 128 | Core::Halt("invalid timing events"); | 138 | Core::Halt("invalid timing events"); |
| 129 | } | 139 | } |
| 130 | 140 | ||
| 131 | void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback) | 141 | void RestoreRegisterEvent(int event_type, const char* name, TimedCallback callback) { |
| 132 | { | ||
| 133 | if (event_type >= (int)event_types.size()) | 142 | if (event_type >= (int)event_types.size()) |
| 134 | event_types.resize(event_type + 1, EventType(AntiCrashCallback, "INVALID EVENT")); | 143 | event_types.resize(event_type + 1, EventType(AntiCrashCallback, "INVALID EVENT")); |
| 135 | 144 | ||
| 136 | event_types[event_type] = EventType(callback, name); | 145 | event_types[event_type] = EventType(callback, name); |
| 137 | } | 146 | } |
| 138 | 147 | ||
| 139 | void UnregisterAllEvents() | 148 | void UnregisterAllEvents() { |
| 140 | { | ||
| 141 | if (first) | 149 | if (first) |
| 142 | PanicAlert("Cannot unregister events with events pending"); | 150 | PanicAlert("Cannot unregister events with events pending"); |
| 143 | event_types.clear(); | 151 | event_types.clear(); |
| 144 | } | 152 | } |
| 145 | 153 | ||
| 146 | void Init() | 154 | void Init() { |
| 147 | { | 155 | Core::g_app_core->down_count = INITIAL_SLICE_LENGTH; |
| 148 | //currentMIPS->downcount = INITIAL_SLICE_LENGTH; | 156 | g_slice_length = INITIAL_SLICE_LENGTH; |
| 149 | //slicelength = INITIAL_SLICE_LENGTH; | 157 | global_timer = 0; |
| 150 | globalTimer = 0; | 158 | idled_cycles = 0; |
| 151 | idledCycles = 0; | 159 | last_global_time_ticks = 0; |
| 152 | hasTsEvents = 0; | 160 | last_global_time_us = 0; |
| 161 | has_ts_events = 0; | ||
| 162 | mhz_change_callbacks.clear(); | ||
| 153 | } | 163 | } |
| 154 | 164 | ||
| 155 | void Shutdown() | 165 | void Shutdown() { |
| 156 | { | ||
| 157 | MoveEvents(); | 166 | MoveEvents(); |
| 158 | ClearPendingEvents(); | 167 | ClearPendingEvents(); |
| 159 | UnregisterAllEvents(); | 168 | UnregisterAllEvents(); |
| 160 | 169 | ||
| 161 | while (eventPool) | 170 | while (event_pool) { |
| 162 | { | 171 | Event* event = event_pool; |
| 163 | Event *ev = eventPool; | 172 | event_pool = event->next; |
| 164 | eventPool = ev->next; | 173 | delete event; |
| 165 | delete ev; | ||
| 166 | } | 174 | } |
| 167 | 175 | ||
| 168 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 176 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 169 | while (eventTsPool) | 177 | while (event_ts_pool) { |
| 170 | { | 178 | Event* event = event_ts_pool; |
| 171 | Event *ev = eventTsPool; | 179 | event_ts_pool = event->next; |
| 172 | eventTsPool = ev->next; | 180 | delete event; |
| 173 | delete ev; | ||
| 174 | } | 181 | } |
| 175 | } | 182 | } |
| 176 | 183 | ||
| 177 | u64 GetTicks() | 184 | u64 GetTicks() { |
| 178 | { | 185 | return (u64)global_timer + g_slice_length - Core::g_app_core->down_count; |
| 179 | LOG_ERROR(Core, "Unimplemented function!"); | ||
| 180 | return 0; | ||
| 181 | //return (u64)globalTimer + slicelength - currentMIPS->downcount; | ||
| 182 | } | 186 | } |
| 183 | 187 | ||
| 184 | u64 GetIdleTicks() | 188 | u64 GetIdleTicks() { |
| 185 | { | 189 | return (u64)idled_cycles; |
| 186 | return (u64)idledCycles; | ||
| 187 | } | 190 | } |
| 188 | 191 | ||
| 189 | 192 | ||
| 190 | // This is to be called when outside threads, such as the graphics thread, wants to | 193 | // This is to be called when outside threads, such as the graphics thread, wants to |
| 191 | // schedule things to be executed on the main thread. | 194 | // schedule things to be executed on the main thread. |
| 192 | void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata) | 195 | void ScheduleEvent_Threadsafe(s64 cycles_into_future, int event_type, u64 userdata) { |
| 193 | { | 196 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 194 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 197 | Event* new_event = GetNewTsEvent(); |
| 195 | Event *ne = GetNewTsEvent(); | 198 | new_event->time = GetTicks() + cycles_into_future; |
| 196 | ne->time = GetTicks() + cyclesIntoFuture; | 199 | new_event->type = event_type; |
| 197 | ne->type = event_type; | 200 | new_event->next = 0; |
| 198 | ne->next = 0; | 201 | new_event->userdata = userdata; |
| 199 | ne->userdata = userdata; | 202 | if (!ts_first) |
| 200 | if (!tsFirst) | 203 | ts_first = new_event; |
| 201 | tsFirst = ne; | 204 | if (ts_last) |
| 202 | if (tsLast) | 205 | ts_last->next = new_event; |
| 203 | tsLast->next = ne; | 206 | ts_last = new_event; |
| 204 | tsLast = ne; | 207 | |
| 205 | 208 | has_ts_events = true; | |
| 206 | hasTsEvents.store(1, std::memory_order_release); | ||
| 207 | } | 209 | } |
| 208 | 210 | ||
| 209 | // Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread | 211 | // Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread |
| 210 | // in which case the event will get handled immediately, before returning. | 212 | // in which case the event will get handled immediately, before returning. |
| 211 | void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) | 213 | void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) { |
| 212 | { | ||
| 213 | if (false) //Core::IsCPUThread()) | 214 | if (false) //Core::IsCPUThread()) |
| 214 | { | 215 | { |
| 215 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 216 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 216 | event_types[event_type].callback(userdata, 0); | 217 | event_types[event_type].callback(userdata, 0); |
| 217 | } | 218 | } |
| 218 | else | 219 | else |
| 219 | ScheduleEvent_Threadsafe(0, event_type, userdata); | 220 | ScheduleEvent_Threadsafe(0, event_type, userdata); |
| 220 | } | 221 | } |
| 221 | 222 | ||
| 222 | void ClearPendingEvents() | 223 | void ClearPendingEvents() { |
| 223 | { | 224 | while (first) { |
| 224 | while (first) | 225 | Event* event = first->next; |
| 225 | { | ||
| 226 | Event *e = first->next; | ||
| 227 | FreeEvent(first); | 226 | FreeEvent(first); |
| 228 | first = e; | 227 | first = event; |
| 229 | } | 228 | } |
| 230 | } | 229 | } |
| 231 | 230 | ||
| 232 | void AddEventToQueue(Event* ne) | 231 | void AddEventToQueue(Event* new_event) { |
| 233 | { | 232 | Event* prev_event = nullptr; |
| 234 | Event* prev = nullptr; | 233 | Event** next_event = &first; |
| 235 | Event** pNext = &first; | 234 | for (;;) { |
| 236 | for (;;) | 235 | Event*& next = *next_event; |
| 237 | { | 236 | if (!next || new_event->time < next->time) { |
| 238 | Event*& next = *pNext; | 237 | new_event->next = next; |
| 239 | if (!next || ne->time < next->time) | 238 | next = new_event; |
| 240 | { | ||
| 241 | ne->next = next; | ||
| 242 | next = ne; | ||
| 243 | break; | 239 | break; |
| 244 | } | 240 | } |
| 245 | prev = next; | 241 | prev_event = next; |
| 246 | pNext = &prev->next; | 242 | next_event = &prev_event->next; |
| 247 | } | 243 | } |
| 248 | } | 244 | } |
| 249 | 245 | ||
| 250 | // This must be run ONLY from within the cpu thread | 246 | void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata) { |
| 251 | // cyclesIntoFuture may be VERY inaccurate if called from anything else | 247 | Event* new_event = GetNewEvent(); |
| 252 | // than Advance | 248 | new_event->userdata = userdata; |
| 253 | void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata) | 249 | new_event->type = event_type; |
| 254 | { | 250 | new_event->time = GetTicks() + cycles_into_future; |
| 255 | Event *ne = GetNewEvent(); | 251 | AddEventToQueue(new_event); |
| 256 | ne->userdata = userdata; | ||
| 257 | ne->type = event_type; | ||
| 258 | ne->time = GetTicks() + cyclesIntoFuture; | ||
| 259 | AddEventToQueue(ne); | ||
| 260 | } | 252 | } |
| 261 | 253 | ||
| 262 | // Returns cycles left in timer. | 254 | s64 UnscheduleEvent(int event_type, u64 userdata) { |
| 263 | s64 UnscheduleEvent(int event_type, u64 userdata) | ||
| 264 | { | ||
| 265 | s64 result = 0; | 255 | s64 result = 0; |
| 266 | if (!first) | 256 | if (!first) |
| 267 | return result; | 257 | return result; |
| 268 | while (first) | 258 | while (first) { |
| 269 | { | 259 | if (first->type == event_type && first->userdata == userdata) { |
| 270 | if (first->type == event_type && first->userdata == userdata) | 260 | result = first->time - GetTicks(); |
| 271 | { | ||
| 272 | result = first->time - globalTimer; | ||
| 273 | 261 | ||
| 274 | Event *next = first->next; | 262 | Event* next = first->next; |
| 275 | FreeEvent(first); | 263 | FreeEvent(first); |
| 276 | first = next; | 264 | first = next; |
| 277 | } | 265 | } else { |
| 278 | else | ||
| 279 | { | ||
| 280 | break; | 266 | break; |
| 281 | } | 267 | } |
| 282 | } | 268 | } |
| 283 | if (!first) | 269 | if (!first) |
| 284 | return result; | 270 | return result; |
| 285 | Event *prev = first; | ||
| 286 | Event *ptr = prev->next; | ||
| 287 | while (ptr) | ||
| 288 | { | ||
| 289 | if (ptr->type == event_type && ptr->userdata == userdata) | ||
| 290 | { | ||
| 291 | result = ptr->time - globalTimer; | ||
| 292 | 271 | ||
| 293 | prev->next = ptr->next; | 272 | Event* prev_event = first; |
| 273 | Event* ptr = prev_event->next; | ||
| 274 | |||
| 275 | while (ptr) { | ||
| 276 | if (ptr->type == event_type && ptr->userdata == userdata) { | ||
| 277 | result = ptr->time - GetTicks(); | ||
| 278 | |||
| 279 | prev_event->next = ptr->next; | ||
| 294 | FreeEvent(ptr); | 280 | FreeEvent(ptr); |
| 295 | ptr = prev->next; | 281 | ptr = prev_event->next; |
| 296 | } | 282 | } else { |
| 297 | else | 283 | prev_event = ptr; |
| 298 | { | ||
| 299 | prev = ptr; | ||
| 300 | ptr = ptr->next; | 284 | ptr = ptr->next; |
| 301 | } | 285 | } |
| 302 | } | 286 | } |
| @@ -304,51 +288,44 @@ s64 UnscheduleEvent(int event_type, u64 userdata) | |||
| 304 | return result; | 288 | return result; |
| 305 | } | 289 | } |
| 306 | 290 | ||
| 307 | s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) | 291 | s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) { |
| 308 | { | ||
| 309 | s64 result = 0; | 292 | s64 result = 0; |
| 310 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 293 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 311 | if (!tsFirst) | 294 | if (!ts_first) |
| 312 | return result; | 295 | return result; |
| 313 | while (tsFirst) | ||
| 314 | { | ||
| 315 | if (tsFirst->type == event_type && tsFirst->userdata == userdata) | ||
| 316 | { | ||
| 317 | result = tsFirst->time - globalTimer; | ||
| 318 | 296 | ||
| 319 | Event *next = tsFirst->next; | 297 | while (ts_first) { |
| 320 | FreeTsEvent(tsFirst); | 298 | if (ts_first->type == event_type && ts_first->userdata == userdata) { |
| 321 | tsFirst = next; | 299 | result = ts_first->time - GetTicks(); |
| 322 | } | 300 | |
| 323 | else | 301 | Event* next = ts_first->next; |
| 324 | { | 302 | FreeTsEvent(ts_first); |
| 303 | ts_first = next; | ||
| 304 | } else { | ||
| 325 | break; | 305 | break; |
| 326 | } | 306 | } |
| 327 | } | 307 | } |
| 328 | if (!tsFirst) | 308 | |
| 309 | if (!ts_first) | ||
| 329 | { | 310 | { |
| 330 | tsLast = nullptr; | 311 | ts_last = nullptr; |
| 331 | return result; | 312 | return result; |
| 332 | } | 313 | } |
| 333 | 314 | ||
| 334 | Event *prev = tsFirst; | 315 | Event* prev_event = ts_first; |
| 335 | Event *ptr = prev->next; | 316 | Event* next = prev_event->next; |
| 336 | while (ptr) | 317 | while (next) { |
| 337 | { | 318 | if (next->type == event_type && next->userdata == userdata) { |
| 338 | if (ptr->type == event_type && ptr->userdata == userdata) | 319 | result = next->time - GetTicks(); |
| 339 | { | 320 | |
| 340 | result = ptr->time - globalTimer; | 321 | prev_event->next = next->next; |
| 341 | 322 | if (next == ts_last) | |
| 342 | prev->next = ptr->next; | 323 | ts_last = prev_event; |
| 343 | if (ptr == tsLast) | 324 | FreeTsEvent(next); |
| 344 | tsLast = prev; | 325 | next = prev_event->next; |
| 345 | FreeTsEvent(ptr); | 326 | } else { |
| 346 | ptr = prev->next; | 327 | prev_event = next; |
| 347 | } | 328 | next = next->next; |
| 348 | else | ||
| 349 | { | ||
| 350 | prev = ptr; | ||
| 351 | ptr = ptr->next; | ||
| 352 | } | 329 | } |
| 353 | } | 330 | } |
| 354 | 331 | ||
| @@ -356,271 +333,217 @@ s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) | |||
| 356 | } | 333 | } |
| 357 | 334 | ||
| 358 | // Warning: not included in save state. | 335 | // Warning: not included in save state. |
| 359 | void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted)) | 336 | void RegisterAdvanceCallback(AdvanceCallback* callback) { |
| 360 | { | 337 | advance_callback = callback; |
| 361 | advanceCallback = callback; | ||
| 362 | } | 338 | } |
| 363 | 339 | ||
| 364 | bool IsScheduled(int event_type) | 340 | void RegisterMHzChangeCallback(MHzChangeCallback callback) { |
| 365 | { | 341 | mhz_change_callbacks.push_back(callback); |
| 342 | } | ||
| 343 | |||
| 344 | bool IsScheduled(int event_type) { | ||
| 366 | if (!first) | 345 | if (!first) |
| 367 | return false; | 346 | return false; |
| 368 | Event *e = first; | 347 | Event* event = first; |
| 369 | while (e) { | 348 | while (event) { |
| 370 | if (e->type == event_type) | 349 | if (event->type == event_type) |
| 371 | return true; | 350 | return true; |
| 372 | e = e->next; | 351 | event = event->next; |
| 373 | } | 352 | } |
| 374 | return false; | 353 | return false; |
| 375 | } | 354 | } |
| 376 | 355 | ||
| 377 | void RemoveEvent(int event_type) | 356 | void RemoveEvent(int event_type) { |
| 378 | { | ||
| 379 | if (!first) | 357 | if (!first) |
| 380 | return; | 358 | return; |
| 381 | while (first) | 359 | while (first) { |
| 382 | { | 360 | if (first->type == event_type) { |
| 383 | if (first->type == event_type) | ||
| 384 | { | ||
| 385 | Event *next = first->next; | 361 | Event *next = first->next; |
| 386 | FreeEvent(first); | 362 | FreeEvent(first); |
| 387 | first = next; | 363 | first = next; |
| 388 | } | 364 | } else { |
| 389 | else | ||
| 390 | { | ||
| 391 | break; | 365 | break; |
| 392 | } | 366 | } |
| 393 | } | 367 | } |
| 394 | if (!first) | 368 | if (!first) |
| 395 | return; | 369 | return; |
| 396 | Event *prev = first; | 370 | Event* prev = first; |
| 397 | Event *ptr = prev->next; | 371 | Event* next = prev->next; |
| 398 | while (ptr) | 372 | while (next) { |
| 399 | { | 373 | if (next->type == event_type) { |
| 400 | if (ptr->type == event_type) | 374 | prev->next = next->next; |
| 401 | { | 375 | FreeEvent(next); |
| 402 | prev->next = ptr->next; | 376 | next = prev->next; |
| 403 | FreeEvent(ptr); | 377 | } else { |
| 404 | ptr = prev->next; | 378 | prev = next; |
| 405 | } | 379 | next = next->next; |
| 406 | else | ||
| 407 | { | ||
| 408 | prev = ptr; | ||
| 409 | ptr = ptr->next; | ||
| 410 | } | 380 | } |
| 411 | } | 381 | } |
| 412 | } | 382 | } |
| 413 | 383 | ||
| 414 | void RemoveThreadsafeEvent(int event_type) | 384 | void RemoveThreadsafeEvent(int event_type) { |
| 415 | { | 385 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 416 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 386 | if (!ts_first) |
| 417 | if (!tsFirst) | ||
| 418 | { | ||
| 419 | return; | 387 | return; |
| 420 | } | 388 | |
| 421 | while (tsFirst) | 389 | while (ts_first) { |
| 422 | { | 390 | if (ts_first->type == event_type) { |
| 423 | if (tsFirst->type == event_type) | 391 | Event* next = ts_first->next; |
| 424 | { | 392 | FreeTsEvent(ts_first); |
| 425 | Event *next = tsFirst->next; | 393 | ts_first = next; |
| 426 | FreeTsEvent(tsFirst); | 394 | } else { |
| 427 | tsFirst = next; | ||
| 428 | } | ||
| 429 | else | ||
| 430 | { | ||
| 431 | break; | 395 | break; |
| 432 | } | 396 | } |
| 433 | } | 397 | } |
| 434 | if (!tsFirst) | 398 | |
| 435 | { | 399 | if (!ts_first) { |
| 436 | tsLast = nullptr; | 400 | ts_last = nullptr; |
| 437 | return; | 401 | return; |
| 438 | } | 402 | } |
| 439 | Event *prev = tsFirst; | 403 | |
| 440 | Event *ptr = prev->next; | 404 | Event* prev = ts_first; |
| 441 | while (ptr) | 405 | Event* next = prev->next; |
| 442 | { | 406 | while (next) { |
| 443 | if (ptr->type == event_type) | 407 | if (next->type == event_type) { |
| 444 | { | 408 | prev->next = next->next; |
| 445 | prev->next = ptr->next; | 409 | if (next == ts_last) |
| 446 | if (ptr == tsLast) | 410 | ts_last = prev; |
| 447 | tsLast = prev; | 411 | FreeTsEvent(next); |
| 448 | FreeTsEvent(ptr); | 412 | next = prev->next; |
| 449 | ptr = prev->next; | 413 | } else { |
| 450 | } | 414 | prev = next; |
| 451 | else | 415 | next = next->next; |
| 452 | { | ||
| 453 | prev = ptr; | ||
| 454 | ptr = ptr->next; | ||
| 455 | } | 416 | } |
| 456 | } | 417 | } |
| 457 | } | 418 | } |
| 458 | 419 | ||
| 459 | void RemoveAllEvents(int event_type) | 420 | void RemoveAllEvents(int event_type) { |
| 460 | { | ||
| 461 | RemoveThreadsafeEvent(event_type); | 421 | RemoveThreadsafeEvent(event_type); |
| 462 | RemoveEvent(event_type); | 422 | RemoveEvent(event_type); |
| 463 | } | 423 | } |
| 464 | 424 | ||
| 465 | //This raise only the events required while the fifo is processing data | 425 | // This raise only the events required while the fifo is processing data |
| 466 | void ProcessFifoWaitEvents() | 426 | void ProcessFifoWaitEvents() { |
| 467 | { | 427 | while (first) { |
| 468 | while (first) | 428 | if (first->time <= (s64)GetTicks()) { |
| 469 | { | ||
| 470 | if (first->time <= globalTimer) | ||
| 471 | { | ||
| 472 | //LOG(TIMER, "[Scheduler] %s (%lld, %lld) ", | ||
| 473 | // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time); | ||
| 474 | Event* evt = first; | 429 | Event* evt = first; |
| 475 | first = first->next; | 430 | first = first->next; |
| 476 | event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time)); | 431 | event_types[evt->type].callback(evt->userdata, (int)(GetTicks() - evt->time)); |
| 477 | FreeEvent(evt); | 432 | FreeEvent(evt); |
| 478 | } | 433 | } else { |
| 479 | else | ||
| 480 | { | ||
| 481 | break; | 434 | break; |
| 482 | } | 435 | } |
| 483 | } | 436 | } |
| 484 | } | 437 | } |
| 485 | 438 | ||
| 486 | void MoveEvents() | 439 | void MoveEvents() { |
| 487 | { | 440 | has_ts_events = false; |
| 488 | hasTsEvents.store(0, std::memory_order_release); | ||
| 489 | 441 | ||
| 490 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 442 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 491 | // Move events from async queue into main queue | 443 | // Move events from async queue into main queue |
| 492 | while (tsFirst) | 444 | while (ts_first) { |
| 493 | { | 445 | Event* next = ts_first->next; |
| 494 | Event *next = tsFirst->next; | 446 | AddEventToQueue(ts_first); |
| 495 | AddEventToQueue(tsFirst); | 447 | ts_first = next; |
| 496 | tsFirst = next; | ||
| 497 | } | 448 | } |
| 498 | tsLast = nullptr; | 449 | ts_last = nullptr; |
| 499 | 450 | ||
| 500 | // Move free events to threadsafe pool | 451 | // Move free events to threadsafe pool |
| 501 | while (allocatedTsEvents > 0 && eventPool) | 452 | while (allocated_ts_events > 0 && event_pool) { |
| 502 | { | 453 | Event* event = event_pool; |
| 503 | Event *ev = eventPool; | 454 | event_pool = event->next; |
| 504 | eventPool = ev->next; | 455 | event->next = event_ts_pool; |
| 505 | ev->next = eventTsPool; | 456 | event_ts_pool = event; |
| 506 | eventTsPool = ev; | 457 | allocated_ts_events--; |
| 507 | allocatedTsEvents--; | ||
| 508 | } | 458 | } |
| 509 | } | 459 | } |
| 510 | 460 | ||
| 511 | void Advance() | 461 | void ForceCheck() { |
| 512 | { | 462 | int cycles_executed = g_slice_length - Core::g_app_core->down_count; |
| 513 | LOG_ERROR(Core, "Unimplemented function!"); | 463 | global_timer += cycles_executed; |
| 514 | //int cyclesExecuted = slicelength - currentMIPS->downcount; | 464 | // This will cause us to check for new events immediately. |
| 515 | //globalTimer += cyclesExecuted; | 465 | Core::g_app_core->down_count = 0; |
| 516 | //currentMIPS->downcount = slicelength; | 466 | // But let's not eat a bunch more time in Advance() because of this. |
| 517 | 467 | g_slice_length = 0; | |
| 518 | //if (Common::AtomicLoadAcquire(hasTsEvents)) | ||
| 519 | // MoveEvents(); | ||
| 520 | //ProcessFifoWaitEvents(); | ||
| 521 | |||
| 522 | //if (!first) | ||
| 523 | //{ | ||
| 524 | // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000"); | ||
| 525 | // currentMIPS->downcount += 10000; | ||
| 526 | //} | ||
| 527 | //else | ||
| 528 | //{ | ||
| 529 | // slicelength = (int)(first->time - globalTimer); | ||
| 530 | // if (slicelength > MAX_SLICE_LENGTH) | ||
| 531 | // slicelength = MAX_SLICE_LENGTH; | ||
| 532 | // currentMIPS->downcount = slicelength; | ||
| 533 | //} | ||
| 534 | //if (advanceCallback) | ||
| 535 | // advanceCallback(cyclesExecuted); | ||
| 536 | } | ||
| 537 | |||
| 538 | void LogPendingEvents() | ||
| 539 | { | ||
| 540 | Event *ptr = first; | ||
| 541 | while (ptr) | ||
| 542 | { | ||
| 543 | //INFO_LOG(TIMER, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type); | ||
| 544 | ptr = ptr->next; | ||
| 545 | } | ||
| 546 | } | 468 | } |
| 547 | 469 | ||
| 548 | void Idle(int maxIdle) | 470 | void Advance() { |
| 549 | { | 471 | int cycles_executed = g_slice_length - Core::g_app_core->down_count; |
| 550 | LOG_ERROR(Core, "Unimplemented function!"); | 472 | global_timer += cycles_executed; |
| 551 | //int cyclesDown = currentMIPS->downcount; | 473 | Core::g_app_core->down_count = g_slice_length; |
| 552 | //if (maxIdle != 0 && cyclesDown > maxIdle) | ||
| 553 | // cyclesDown = maxIdle; | ||
| 554 | |||
| 555 | //if (first && cyclesDown > 0) | ||
| 556 | //{ | ||
| 557 | // int cyclesExecuted = slicelength - currentMIPS->downcount; | ||
| 558 | // int cyclesNextEvent = (int) (first->time - globalTimer); | ||
| 559 | |||
| 560 | // if (cyclesNextEvent < cyclesExecuted + cyclesDown) | ||
| 561 | // { | ||
| 562 | // cyclesDown = cyclesNextEvent - cyclesExecuted; | ||
| 563 | // // Now, now... no time machines, please. | ||
| 564 | // if (cyclesDown < 0) | ||
| 565 | // cyclesDown = 0; | ||
| 566 | // } | ||
| 567 | //} | ||
| 568 | |||
| 569 | //INFO_LOG(TIME, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(g_clock_rate_arm11 * 0.001f)); | ||
| 570 | |||
| 571 | //idledCycles += cyclesDown; | ||
| 572 | //currentMIPS->downcount -= cyclesDown; | ||
| 573 | //if (currentMIPS->downcount == 0) | ||
| 574 | // currentMIPS->downcount = -1; | ||
| 575 | } | ||
| 576 | |||
| 577 | std::string GetScheduledEventsSummary() | ||
| 578 | { | ||
| 579 | Event *ptr = first; | ||
| 580 | std::string text = "Scheduled events\n"; | ||
| 581 | text.reserve(1000); | ||
| 582 | while (ptr) | ||
| 583 | { | ||
| 584 | unsigned int t = ptr->type; | ||
| 585 | if (t >= event_types.size()) | ||
| 586 | PanicAlert("Invalid event type"); // %i", t); | ||
| 587 | const char *name = event_types[ptr->type].name; | ||
| 588 | if (!name) | ||
| 589 | name = "[unknown]"; | ||
| 590 | 474 | ||
| 591 | text += Common::StringFromFormat("%s : %i %08x%08x\n", name, (int)ptr->time, | 475 | if (has_ts_events) |
| 592 | (u32)(ptr->userdata >> 32), (u32)(ptr->userdata)); | 476 | MoveEvents(); |
| 477 | ProcessFifoWaitEvents(); | ||
| 593 | 478 | ||
| 594 | ptr = ptr->next; | 479 | if (!first) { |
| 480 | if (g_slice_length < 10000) { | ||
| 481 | g_slice_length += 10000; | ||
| 482 | Core::g_app_core->down_count += g_slice_length; | ||
| 483 | } | ||
| 484 | } else { | ||
| 485 | // Note that events can eat cycles as well. | ||
| 486 | int target = (int)(first->time - global_timer); | ||
| 487 | if (target > MAX_SLICE_LENGTH) | ||
| 488 | target = MAX_SLICE_LENGTH; | ||
| 489 | |||
| 490 | const int diff = target - g_slice_length; | ||
| 491 | g_slice_length += diff; | ||
| 492 | Core::g_app_core->down_count += diff; | ||
| 595 | } | 493 | } |
| 596 | return text; | 494 | if (advance_callback) |
| 495 | advance_callback(cycles_executed); | ||
| 597 | } | 496 | } |
| 598 | 497 | ||
| 599 | void Event_DoState(PointerWrap &p, BaseEvent *ev) | 498 | void LogPendingEvents() { |
| 600 | { | 499 | Event* event = first; |
| 601 | p.Do(*ev); | 500 | while (event) { |
| 501 | //LOG_TRACE(Core_Timing, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, next->time, next->type); | ||
| 502 | event = event->next; | ||
| 503 | } | ||
| 602 | } | 504 | } |
| 603 | 505 | ||
| 604 | void DoState(PointerWrap &p) | 506 | void Idle(int max_idle) { |
| 605 | { | 507 | int cycles_down = Core::g_app_core->down_count; |
| 606 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 508 | if (max_idle != 0 && cycles_down > max_idle) |
| 509 | cycles_down = max_idle; | ||
| 607 | 510 | ||
| 608 | auto s = p.Section("CoreTiming", 1); | 511 | if (first && cycles_down > 0) { |
| 609 | if (!s) | 512 | int cycles_executed = g_slice_length - Core::g_app_core->down_count; |
| 610 | return; | 513 | int cycles_next_event = (int)(first->time - global_timer); |
| 611 | 514 | ||
| 612 | int n = (int)event_types.size(); | 515 | if (cycles_next_event < cycles_executed + cycles_down) { |
| 613 | p.Do(n); | 516 | cycles_down = cycles_next_event - cycles_executed; |
| 614 | // These (should) be filled in later by the modules. | 517 | // Now, now... no time machines, please. |
| 615 | event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT")); | 518 | if (cycles_down < 0) |
| 519 | cycles_down = 0; | ||
| 520 | } | ||
| 521 | } | ||
| 616 | 522 | ||
| 617 | p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)nullptr); | 523 | LOG_TRACE(Core_Timing, "Idle for %i cycles! (%f ms)", cycles_down, cycles_down / (float)(g_clock_rate_arm11 * 0.001f)); |
| 618 | p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast); | ||
| 619 | 524 | ||
| 620 | p.Do(g_clock_rate_arm11); | 525 | idled_cycles += cycles_down; |
| 621 | p.Do(slicelength); | 526 | Core::g_app_core->down_count -= cycles_down; |
| 622 | p.Do(globalTimer); | 527 | if (Core::g_app_core->down_count == 0) |
| 623 | p.Do(idledCycles); | 528 | Core::g_app_core->down_count = -1; |
| 529 | } | ||
| 530 | |||
| 531 | std::string GetScheduledEventsSummary() { | ||
| 532 | Event* event = first; | ||
| 533 | std::string text = "Scheduled events\n"; | ||
| 534 | text.reserve(1000); | ||
| 535 | while (event) { | ||
| 536 | unsigned int t = event->type; | ||
| 537 | if (t >= event_types.size()) | ||
| 538 | PanicAlert("Invalid event type"); // %i", t); | ||
| 539 | const char* name = event_types[event->type].name; | ||
| 540 | if (!name) | ||
| 541 | name = "[unknown]"; | ||
| 542 | text += Common::StringFromFormat("%s : %i %08x%08x\n", name, (int)event->time, | ||
| 543 | (u32)(event->userdata >> 32), (u32)(event->userdata)); | ||
| 544 | event = event->next; | ||
| 545 | } | ||
| 546 | return text; | ||
| 624 | } | 547 | } |
| 625 | 548 | ||
| 626 | } // namespace | 549 | } // namespace |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 496234538..d62ff3604 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -1,9 +1,11 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // Copyright (c) 2012- PPSSPP Project / Dolphin Project. |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | ||
| 8 | |||
| 7 | // This is a system to schedule events into the emulated machine's future. Time is measured | 9 | // This is a system to schedule events into the emulated machine's future. Time is measured |
| 8 | // in main CPU clock cycles. | 10 | // in main CPU clock cycles. |
| 9 | 11 | ||
| @@ -12,14 +14,14 @@ | |||
| 12 | 14 | ||
| 13 | // See HW/SystemTimers.cpp for the main part of Dolphin's usage of this scheduler. | 15 | // See HW/SystemTimers.cpp for the main part of Dolphin's usage of this scheduler. |
| 14 | 16 | ||
| 15 | // The int cyclesLate that the callbacks get is how many cycles late it was. | 17 | // The int cycles_late that the callbacks get is how many cycles late it was. |
| 16 | // So to schedule a new event on a regular basis: | 18 | // So to schedule a new event on a regular basis: |
| 17 | // inside callback: | 19 | // inside callback: |
| 18 | // ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") | 20 | // ScheduleEvent(periodInCycles - cycles_late, callback, "whatever") |
| 19 | 21 | ||
| 20 | #include "common/common.h" | 22 | #include <functional> |
| 21 | 23 | ||
| 22 | class PointerWrap; | 24 | #include "common/common.h" |
| 23 | 25 | ||
| 24 | extern int g_clock_rate_arm11; | 26 | extern int g_clock_rate_arm11; |
| 25 | 27 | ||
| @@ -55,55 +57,84 @@ inline s64 cyclesToUs(s64 cycles) { | |||
| 55 | return cycles / (g_clock_rate_arm11 / 1000000); | 57 | return cycles / (g_clock_rate_arm11 / 1000000); |
| 56 | } | 58 | } |
| 57 | 59 | ||
| 58 | namespace CoreTiming { | 60 | inline u64 cyclesToMs(s64 cycles) { |
| 61 | return cycles / (g_clock_rate_arm11 / 1000); | ||
| 62 | } | ||
| 59 | 63 | ||
| 64 | namespace CoreTiming | ||
| 65 | { | ||
| 60 | void Init(); | 66 | void Init(); |
| 61 | void Shutdown(); | 67 | void Shutdown(); |
| 62 | 68 | ||
| 63 | typedef void(*TimedCallback)(u64 userdata, int cyclesLate); | 69 | typedef void(*MHzChangeCallback)(); |
| 70 | typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; | ||
| 64 | 71 | ||
| 65 | u64 GetTicks(); | 72 | u64 GetTicks(); |
| 66 | u64 GetIdleTicks(); | 73 | u64 GetIdleTicks(); |
| 67 | 74 | u64 GetGlobalTimeUs(); | |
| 68 | // Returns the event_type identifier. | 75 | |
| 69 | int RegisterEvent(const char *name, TimedCallback callback); | 76 | /** |
| 70 | // For save states. | 77 | * Registers an event type with the specified name and callback |
| 78 | * @param name Name of the event type | ||
| 79 | * @param callback Function that will execute when this event fires | ||
| 80 | * @returns An identifier for the event type that was registered | ||
| 81 | */ | ||
| 82 | int RegisterEvent(const char* name, TimedCallback callback); | ||
| 83 | /// For save states. | ||
| 71 | void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback); | 84 | void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback); |
| 72 | void UnregisterAllEvents(); | 85 | void UnregisterAllEvents(); |
| 73 | 86 | ||
| 74 | // userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk, | 87 | /// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk, |
| 75 | // when we implement state saves. | 88 | /// when we implement state saves. |
| 76 | void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata = 0); | 89 | /** |
| 77 | void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata = 0); | 90 | * Schedules an event to run after the specified number of cycles, |
| 91 | * with an optional parameter to be passed to the callback handler. | ||
| 92 | * This must be run ONLY from within the cpu thread. | ||
| 93 | * @param cycles_into_future The number of cycles after which this event will be fired | ||
| 94 | * @param event_type The event type to fire, as returned from RegisterEvent | ||
| 95 | * @param userdata Optional parameter to pass to the callback when fired | ||
| 96 | */ | ||
| 97 | void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata = 0); | ||
| 98 | |||
| 99 | void ScheduleEvent_Threadsafe(s64 cycles_into_future, int event_type, u64 userdata = 0); | ||
| 78 | void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata = 0); | 100 | void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata = 0); |
| 101 | |||
| 102 | /** | ||
| 103 | * Unschedules an event with the specified type and userdata | ||
| 104 | * @param event_type The type of event to unschedule, as returned from RegisterEvent | ||
| 105 | * @param userdata The userdata that identifies this event, as passed to ScheduleEvent | ||
| 106 | * @returns The remaining ticks until the next invocation of the event callback | ||
| 107 | */ | ||
| 79 | s64 UnscheduleEvent(int event_type, u64 userdata); | 108 | s64 UnscheduleEvent(int event_type, u64 userdata); |
| 109 | |||
| 80 | s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata); | 110 | s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata); |
| 81 | 111 | ||
| 82 | void RemoveEvent(int event_type); | 112 | void RemoveEvent(int event_type); |
| 83 | void RemoveThreadsafeEvent(int event_type); | 113 | void RemoveThreadsafeEvent(int event_type); |
| 84 | void RemoveAllEvents(int event_type); | 114 | void RemoveAllEvents(int event_type); |
| 85 | bool IsScheduled(int event_type); | 115 | bool IsScheduled(int event_type); |
| 116 | /// Runs any pending events and updates downcount for the next slice of cycles | ||
| 86 | void Advance(); | 117 | void Advance(); |
| 87 | void MoveEvents(); | 118 | void MoveEvents(); |
| 88 | void ProcessFifoWaitEvents(); | 119 | void ProcessFifoWaitEvents(); |
| 120 | void ForceCheck(); | ||
| 89 | 121 | ||
| 90 | // Pretend that the main CPU has executed enough cycles to reach the next event. | 122 | /// Pretend that the main CPU has executed enough cycles to reach the next event. |
| 91 | void Idle(int maxIdle = 0); | 123 | void Idle(int maxIdle = 0); |
| 92 | 124 | ||
| 93 | // Clear all pending events. This should ONLY be done on exit or state load. | 125 | /// Clear all pending events. This should ONLY be done on exit or state load. |
| 94 | void ClearPendingEvents(); | 126 | void ClearPendingEvents(); |
| 95 | 127 | ||
| 96 | void LogPendingEvents(); | 128 | void LogPendingEvents(); |
| 97 | 129 | ||
| 98 | // Warning: not included in save states. | 130 | /// Warning: not included in save states. |
| 99 | void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted)); | 131 | void RegisterAdvanceCallback(void(*callback)(int cycles_executed)); |
| 132 | void RegisterMHzChangeCallback(MHzChangeCallback callback); | ||
| 100 | 133 | ||
| 101 | std::string GetScheduledEventsSummary(); | 134 | std::string GetScheduledEventsSummary(); |
| 102 | 135 | ||
| 103 | void DoState(PointerWrap &p); | 136 | void SetClockFrequencyMHz(int cpu_mhz); |
| 104 | |||
| 105 | void SetClockFrequencyMHz(int cpuMhz); | ||
| 106 | int GetClockFrequencyMHz(); | 137 | int GetClockFrequencyMHz(); |
| 107 | extern int slicelength; | 138 | extern int g_slice_length; |
| 108 | 139 | ||
| 109 | } // namespace | 140 | } // namespace |
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index 4759ef3ae..0805f42ae 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include "core/file_sys/archive_extsavedata.h" | 10 | #include "core/file_sys/archive_extsavedata.h" |
| 11 | #include "core/file_sys/disk_archive.h" | 11 | #include "core/file_sys/disk_archive.h" |
| 12 | #include "core/hle/service/fs/archive.h" | ||
| 12 | #include "core/settings.h" | 13 | #include "core/settings.h" |
| 13 | 14 | ||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -19,15 +20,22 @@ namespace FileSys { | |||
| 19 | static std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) { | 20 | static std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) { |
| 20 | std::vector<u8> vec_data = path.AsBinary(); | 21 | std::vector<u8> vec_data = path.AsBinary(); |
| 21 | const u32* data = reinterpret_cast<const u32*>(vec_data.data()); | 22 | const u32* data = reinterpret_cast<const u32*>(vec_data.data()); |
| 22 | u32 media_type = data[0]; | ||
| 23 | u32 save_low = data[1]; | 23 | u32 save_low = data[1]; |
| 24 | u32 save_high = data[2]; | 24 | u32 save_high = data[2]; |
| 25 | return Common::StringFromFormat("%s%s/%08X/%08X/", mount_point.c_str(), media_type == 0 ? "nand" : "sdmc", save_high, save_low); | 25 | return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_high, save_low); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | Archive_ExtSaveData::Archive_ExtSaveData(const std::string& mount_point) | 28 | static std::string GetExtDataContainerPath(const std::string& mount_point, bool shared) { |
| 29 | : DiskArchive(mount_point), concrete_mount_point(mount_point) { | 29 | if (shared) |
| 30 | LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", this->mount_point.c_str()); | 30 | return Common::StringFromFormat("%sdata/%s/extdata/", mount_point.c_str(), SYSTEM_ID.c_str()); |
| 31 | |||
| 32 | return Common::StringFromFormat("%sNintendo 3DS/%s/%s/extdata/", mount_point.c_str(), | ||
| 33 | SYSTEM_ID.c_str(), SDCARD_ID.c_str()); | ||
| 34 | } | ||
| 35 | |||
| 36 | Archive_ExtSaveData::Archive_ExtSaveData(const std::string& mount_location, bool shared) | ||
| 37 | : DiskArchive(GetExtDataContainerPath(mount_location, shared)) { | ||
| 38 | LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str()); | ||
| 31 | } | 39 | } |
| 32 | 40 | ||
| 33 | bool Archive_ExtSaveData::Initialize() { | 41 | bool Archive_ExtSaveData::Initialize() { |
diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h index a3a144799..fb7f209d2 100644 --- a/src/core/file_sys/archive_extsavedata.h +++ b/src/core/file_sys/archive_extsavedata.h | |||
| @@ -17,7 +17,7 @@ namespace FileSys { | |||
| 17 | /// File system interface to the ExtSaveData archive | 17 | /// File system interface to the ExtSaveData archive |
| 18 | class Archive_ExtSaveData final : public DiskArchive { | 18 | class Archive_ExtSaveData final : public DiskArchive { |
| 19 | public: | 19 | public: |
| 20 | Archive_ExtSaveData(const std::string& mount_point); | 20 | Archive_ExtSaveData(const std::string& mount_point, bool shared); |
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| 23 | * Initialize the archive. | 23 | * Initialize the archive. |
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index 280d4ff5d..3baee5294 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include "core/file_sys/archive_savedata.h" | 10 | #include "core/file_sys/archive_savedata.h" |
| 11 | #include "core/file_sys/disk_archive.h" | 11 | #include "core/file_sys/disk_archive.h" |
| 12 | #include "core/hle/service/fs/archive.h" | ||
| 12 | #include "core/settings.h" | 13 | #include "core/settings.h" |
| 13 | 14 | ||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -16,14 +17,25 @@ | |||
| 16 | 17 | ||
| 17 | namespace FileSys { | 18 | namespace FileSys { |
| 18 | 19 | ||
| 19 | Archive_SaveData::Archive_SaveData(const std::string& mount_point) | 20 | static std::string GetSaveDataContainerPath(const std::string& sdmc_directory) { |
| 20 | : DiskArchive(mount_point) { | 21 | return Common::StringFromFormat("%sNintendo 3DS/%s/%s/title/", sdmc_directory.c_str(), |
| 22 | SYSTEM_ID.c_str(), SDCARD_ID.c_str()); | ||
| 23 | } | ||
| 24 | |||
| 25 | static std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) { | ||
| 26 | u32 high = program_id >> 32; | ||
| 27 | u32 low = program_id & 0xFFFFFFFF; | ||
| 28 | return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high, low); | ||
| 29 | } | ||
| 30 | |||
| 31 | Archive_SaveData::Archive_SaveData(const std::string& sdmc_directory) | ||
| 32 | : DiskArchive(GetSaveDataContainerPath(sdmc_directory)) { | ||
| 21 | LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str()); | 33 | LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str()); |
| 22 | } | 34 | } |
| 23 | 35 | ||
| 24 | ResultCode Archive_SaveData::Open(const Path& path) { | 36 | ResultCode Archive_SaveData::Open(const Path& path) { |
| 25 | if (concrete_mount_point.empty()) | 37 | if (concrete_mount_point.empty()) |
| 26 | concrete_mount_point = Common::StringFromFormat("%s%016X", mount_point.c_str(), Kernel::g_program_id) + DIR_SEP; | 38 | concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_program_id); |
| 27 | if (!FileUtil::Exists(concrete_mount_point)) { | 39 | if (!FileUtil::Exists(concrete_mount_point)) { |
| 28 | // When a SaveData archive is created for the first time, it is not yet formatted | 40 | // When a SaveData archive is created for the first time, it is not yet formatted |
| 29 | // and the save file/directory structure expected by the game has not yet been initialized. | 41 | // and the save file/directory structure expected by the game has not yet been initialized. |
diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_savedatacheck.cpp index 233158a0c..a7a507536 100644 --- a/src/core/file_sys/archive_savedatacheck.cpp +++ b/src/core/file_sys/archive_savedatacheck.cpp | |||
| @@ -5,13 +5,24 @@ | |||
| 5 | #include "common/file_util.h" | 5 | #include "common/file_util.h" |
| 6 | 6 | ||
| 7 | #include "core/file_sys/archive_savedatacheck.h" | 7 | #include "core/file_sys/archive_savedatacheck.h" |
| 8 | #include "core/hle/service/fs/archive.h" | ||
| 8 | 9 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 10 | // FileSys namespace | 11 | // FileSys namespace |
| 11 | 12 | ||
| 12 | namespace FileSys { | 13 | namespace FileSys { |
| 13 | 14 | ||
| 14 | Archive_SaveDataCheck::Archive_SaveDataCheck(const std::string& mount_loc) : mount_point(mount_loc) { | 15 | static std::string GetSaveDataCheckContainerPath(const std::string& nand_directory) { |
| 16 | return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID.c_str()); | ||
| 17 | } | ||
| 18 | |||
| 19 | static std::string GetSaveDataCheckPath(const std::string& mount_point, u32 high, u32 low) { | ||
| 20 | return Common::StringFromFormat("%s%08x/%08x/content/00000000.app.romfs", | ||
| 21 | mount_point.c_str(), high, low); | ||
| 22 | } | ||
| 23 | |||
| 24 | Archive_SaveDataCheck::Archive_SaveDataCheck(const std::string& nand_directory) : | ||
| 25 | mount_point(GetSaveDataCheckContainerPath(nand_directory)) { | ||
| 15 | } | 26 | } |
| 16 | 27 | ||
| 17 | ResultCode Archive_SaveDataCheck::Open(const Path& path) { | 28 | ResultCode Archive_SaveDataCheck::Open(const Path& path) { |
| @@ -23,7 +34,7 @@ ResultCode Archive_SaveDataCheck::Open(const Path& path) { | |||
| 23 | // this archive again with a different path, will corrupt the previously open file. | 34 | // this archive again with a different path, will corrupt the previously open file. |
| 24 | auto vec = path.AsBinary(); | 35 | auto vec = path.AsBinary(); |
| 25 | const u32* data = reinterpret_cast<u32*>(vec.data()); | 36 | const u32* data = reinterpret_cast<u32*>(vec.data()); |
| 26 | std::string file_path = Common::StringFromFormat("%s%08x%08x.bin", mount_point.c_str(), data[1], data[0]); | 37 | std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]); |
| 27 | FileUtil::IOFile file(file_path, "rb"); | 38 | FileUtil::IOFile file(file_path, "rb"); |
| 28 | 39 | ||
| 29 | std::fill(raw_data.begin(), raw_data.end(), 0); | 40 | std::fill(raw_data.begin(), raw_data.end(), 0); |
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 1c1c170b6..26b03e82f 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp | |||
| @@ -16,8 +16,8 @@ | |||
| 16 | 16 | ||
| 17 | namespace FileSys { | 17 | namespace FileSys { |
| 18 | 18 | ||
| 19 | Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) { | 19 | Archive_SDMC::Archive_SDMC(const std::string& sdmc_directory) : DiskArchive(sdmc_directory) { |
| 20 | LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str()); | 20 | LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str()); |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | bool Archive_SDMC::Initialize() { | 23 | bool Archive_SDMC::Initialize() { |
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index 0da32d510..c2a5d641a 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include "core/file_sys/archive_systemsavedata.h" | 10 | #include "core/file_sys/archive_systemsavedata.h" |
| 11 | #include "core/file_sys/disk_archive.h" | 11 | #include "core/file_sys/disk_archive.h" |
| 12 | #include "core/hle/service/fs/archive.h" | ||
| 12 | #include "core/settings.h" | 13 | #include "core/settings.h" |
| 13 | 14 | ||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -22,8 +23,12 @@ static std::string GetSystemSaveDataPath(const std::string& mount_point, u64 sav | |||
| 22 | return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high); | 23 | return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high); |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 26 | static std::string GetSystemSaveDataContainerPath(const std::string& mount_point) { | ||
| 27 | return Common::StringFromFormat("%sdata/%s/sysdata/", mount_point.c_str(), SYSTEM_ID.c_str()); | ||
| 28 | } | ||
| 29 | |||
| 25 | Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id) | 30 | Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id) |
| 26 | : DiskArchive(GetSystemSaveDataPath(mount_point, save_id)) { | 31 | : DiskArchive(GetSystemSaveDataPath(GetSystemSaveDataContainerPath(mount_point), save_id)) { |
| 27 | LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str()); | 32 | LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str()); |
| 28 | } | 33 | } |
| 29 | 34 | ||
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h index 55d85193c..c8f5845ca 100644 --- a/src/core/file_sys/archive_systemsavedata.h +++ b/src/core/file_sys/archive_systemsavedata.h | |||
| @@ -15,8 +15,6 @@ | |||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| 17 | /// File system interface to the SystemSaveData archive | 17 | /// File system interface to the SystemSaveData archive |
| 18 | /// TODO(Subv): This archive should point to a location in the NAND, | ||
| 19 | /// specifically nand:/data/<ID0>/sysdata/<SaveID-Low>/<SaveID-High> | ||
| 20 | class Archive_SystemSaveData final : public DiskArchive { | 18 | class Archive_SystemSaveData final : public DiskArchive { |
| 21 | public: | 19 | public: |
| 22 | Archive_SystemSaveData(const std::string& mount_point, u64 save_id); | 20 | Archive_SystemSaveData(const std::string& mount_point, u64 save_id); |
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 0f822f84b..a2f51b41b 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | |||
| 9 | #include "core/arm/arm_interface.h" | ||
| 8 | #include "core/mem_map.h" | 10 | #include "core/mem_map.h" |
| 9 | #include "core/hle/hle.h" | 11 | #include "core/hle/hle.h" |
| 10 | 12 | ||
| @@ -135,6 +137,12 @@ template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() { | |||
| 135 | FuncReturn(retval); | 137 | FuncReturn(retval); |
| 136 | } | 138 | } |
| 137 | 139 | ||
| 140 | template<s32 func(u32, s64, s64)> void Wrap() { | ||
| 141 | s64 param1 = ((u64)PARAM(3) << 32) | PARAM(2); | ||
| 142 | s64 param2 = ((u64)PARAM(4) << 32) | PARAM(1); | ||
| 143 | FuncReturn(func(PARAM(0), param1, param2)); | ||
| 144 | } | ||
| 145 | |||
| 138 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 146 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 139 | // Function wrappers that return type u32 | 147 | // Function wrappers that return type u32 |
| 140 | 148 | ||
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index 33ac12507..5d77a1458 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <vector> | 5 | #include <vector> |
| 6 | 6 | ||
| 7 | #include "core/arm/arm_interface.h" | ||
| 7 | #include "core/mem_map.h" | 8 | #include "core/mem_map.h" |
| 8 | #include "core/hle/hle.h" | 9 | #include "core/hle/hle.h" |
| 9 | #include "core/hle/kernel/thread.h" | 10 | #include "core/hle/kernel/thread.h" |
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h index 59b770f02..3f6f9a4b5 100644 --- a/src/core/hle/hle.h +++ b/src/core/hle/hle.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | ||
| 8 | |||
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | #include "core/core.h" | 10 | #include "core/core.h" |
| 9 | 11 | ||
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 736bbc36a..62e3460e1 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -30,24 +30,28 @@ public: | |||
| 30 | 30 | ||
| 31 | /// Arbitrate an address | 31 | /// Arbitrate an address |
| 32 | ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { | 32 | ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { |
| 33 | Object* object = Kernel::g_handle_table.GetGeneric(handle).get(); | ||
| 34 | if (object == nullptr) | ||
| 35 | return InvalidHandle(ErrorModule::Kernel); | ||
| 36 | |||
| 33 | switch (type) { | 37 | switch (type) { |
| 34 | 38 | ||
| 35 | // Signal thread(s) waiting for arbitrate address... | 39 | // Signal thread(s) waiting for arbitrate address... |
| 36 | case ArbitrationType::Signal: | 40 | case ArbitrationType::Signal: |
| 37 | // Negative value means resume all threads | 41 | // Negative value means resume all threads |
| 38 | if (value < 0) { | 42 | if (value < 0) { |
| 39 | ArbitrateAllThreads(handle, address); | 43 | ArbitrateAllThreads(object, address); |
| 40 | } else { | 44 | } else { |
| 41 | // Resume first N threads | 45 | // Resume first N threads |
| 42 | for(int i = 0; i < value; i++) | 46 | for(int i = 0; i < value; i++) |
| 43 | ArbitrateHighestPriorityThread(handle, address); | 47 | ArbitrateHighestPriorityThread(object, address); |
| 44 | } | 48 | } |
| 45 | break; | 49 | break; |
| 46 | 50 | ||
| 47 | // Wait current thread (acquire the arbiter)... | 51 | // Wait current thread (acquire the arbiter)... |
| 48 | case ArbitrationType::WaitIfLessThan: | 52 | case ArbitrationType::WaitIfLessThan: |
| 49 | if ((s32)Memory::Read32(address) <= value) { | 53 | if ((s32)Memory::Read32(address) <= value) { |
| 50 | Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); | 54 | Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); |
| 51 | HLE::Reschedule(__func__); | 55 | HLE::Reschedule(__func__); |
| 52 | } | 56 | } |
| 53 | break; | 57 | break; |
| @@ -57,7 +61,7 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 | |||
| 57 | s32 memory_value = Memory::Read32(address) - 1; | 61 | s32 memory_value = Memory::Read32(address) - 1; |
| 58 | Memory::Write32(address, memory_value); | 62 | Memory::Write32(address, memory_value); |
| 59 | if (memory_value <= value) { | 63 | if (memory_value <= value) { |
| 60 | Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); | 64 | Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); |
| 61 | HLE::Reschedule(__func__); | 65 | HLE::Reschedule(__func__); |
| 62 | } | 66 | } |
| 63 | break; | 67 | break; |
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index e43c3ee4e..271190dbe 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp | |||
| @@ -33,11 +33,11 @@ public: | |||
| 33 | ResultVal<bool> WaitSynchronization() override { | 33 | ResultVal<bool> WaitSynchronization() override { |
| 34 | bool wait = locked; | 34 | bool wait = locked; |
| 35 | if (locked) { | 35 | if (locked) { |
| 36 | Handle thread = GetCurrentThreadHandle(); | 36 | Handle thread = GetCurrentThread()->GetHandle(); |
| 37 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { | 37 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { |
| 38 | waiting_threads.push_back(thread); | 38 | waiting_threads.push_back(thread); |
| 39 | } | 39 | } |
| 40 | Kernel::WaitCurrentThread(WAITTYPE_EVENT, GetHandle()); | 40 | Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); |
| 41 | } | 41 | } |
| 42 | if (reset_type != RESETTYPE_STICKY && !permanent_locked) { | 42 | if (reset_type != RESETTYPE_STICKY && !permanent_locked) { |
| 43 | locked = true; | 43 | locked = true; |
| @@ -53,7 +53,7 @@ public: | |||
| 53 | * @return Result of operation, 0 on success, otherwise error code | 53 | * @return Result of operation, 0 on success, otherwise error code |
| 54 | */ | 54 | */ |
| 55 | ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { | 55 | ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { |
| 56 | Event* evt = g_handle_table.Get<Event>(handle); | 56 | Event* evt = g_handle_table.Get<Event>(handle).get(); |
| 57 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 57 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 58 | 58 | ||
| 59 | evt->permanent_locked = permanent_locked; | 59 | evt->permanent_locked = permanent_locked; |
| @@ -67,7 +67,7 @@ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { | |||
| 67 | * @return Result of operation, 0 on success, otherwise error code | 67 | * @return Result of operation, 0 on success, otherwise error code |
| 68 | */ | 68 | */ |
| 69 | ResultCode SetEventLocked(const Handle handle, const bool locked) { | 69 | ResultCode SetEventLocked(const Handle handle, const bool locked) { |
| 70 | Event* evt = g_handle_table.Get<Event>(handle); | 70 | Event* evt = g_handle_table.Get<Event>(handle).get(); |
| 71 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 71 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 72 | 72 | ||
| 73 | if (!evt->permanent_locked) { | 73 | if (!evt->permanent_locked) { |
| @@ -82,13 +82,15 @@ ResultCode SetEventLocked(const Handle handle, const bool locked) { | |||
| 82 | * @return Result of operation, 0 on success, otherwise error code | 82 | * @return Result of operation, 0 on success, otherwise error code |
| 83 | */ | 83 | */ |
| 84 | ResultCode SignalEvent(const Handle handle) { | 84 | ResultCode SignalEvent(const Handle handle) { |
| 85 | Event* evt = g_handle_table.Get<Event>(handle); | 85 | Event* evt = g_handle_table.Get<Event>(handle).get(); |
| 86 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 86 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 87 | 87 | ||
| 88 | // Resume threads waiting for event to signal | 88 | // Resume threads waiting for event to signal |
| 89 | bool event_caught = false; | 89 | bool event_caught = false; |
| 90 | for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { | 90 | for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { |
| 91 | ResumeThreadFromWait( evt->waiting_threads[i]); | 91 | Thread* thread = Kernel::g_handle_table.Get<Thread>(evt->waiting_threads[i]).get(); |
| 92 | if (thread != nullptr) | ||
| 93 | thread->ResumeFromWait(); | ||
| 92 | 94 | ||
| 93 | // If any thread is signalled awake by this event, assume the event was "caught" and reset | 95 | // If any thread is signalled awake by this event, assume the event was "caught" and reset |
| 94 | // the event. This will result in the next thread waiting on the event to block. Otherwise, | 96 | // the event. This will result in the next thread waiting on the event to block. Otherwise, |
| @@ -110,7 +112,7 @@ ResultCode SignalEvent(const Handle handle) { | |||
| 110 | * @return Result of operation, 0 on success, otherwise error code | 112 | * @return Result of operation, 0 on success, otherwise error code |
| 111 | */ | 113 | */ |
| 112 | ResultCode ClearEvent(Handle handle) { | 114 | ResultCode ClearEvent(Handle handle) { |
| 113 | Event* evt = g_handle_table.Get<Event>(handle); | 115 | Event* evt = g_handle_table.Get<Event>(handle).get(); |
| 114 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); | 116 | if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 115 | 117 | ||
| 116 | if (!evt->permanent_locked) { | 118 | if (!evt->permanent_locked) { |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e59ed1b57..d3684896f 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -6,13 +6,15 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common.h" | 7 | #include "common/common.h" |
| 8 | 8 | ||
| 9 | #include "core/arm/arm_interface.h" | ||
| 9 | #include "core/core.h" | 10 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 13 | #include "core/hle/kernel/timer.h" | ||
| 12 | 14 | ||
| 13 | namespace Kernel { | 15 | namespace Kernel { |
| 14 | 16 | ||
| 15 | Handle g_main_thread = 0; | 17 | SharedPtr<Thread> g_main_thread = nullptr; |
| 16 | HandleTable g_handle_table; | 18 | HandleTable g_handle_table; |
| 17 | u64 g_program_id = 0; | 19 | u64 g_program_id = 0; |
| 18 | 20 | ||
| @@ -21,7 +23,7 @@ HandleTable::HandleTable() { | |||
| 21 | Clear(); | 23 | Clear(); |
| 22 | } | 24 | } |
| 23 | 25 | ||
| 24 | ResultVal<Handle> HandleTable::Create(Object* obj) { | 26 | ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { |
| 25 | _dbg_assert_(Kernel, obj != nullptr); | 27 | _dbg_assert_(Kernel, obj != nullptr); |
| 26 | 28 | ||
| 27 | u16 slot = next_free_slot; | 29 | u16 slot = next_free_slot; |
| @@ -37,22 +39,23 @@ ResultVal<Handle> HandleTable::Create(Object* obj) { | |||
| 37 | // CTR-OS doesn't use generation 0, so skip straight to 1. | 39 | // CTR-OS doesn't use generation 0, so skip straight to 1. |
| 38 | if (next_generation >= (1 << 15)) next_generation = 1; | 40 | if (next_generation >= (1 << 15)) next_generation = 1; |
| 39 | 41 | ||
| 42 | Handle handle = generation | (slot << 15); | ||
| 43 | if (obj->handle == INVALID_HANDLE) | ||
| 44 | obj->handle = handle; | ||
| 45 | |||
| 40 | generations[slot] = generation; | 46 | generations[slot] = generation; |
| 41 | intrusive_ptr_add_ref(obj); | 47 | objects[slot] = std::move(obj); |
| 42 | objects[slot] = obj; | ||
| 43 | 48 | ||
| 44 | Handle handle = generation | (slot << 15); | ||
| 45 | obj->handle = handle; | ||
| 46 | return MakeResult<Handle>(handle); | 49 | return MakeResult<Handle>(handle); |
| 47 | } | 50 | } |
| 48 | 51 | ||
| 49 | ResultVal<Handle> HandleTable::Duplicate(Handle handle) { | 52 | ResultVal<Handle> HandleTable::Duplicate(Handle handle) { |
| 50 | Object* object = GetGeneric(handle); | 53 | SharedPtr<Object> object = GetGeneric(handle); |
| 51 | if (object == nullptr) { | 54 | if (object == nullptr) { |
| 52 | LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); | 55 | LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); |
| 53 | return ERR_INVALID_HANDLE; | 56 | return ERR_INVALID_HANDLE; |
| 54 | } | 57 | } |
| 55 | return Create(object); | 58 | return Create(std::move(object)); |
| 56 | } | 59 | } |
| 57 | 60 | ||
| 58 | ResultCode HandleTable::Close(Handle handle) { | 61 | ResultCode HandleTable::Close(Handle handle) { |
| @@ -62,7 +65,6 @@ ResultCode HandleTable::Close(Handle handle) { | |||
| 62 | size_t slot = GetSlot(handle); | 65 | size_t slot = GetSlot(handle); |
| 63 | u16 generation = GetGeneration(handle); | 66 | u16 generation = GetGeneration(handle); |
| 64 | 67 | ||
| 65 | intrusive_ptr_release(objects[slot]); | ||
| 66 | objects[slot] = nullptr; | 68 | objects[slot] = nullptr; |
| 67 | 69 | ||
| 68 | generations[generation] = next_free_slot; | 70 | generations[generation] = next_free_slot; |
| @@ -77,10 +79,9 @@ bool HandleTable::IsValid(Handle handle) const { | |||
| 77 | return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; | 79 | return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; |
| 78 | } | 80 | } |
| 79 | 81 | ||
| 80 | Object* HandleTable::GetGeneric(Handle handle) const { | 82 | SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { |
| 81 | if (handle == CurrentThread) { | 83 | if (handle == CurrentThread) { |
| 82 | // TODO(yuriks) Directly return the pointer once this is possible. | 84 | return GetCurrentThread(); |
| 83 | handle = GetCurrentThreadHandle(); | ||
| 84 | } else if (handle == CurrentProcess) { | 85 | } else if (handle == CurrentProcess) { |
| 85 | LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess); | 86 | LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess); |
| 86 | return nullptr; | 87 | return nullptr; |
| @@ -95,8 +96,6 @@ Object* HandleTable::GetGeneric(Handle handle) const { | |||
| 95 | void HandleTable::Clear() { | 96 | void HandleTable::Clear() { |
| 96 | for (size_t i = 0; i < MAX_COUNT; ++i) { | 97 | for (size_t i = 0; i < MAX_COUNT; ++i) { |
| 97 | generations[i] = i + 1; | 98 | generations[i] = i + 1; |
| 98 | if (objects[i] != nullptr) | ||
| 99 | intrusive_ptr_release(objects[i]); | ||
| 100 | objects[i] = nullptr; | 99 | objects[i] = nullptr; |
| 101 | } | 100 | } |
| 102 | next_free_slot = 0; | 101 | next_free_slot = 0; |
| @@ -105,12 +104,13 @@ void HandleTable::Clear() { | |||
| 105 | /// Initialize the kernel | 104 | /// Initialize the kernel |
| 106 | void Init() { | 105 | void Init() { |
| 107 | Kernel::ThreadingInit(); | 106 | Kernel::ThreadingInit(); |
| 107 | Kernel::TimersInit(); | ||
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | /// Shutdown the kernel | 110 | /// Shutdown the kernel |
| 111 | void Shutdown() { | 111 | void Shutdown() { |
| 112 | Kernel::ThreadingShutdown(); | 112 | Kernel::ThreadingShutdown(); |
| 113 | 113 | Kernel::TimersShutdown(); | |
| 114 | g_handle_table.Clear(); // Free all kernel objects | 114 | g_handle_table.Clear(); // Free all kernel objects |
| 115 | } | 115 | } |
| 116 | 116 | ||
| @@ -123,7 +123,9 @@ bool LoadExec(u32 entry_point) { | |||
| 123 | Core::g_app_core->SetPC(entry_point); | 123 | Core::g_app_core->SetPC(entry_point); |
| 124 | 124 | ||
| 125 | // 0x30 is the typical main thread priority I've seen used so far | 125 | // 0x30 is the typical main thread priority I've seen used so far |
| 126 | g_main_thread = Kernel::SetupMainThread(0x30); | 126 | g_main_thread = Kernel::SetupMainThread(0x30, Kernel::DEFAULT_STACK_SIZE); |
| 127 | // Setup the idle thread | ||
| 128 | Kernel::SetupIdleThread(); | ||
| 127 | 129 | ||
| 128 | return true; | 130 | return true; |
| 129 | } | 131 | } |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 7f86fd07d..5e5217b78 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <boost/intrusive_ptr.hpp> | ||
| 8 | |||
| 7 | #include <array> | 9 | #include <array> |
| 8 | #include <string> | 10 | #include <string> |
| 9 | #include "common/common.h" | 11 | #include "common/common.h" |
| @@ -16,6 +18,8 @@ const Handle INVALID_HANDLE = 0; | |||
| 16 | 18 | ||
| 17 | namespace Kernel { | 19 | namespace Kernel { |
| 18 | 20 | ||
| 21 | class Thread; | ||
| 22 | |||
| 19 | // TODO: Verify code | 23 | // TODO: Verify code |
| 20 | const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel, | 24 | const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel, |
| 21 | ErrorSummary::OutOfResource, ErrorLevel::Temporary); | 25 | ErrorSummary::OutOfResource, ErrorLevel::Temporary); |
| @@ -39,6 +43,7 @@ enum class HandleType : u32 { | |||
| 39 | Process = 8, | 43 | Process = 8, |
| 40 | AddressArbiter = 9, | 44 | AddressArbiter = 9, |
| 41 | Semaphore = 10, | 45 | Semaphore = 10, |
| 46 | Timer = 11 | ||
| 42 | }; | 47 | }; |
| 43 | 48 | ||
| 44 | enum { | 49 | enum { |
| @@ -49,7 +54,7 @@ class HandleTable; | |||
| 49 | 54 | ||
| 50 | class Object : NonCopyable { | 55 | class Object : NonCopyable { |
| 51 | friend class HandleTable; | 56 | friend class HandleTable; |
| 52 | u32 handle; | 57 | u32 handle = INVALID_HANDLE; |
| 53 | public: | 58 | public: |
| 54 | virtual ~Object() {} | 59 | virtual ~Object() {} |
| 55 | Handle GetHandle() const { return handle; } | 60 | Handle GetHandle() const { return handle; } |
| @@ -73,7 +78,7 @@ private: | |||
| 73 | unsigned int ref_count = 0; | 78 | unsigned int ref_count = 0; |
| 74 | }; | 79 | }; |
| 75 | 80 | ||
| 76 | // Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting | 81 | // Special functions used by boost::instrusive_ptr to do automatic ref-counting |
| 77 | inline void intrusive_ptr_add_ref(Object* object) { | 82 | inline void intrusive_ptr_add_ref(Object* object) { |
| 78 | ++object->ref_count; | 83 | ++object->ref_count; |
| 79 | } | 84 | } |
| @@ -84,6 +89,9 @@ inline void intrusive_ptr_release(Object* object) { | |||
| 84 | } | 89 | } |
| 85 | } | 90 | } |
| 86 | 91 | ||
| 92 | template <typename T> | ||
| 93 | using SharedPtr = boost::intrusive_ptr<T>; | ||
| 94 | |||
| 87 | /** | 95 | /** |
| 88 | * This class allows the creation of Handles, which are references to objects that can be tested | 96 | * This class allows the creation of Handles, which are references to objects that can be tested |
| 89 | * for validity and looked up. Here they are used to pass references to kernel objects to/from the | 97 | * for validity and looked up. Here they are used to pass references to kernel objects to/from the |
| @@ -116,7 +124,7 @@ public: | |||
| 116 | * @return The created Handle or one of the following errors: | 124 | * @return The created Handle or one of the following errors: |
| 117 | * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. | 125 | * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. |
| 118 | */ | 126 | */ |
| 119 | ResultVal<Handle> Create(Object* obj); | 127 | ResultVal<Handle> Create(SharedPtr<Object> obj); |
| 120 | 128 | ||
| 121 | /** | 129 | /** |
| 122 | * Returns a new handle that points to the same object as the passed in handle. | 130 | * Returns a new handle that points to the same object as the passed in handle. |
| @@ -140,7 +148,7 @@ public: | |||
| 140 | * Looks up a handle. | 148 | * Looks up a handle. |
| 141 | * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid. | 149 | * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid. |
| 142 | */ | 150 | */ |
| 143 | Object* GetGeneric(Handle handle) const; | 151 | SharedPtr<Object> GetGeneric(Handle handle) const; |
| 144 | 152 | ||
| 145 | /** | 153 | /** |
| 146 | * Looks up a handle while verifying its type. | 154 | * Looks up a handle while verifying its type. |
| @@ -148,10 +156,10 @@ public: | |||
| 148 | * type differs from the handle type `T::HANDLE_TYPE`. | 156 | * type differs from the handle type `T::HANDLE_TYPE`. |
| 149 | */ | 157 | */ |
| 150 | template <class T> | 158 | template <class T> |
| 151 | T* Get(Handle handle) const { | 159 | SharedPtr<T> Get(Handle handle) const { |
| 152 | Object* object = GetGeneric(handle); | 160 | SharedPtr<Object> object = GetGeneric(handle); |
| 153 | if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { | 161 | if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { |
| 154 | return static_cast<T*>(object); | 162 | return boost::static_pointer_cast<T>(std::move(object)); |
| 155 | } | 163 | } |
| 156 | return nullptr; | 164 | return nullptr; |
| 157 | } | 165 | } |
| @@ -170,7 +178,7 @@ private: | |||
| 170 | static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; } | 178 | static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; } |
| 171 | 179 | ||
| 172 | /// Stores the Object referenced by the handle or null if the slot is empty. | 180 | /// Stores the Object referenced by the handle or null if the slot is empty. |
| 173 | std::array<Object*, MAX_COUNT> objects; | 181 | std::array<SharedPtr<Object>, MAX_COUNT> objects; |
| 174 | 182 | ||
| 175 | /** | 183 | /** |
| 176 | * The value of `next_generation` when the handle was created, used to check for validity. For | 184 | * The value of `next_generation` when the handle was created, used to check for validity. For |
| @@ -189,7 +197,7 @@ private: | |||
| 189 | }; | 197 | }; |
| 190 | 198 | ||
| 191 | extern HandleTable g_handle_table; | 199 | extern HandleTable g_handle_table; |
| 192 | extern Handle g_main_thread; | 200 | extern SharedPtr<Thread> g_main_thread; |
| 193 | 201 | ||
| 194 | /// The ID code of the currently running game | 202 | /// The ID code of the currently running game |
| 195 | /// TODO(Subv): This variable should not be here, | 203 | /// TODO(Subv): This variable should not be here, |
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 3dfeffc9b..853a5dd74 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -40,14 +40,21 @@ static MutexMap g_mutex_held_locks; | |||
| 40 | * @param mutex Mutex that is to be acquired | 40 | * @param mutex Mutex that is to be acquired |
| 41 | * @param thread Thread that will acquired | 41 | * @param thread Thread that will acquired |
| 42 | */ | 42 | */ |
| 43 | void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) { | 43 | void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) { |
| 44 | g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); | 44 | g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); |
| 45 | mutex->lock_thread = thread; | 45 | mutex->lock_thread = thread; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { | 48 | bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) { |
| 49 | MutexAcquireLock(mutex, thread); | 49 | MutexAcquireLock(mutex, thread_handle); |
| 50 | Kernel::ResumeThreadFromWait(thread); | 50 | |
| 51 | Thread* thread = Kernel::g_handle_table.Get<Thread>(thread_handle).get(); | ||
| 52 | if (thread == nullptr) { | ||
| 53 | LOG_ERROR(Kernel, "Called with invalid handle: %08X", thread_handle); | ||
| 54 | return false; | ||
| 55 | } | ||
| 56 | |||
| 57 | thread->ResumeFromWait(); | ||
| 51 | return true; | 58 | return true; |
| 52 | } | 59 | } |
| 53 | 60 | ||
| @@ -87,7 +94,7 @@ void ReleaseThreadMutexes(Handle thread) { | |||
| 87 | 94 | ||
| 88 | // Release every mutex that the thread holds, and resume execution on the waiting threads | 95 | // Release every mutex that the thread holds, and resume execution on the waiting threads |
| 89 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { | 96 | for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { |
| 90 | Mutex* mutex = g_handle_table.Get<Mutex>(iter->second); | 97 | Mutex* mutex = g_handle_table.Get<Mutex>(iter->second).get(); |
| 91 | ResumeWaitingThread(mutex); | 98 | ResumeWaitingThread(mutex); |
| 92 | } | 99 | } |
| 93 | 100 | ||
| @@ -115,7 +122,7 @@ bool ReleaseMutex(Mutex* mutex) { | |||
| 115 | * @param handle Handle to mutex to release | 122 | * @param handle Handle to mutex to release |
| 116 | */ | 123 | */ |
| 117 | ResultCode ReleaseMutex(Handle handle) { | 124 | ResultCode ReleaseMutex(Handle handle) { |
| 118 | Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle); | 125 | Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle).get(); |
| 119 | if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); | 126 | if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 120 | 127 | ||
| 121 | if (!ReleaseMutex(mutex)) { | 128 | if (!ReleaseMutex(mutex)) { |
| @@ -168,8 +175,8 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { | |||
| 168 | ResultVal<bool> Mutex::WaitSynchronization() { | 175 | ResultVal<bool> Mutex::WaitSynchronization() { |
| 169 | bool wait = locked; | 176 | bool wait = locked; |
| 170 | if (locked) { | 177 | if (locked) { |
| 171 | waiting_threads.push_back(GetCurrentThreadHandle()); | 178 | waiting_threads.push_back(GetCurrentThread()->GetHandle()); |
| 172 | Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); | 179 | Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this); |
| 173 | } else { | 180 | } else { |
| 174 | // Lock the mutex when the first thread accesses it | 181 | // Lock the mutex when the first thread accesses it |
| 175 | locked = true; | 182 | locked = true; |
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 6bc8066a6..88ec9a104 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp | |||
| @@ -37,8 +37,8 @@ public: | |||
| 37 | bool wait = !IsAvailable(); | 37 | bool wait = !IsAvailable(); |
| 38 | 38 | ||
| 39 | if (wait) { | 39 | if (wait) { |
| 40 | Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle()); | 40 | Kernel::WaitCurrentThread(WAITTYPE_SEMA, this); |
| 41 | waiting_threads.push(GetCurrentThreadHandle()); | 41 | waiting_threads.push(GetCurrentThread()->GetHandle()); |
| 42 | } else { | 42 | } else { |
| 43 | --available_count; | 43 | --available_count; |
| 44 | } | 44 | } |
| @@ -70,7 +70,7 @@ ResultCode CreateSemaphore(Handle* handle, s32 initial_count, | |||
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { | 72 | ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { |
| 73 | Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle); | 73 | Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle).get(); |
| 74 | if (semaphore == nullptr) | 74 | if (semaphore == nullptr) |
| 75 | return InvalidHandle(ErrorModule::Kernel); | 75 | return InvalidHandle(ErrorModule::Kernel); |
| 76 | 76 | ||
| @@ -84,7 +84,9 @@ ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { | |||
| 84 | // Notify some of the threads that the semaphore has been released | 84 | // Notify some of the threads that the semaphore has been released |
| 85 | // stop once the semaphore is full again or there are no more waiting threads | 85 | // stop once the semaphore is full again or there are no more waiting threads |
| 86 | while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { | 86 | while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { |
| 87 | Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front()); | 87 | Thread* thread = Kernel::g_handle_table.Get<Thread>(semaphore->waiting_threads.front()).get(); |
| 88 | if (thread != nullptr) | ||
| 89 | thread->ResumeFromWait(); | ||
| 88 | semaphore->waiting_threads.pop(); | 90 | semaphore->waiting_threads.pop(); |
| 89 | --semaphore->available_count; | 91 | --semaphore->available_count; |
| 90 | } | 92 | } |
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index cea1f6fa1..5368e4728 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp | |||
| @@ -61,7 +61,7 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions | |||
| 61 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | 61 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, |
| 62 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | 62 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); |
| 63 | } | 63 | } |
| 64 | SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); | 64 | SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get(); |
| 65 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); | 65 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 66 | 66 | ||
| 67 | shared_memory->base_address = address; | 67 | shared_memory->base_address = address; |
| @@ -72,7 +72,7 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions | |||
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { | 74 | ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { |
| 75 | SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); | 75 | SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get(); |
| 76 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); | 76 | if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); |
| 77 | 77 | ||
| 78 | if (0 != shared_memory->base_address) | 78 | if (0 != shared_memory->base_address) |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 872df2d14..bc86a7c59 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -10,7 +10,9 @@ | |||
| 10 | #include "common/common.h" | 10 | #include "common/common.h" |
| 11 | #include "common/thread_queue_list.h" | 11 | #include "common/thread_queue_list.h" |
| 12 | 12 | ||
| 13 | #include "core/arm/arm_interface.h" | ||
| 13 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/core_timing.h" | ||
| 14 | #include "core/hle/hle.h" | 16 | #include "core/hle/hle.h" |
| 15 | #include "core/hle/kernel/kernel.h" | 17 | #include "core/hle/kernel/kernel.h" |
| 16 | #include "core/hle/kernel/thread.h" | 18 | #include "core/hle/kernel/thread.h" |
| @@ -20,64 +22,25 @@ | |||
| 20 | 22 | ||
| 21 | namespace Kernel { | 23 | namespace Kernel { |
| 22 | 24 | ||
| 23 | class Thread : public Kernel::Object { | 25 | ResultVal<bool> Thread::WaitSynchronization() { |
| 24 | public: | 26 | const bool wait = status != THREADSTATUS_DORMANT; |
| 25 | 27 | if (wait) { | |
| 26 | std::string GetName() const override { return name; } | 28 | Thread* thread = GetCurrentThread(); |
| 27 | std::string GetTypeName() const override { return "Thread"; } | 29 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { |
| 28 | 30 | waiting_threads.push_back(thread); | |
| 29 | static const HandleType HANDLE_TYPE = HandleType::Thread; | ||
| 30 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | ||
| 31 | |||
| 32 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } | ||
| 33 | inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } | ||
| 34 | inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; } | ||
| 35 | inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } | ||
| 36 | inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } | ||
| 37 | |||
| 38 | ResultVal<bool> WaitSynchronization() override { | ||
| 39 | const bool wait = status != THREADSTATUS_DORMANT; | ||
| 40 | if (wait) { | ||
| 41 | Handle thread = GetCurrentThreadHandle(); | ||
| 42 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { | ||
| 43 | waiting_threads.push_back(thread); | ||
| 44 | } | ||
| 45 | WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle()); | ||
| 46 | } | 31 | } |
| 47 | 32 | WaitCurrentThread(WAITTYPE_THREADEND, this); | |
| 48 | return MakeResult<bool>(wait); | ||
| 49 | } | 33 | } |
| 50 | 34 | ||
| 51 | ThreadContext context; | 35 | return MakeResult<bool>(wait); |
| 52 | 36 | } | |
| 53 | u32 thread_id; | ||
| 54 | |||
| 55 | u32 status; | ||
| 56 | u32 entry_point; | ||
| 57 | u32 stack_top; | ||
| 58 | u32 stack_size; | ||
| 59 | |||
| 60 | s32 initial_priority; | ||
| 61 | s32 current_priority; | ||
| 62 | |||
| 63 | s32 processor_id; | ||
| 64 | |||
| 65 | WaitType wait_type; | ||
| 66 | Handle wait_handle; | ||
| 67 | VAddr wait_address; | ||
| 68 | |||
| 69 | std::vector<Handle> waiting_threads; | ||
| 70 | |||
| 71 | std::string name; | ||
| 72 | }; | ||
| 73 | 37 | ||
| 74 | // Lists all thread ids that aren't deleted/etc. | 38 | // Lists all thread ids that aren't deleted/etc. |
| 75 | static std::vector<Handle> thread_queue; | 39 | static std::vector<SharedPtr<Thread>> thread_list; |
| 76 | 40 | ||
| 77 | // Lists only ready thread ids. | 41 | // Lists only ready thread ids. |
| 78 | static Common::ThreadQueueList<Handle> thread_ready_queue; | 42 | static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> thread_ready_queue; |
| 79 | 43 | ||
| 80 | static Handle current_thread_handle; | ||
| 81 | static Thread* current_thread; | 44 | static Thread* current_thread; |
| 82 | 45 | ||
| 83 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup | 46 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup |
| @@ -87,30 +50,9 @@ Thread* GetCurrentThread() { | |||
| 87 | return current_thread; | 50 | return current_thread; |
| 88 | } | 51 | } |
| 89 | 52 | ||
| 90 | /// Gets the current thread handle | ||
| 91 | Handle GetCurrentThreadHandle() { | ||
| 92 | return GetCurrentThread()->GetHandle(); | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Sets the current thread | ||
| 96 | inline void SetCurrentThread(Thread* t) { | ||
| 97 | current_thread = t; | ||
| 98 | current_thread_handle = t->GetHandle(); | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Saves the current CPU context | ||
| 102 | void SaveContext(ThreadContext& ctx) { | ||
| 103 | Core::g_app_core->SaveContext(ctx); | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Loads a CPU context | ||
| 107 | void LoadContext(ThreadContext& ctx) { | ||
| 108 | Core::g_app_core->LoadContext(ctx); | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Resets a thread | 53 | /// Resets a thread |
| 112 | void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | 54 | static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { |
| 113 | memset(&t->context, 0, sizeof(ThreadContext)); | 55 | memset(&t->context, 0, sizeof(Core::ThreadContext)); |
| 114 | 56 | ||
| 115 | t->context.cpu_registers[0] = arg; | 57 | t->context.cpu_registers[0] = arg; |
| 116 | t->context.pc = t->context.reg_15 = t->entry_point; | 58 | t->context.pc = t->context.reg_15 = t->entry_point; |
| @@ -126,22 +68,21 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | |||
| 126 | t->current_priority = t->initial_priority; | 68 | t->current_priority = t->initial_priority; |
| 127 | } | 69 | } |
| 128 | t->wait_type = WAITTYPE_NONE; | 70 | t->wait_type = WAITTYPE_NONE; |
| 129 | t->wait_handle = 0; | 71 | t->wait_object = nullptr; |
| 130 | t->wait_address = 0; | 72 | t->wait_address = 0; |
| 131 | } | 73 | } |
| 132 | 74 | ||
| 133 | /// Change a thread to "ready" state | 75 | /// Change a thread to "ready" state |
| 134 | void ChangeReadyState(Thread* t, bool ready) { | 76 | static void ChangeReadyState(Thread* t, bool ready) { |
| 135 | Handle handle = t->GetHandle(); | ||
| 136 | if (t->IsReady()) { | 77 | if (t->IsReady()) { |
| 137 | if (!ready) { | 78 | if (!ready) { |
| 138 | thread_ready_queue.remove(t->current_priority, handle); | 79 | thread_ready_queue.remove(t->current_priority, t); |
| 139 | } | 80 | } |
| 140 | } else if (ready) { | 81 | } else if (ready) { |
| 141 | if (t->IsRunning()) { | 82 | if (t->IsRunning()) { |
| 142 | thread_ready_queue.push_front(t->current_priority, handle); | 83 | thread_ready_queue.push_front(t->current_priority, t); |
| 143 | } else { | 84 | } else { |
| 144 | thread_ready_queue.push_back(t->current_priority, handle); | 85 | thread_ready_queue.push_back(t->current_priority, t); |
| 145 | } | 86 | } |
| 146 | t->status = THREADSTATUS_READY; | 87 | t->status = THREADSTATUS_READY; |
| 147 | } | 88 | } |
| @@ -153,43 +94,36 @@ static bool CheckWaitType(const Thread* thread, WaitType type) { | |||
| 153 | } | 94 | } |
| 154 | 95 | ||
| 155 | /// Check if a thread is blocking on a specified wait type with a specified handle | 96 | /// Check if a thread is blocking on a specified wait type with a specified handle |
| 156 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) { | 97 | static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) { |
| 157 | return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle); | 98 | return CheckWaitType(thread, type) && wait_object == thread->wait_object; |
| 158 | } | 99 | } |
| 159 | 100 | ||
| 160 | /// Check if a thread is blocking on a specified wait type with a specified handle and address | 101 | /// Check if a thread is blocking on a specified wait type with a specified handle and address |
| 161 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { | 102 | static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) { |
| 162 | return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address); | 103 | return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address); |
| 163 | } | 104 | } |
| 164 | 105 | ||
| 165 | /// Stops the current thread | 106 | /// Stops the current thread |
| 166 | ResultCode StopThread(Handle handle, const char* reason) { | 107 | void Thread::Stop(const char* reason) { |
| 167 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 168 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); | ||
| 169 | |||
| 170 | // Release all the mutexes that this thread holds | 108 | // Release all the mutexes that this thread holds |
| 171 | ReleaseThreadMutexes(handle); | 109 | ReleaseThreadMutexes(GetHandle()); |
| 172 | 110 | ||
| 173 | ChangeReadyState(thread, false); | 111 | ChangeReadyState(this, false); |
| 174 | thread->status = THREADSTATUS_DORMANT; | 112 | status = THREADSTATUS_DORMANT; |
| 175 | for (Handle waiting_handle : thread->waiting_threads) { | 113 | for (auto& waiting_thread : waiting_threads) { |
| 176 | Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle); | 114 | if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this)) |
| 177 | 115 | waiting_thread->ResumeFromWait(); | |
| 178 | if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle)) | ||
| 179 | ResumeThreadFromWait(waiting_handle); | ||
| 180 | } | 116 | } |
| 181 | thread->waiting_threads.clear(); | 117 | waiting_threads.clear(); |
| 182 | 118 | ||
| 183 | // Stopped threads are never waiting. | 119 | // Stopped threads are never waiting. |
| 184 | thread->wait_type = WAITTYPE_NONE; | 120 | wait_type = WAITTYPE_NONE; |
| 185 | thread->wait_handle = 0; | 121 | wait_object = nullptr; |
| 186 | thread->wait_address = 0; | 122 | wait_address = 0; |
| 187 | |||
| 188 | return RESULT_SUCCESS; | ||
| 189 | } | 123 | } |
| 190 | 124 | ||
| 191 | /// Changes a threads state | 125 | /// Changes a threads state |
| 192 | void ChangeThreadState(Thread* t, ThreadStatus new_status) { | 126 | static void ChangeThreadState(Thread* t, ThreadStatus new_status) { |
| 193 | if (!t || t->status == new_status) { | 127 | if (!t || t->status == new_status) { |
| 194 | return; | 128 | return; |
| 195 | } | 129 | } |
| @@ -204,46 +138,44 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) { | |||
| 204 | } | 138 | } |
| 205 | 139 | ||
| 206 | /// Arbitrate the highest priority thread that is waiting | 140 | /// Arbitrate the highest priority thread that is waiting |
| 207 | Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { | 141 | Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { |
| 208 | Handle highest_priority_thread = 0; | 142 | Thread* highest_priority_thread = nullptr; |
| 209 | s32 priority = THREADPRIO_LOWEST; | 143 | s32 priority = THREADPRIO_LOWEST; |
| 210 | 144 | ||
| 211 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 145 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 212 | for (Handle handle : thread_queue) { | 146 | for (auto& thread : thread_list) { |
| 213 | Thread* thread = g_handle_table.Get<Thread>(handle); | 147 | if (!CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) |
| 214 | |||
| 215 | if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) | ||
| 216 | continue; | 148 | continue; |
| 217 | 149 | ||
| 218 | if (thread == nullptr) | 150 | if (thread == nullptr) |
| 219 | continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. | 151 | continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. |
| 220 | 152 | ||
| 221 | if(thread->current_priority <= priority) { | 153 | if(thread->current_priority <= priority) { |
| 222 | highest_priority_thread = handle; | 154 | highest_priority_thread = thread.get(); |
| 223 | priority = thread->current_priority; | 155 | priority = thread->current_priority; |
| 224 | } | 156 | } |
| 225 | } | 157 | } |
| 158 | |||
| 226 | // If a thread was arbitrated, resume it | 159 | // If a thread was arbitrated, resume it |
| 227 | if (0 != highest_priority_thread) | 160 | if (nullptr != highest_priority_thread) { |
| 228 | ResumeThreadFromWait(highest_priority_thread); | 161 | highest_priority_thread->ResumeFromWait(); |
| 162 | } | ||
| 229 | 163 | ||
| 230 | return highest_priority_thread; | 164 | return highest_priority_thread; |
| 231 | } | 165 | } |
| 232 | 166 | ||
| 233 | /// Arbitrate all threads currently waiting | 167 | /// Arbitrate all threads currently waiting |
| 234 | void ArbitrateAllThreads(u32 arbiter, u32 address) { | 168 | void ArbitrateAllThreads(Object* arbiter, u32 address) { |
| 235 | 169 | ||
| 236 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 170 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 237 | for (Handle handle : thread_queue) { | 171 | for (auto& thread : thread_list) { |
| 238 | Thread* thread = g_handle_table.Get<Thread>(handle); | 172 | if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) |
| 239 | 173 | thread->ResumeFromWait(); | |
| 240 | if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) | ||
| 241 | ResumeThreadFromWait(handle); | ||
| 242 | } | 174 | } |
| 243 | } | 175 | } |
| 244 | 176 | ||
| 245 | /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) | 177 | /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) |
| 246 | void CallThread(Thread* t) { | 178 | static void CallThread(Thread* t) { |
| 247 | // Stop waiting | 179 | // Stop waiting |
| 248 | if (t->wait_type != WAITTYPE_NONE) { | 180 | if (t->wait_type != WAITTYPE_NONE) { |
| 249 | t->wait_type = WAITTYPE_NONE; | 181 | t->wait_type = WAITTYPE_NONE; |
| @@ -252,12 +184,12 @@ void CallThread(Thread* t) { | |||
| 252 | } | 184 | } |
| 253 | 185 | ||
| 254 | /// Switches CPU context to that of the specified thread | 186 | /// Switches CPU context to that of the specified thread |
| 255 | void SwitchContext(Thread* t) { | 187 | static void SwitchContext(Thread* t) { |
| 256 | Thread* cur = GetCurrentThread(); | 188 | Thread* cur = GetCurrentThread(); |
| 257 | 189 | ||
| 258 | // Save context for current thread | 190 | // Save context for current thread |
| 259 | if (cur) { | 191 | if (cur) { |
| 260 | SaveContext(cur->context); | 192 | Core::g_app_core->SaveContext(cur->context); |
| 261 | 193 | ||
| 262 | if (cur->IsRunning()) { | 194 | if (cur->IsRunning()) { |
| 263 | ChangeReadyState(cur, true); | 195 | ChangeReadyState(cur, true); |
| @@ -265,19 +197,19 @@ void SwitchContext(Thread* t) { | |||
| 265 | } | 197 | } |
| 266 | // Load context of new thread | 198 | // Load context of new thread |
| 267 | if (t) { | 199 | if (t) { |
| 268 | SetCurrentThread(t); | 200 | current_thread = t; |
| 269 | ChangeReadyState(t, false); | 201 | ChangeReadyState(t, false); |
| 270 | t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; | 202 | t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; |
| 271 | t->wait_type = WAITTYPE_NONE; | 203 | t->wait_type = WAITTYPE_NONE; |
| 272 | LoadContext(t->context); | 204 | Core::g_app_core->LoadContext(t->context); |
| 273 | } else { | 205 | } else { |
| 274 | SetCurrentThread(nullptr); | 206 | current_thread = nullptr; |
| 275 | } | 207 | } |
| 276 | } | 208 | } |
| 277 | 209 | ||
| 278 | /// Gets the next thread that is ready to be run by priority | 210 | /// Gets the next thread that is ready to be run by priority |
| 279 | Thread* NextThread() { | 211 | static Thread* NextThread() { |
| 280 | Handle next; | 212 | Thread* next; |
| 281 | Thread* cur = GetCurrentThread(); | 213 | Thread* cur = GetCurrentThread(); |
| 282 | 214 | ||
| 283 | if (cur && cur->IsRunning()) { | 215 | if (cur && cur->IsRunning()) { |
| @@ -288,63 +220,111 @@ Thread* NextThread() { | |||
| 288 | if (next == 0) { | 220 | if (next == 0) { |
| 289 | return nullptr; | 221 | return nullptr; |
| 290 | } | 222 | } |
| 291 | return Kernel::g_handle_table.Get<Thread>(next); | 223 | return next; |
| 292 | } | 224 | } |
| 293 | 225 | ||
| 294 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { | 226 | void WaitCurrentThread(WaitType wait_type, Object* wait_object) { |
| 295 | Thread* thread = GetCurrentThread(); | 227 | Thread* thread = GetCurrentThread(); |
| 296 | thread->wait_type = wait_type; | 228 | thread->wait_type = wait_type; |
| 297 | thread->wait_handle = wait_handle; | 229 | thread->wait_object = wait_object; |
| 298 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | 230 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); |
| 299 | } | 231 | } |
| 300 | 232 | ||
| 301 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) { | 233 | void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) { |
| 302 | WaitCurrentThread(wait_type, wait_handle); | 234 | WaitCurrentThread(wait_type, wait_object); |
| 303 | GetCurrentThread()->wait_address = wait_address; | 235 | GetCurrentThread()->wait_address = wait_address; |
| 304 | } | 236 | } |
| 305 | 237 | ||
| 238 | /// Event type for the thread wake up event | ||
| 239 | static int ThreadWakeupEventType = -1; | ||
| 240 | |||
| 241 | /// Callback that will wake up the thread it was scheduled for | ||
| 242 | static void ThreadWakeupCallback(u64 parameter, int cycles_late) { | ||
| 243 | Handle handle = static_cast<Handle>(parameter); | ||
| 244 | SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(handle); | ||
| 245 | if (thread == nullptr) { | ||
| 246 | LOG_ERROR(Kernel, "Thread doesn't exist %u", handle); | ||
| 247 | return; | ||
| 248 | } | ||
| 249 | |||
| 250 | thread->ResumeFromWait(); | ||
| 251 | } | ||
| 252 | |||
| 253 | |||
| 254 | void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { | ||
| 255 | // Don't schedule a wakeup if the thread wants to wait forever | ||
| 256 | if (nanoseconds == -1) | ||
| 257 | return; | ||
| 258 | _dbg_assert_(Kernel, thread != nullptr); | ||
| 259 | |||
| 260 | u64 microseconds = nanoseconds / 1000; | ||
| 261 | CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); | ||
| 262 | } | ||
| 263 | |||
| 306 | /// Resumes a thread from waiting by marking it as "ready" | 264 | /// Resumes a thread from waiting by marking it as "ready" |
| 307 | void ResumeThreadFromWait(Handle handle) { | 265 | void Thread::ResumeFromWait() { |
| 308 | Thread* thread = Kernel::g_handle_table.Get<Thread>(handle); | 266 | // Cancel any outstanding wakeup events |
| 309 | if (thread) { | 267 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); |
| 310 | thread->status &= ~THREADSTATUS_WAIT; | 268 | |
| 311 | thread->wait_handle = 0; | 269 | status &= ~THREADSTATUS_WAIT; |
| 312 | thread->wait_type = WAITTYPE_NONE; | 270 | wait_object = nullptr; |
| 313 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | 271 | wait_type = WAITTYPE_NONE; |
| 314 | ChangeReadyState(thread, true); | 272 | if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { |
| 315 | } | 273 | ChangeReadyState(this, true); |
| 316 | } | 274 | } |
| 317 | } | 275 | } |
| 318 | 276 | ||
| 319 | /// Prints the thread queue for debugging purposes | 277 | /// Prints the thread queue for debugging purposes |
| 320 | void DebugThreadQueue() { | 278 | static void DebugThreadQueue() { |
| 321 | Thread* thread = GetCurrentThread(); | 279 | Thread* thread = GetCurrentThread(); |
| 322 | if (!thread) { | 280 | if (!thread) { |
| 323 | return; | 281 | return; |
| 324 | } | 282 | } |
| 325 | LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); | 283 | LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThread()->GetHandle()); |
| 326 | for (u32 i = 0; i < thread_queue.size(); i++) { | 284 | for (auto& t : thread_list) { |
| 327 | Handle handle = thread_queue[i]; | 285 | s32 priority = thread_ready_queue.contains(t.get()); |
| 328 | s32 priority = thread_ready_queue.contains(handle); | ||
| 329 | if (priority != -1) { | 286 | if (priority != -1) { |
| 330 | LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle); | 287 | LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, t->GetHandle()); |
| 331 | } | 288 | } |
| 332 | } | 289 | } |
| 333 | } | 290 | } |
| 334 | 291 | ||
| 335 | /// Creates a new thread | 292 | ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, |
| 336 | Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, | 293 | u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size) { |
| 337 | s32 processor_id, u32 stack_top, int stack_size) { | 294 | if (stack_size < 0x200) { |
| 295 | LOG_ERROR(Kernel, "(name=%s): invalid stack_size=0x%08X", name.c_str(), stack_size); | ||
| 296 | // TODO: Verify error | ||
| 297 | return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Kernel, | ||
| 298 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 299 | } | ||
| 300 | |||
| 301 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | ||
| 302 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | ||
| 303 | LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", | ||
| 304 | name.c_str(), priority, new_priority); | ||
| 305 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | ||
| 306 | // validity of this | ||
| 307 | priority = new_priority; | ||
| 308 | } | ||
| 338 | 309 | ||
| 339 | _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), | 310 | if (!Memory::GetPointer(entry_point)) { |
| 340 | "priority=%d, outside of allowable range!", priority) | 311 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); |
| 312 | // TODO: Verify error | ||
| 313 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | ||
| 314 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 315 | } | ||
| 341 | 316 | ||
| 342 | Thread* thread = new Thread; | 317 | SharedPtr<Thread> thread(new Thread); |
| 343 | 318 | ||
| 344 | // TOOD(yuriks): Fix error reporting | 319 | // TODO(yuriks): Thread requires a handle to be inserted into the various scheduling queues for |
| 345 | handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE); | 320 | // the time being. Create a handle here, it will be copied to the handle field in |
| 321 | // the object and use by the rest of the code. This should be removed when other | ||
| 322 | // code doesn't rely on the handle anymore. | ||
| 323 | ResultVal<Handle> handle = Kernel::g_handle_table.Create(thread); | ||
| 324 | if (handle.Failed()) | ||
| 325 | return handle.Code(); | ||
| 346 | 326 | ||
| 347 | thread_queue.push_back(handle); | 327 | thread_list.push_back(thread); |
| 348 | thread_ready_queue.prepare(priority); | 328 | thread_ready_queue.prepare(priority); |
| 349 | 329 | ||
| 350 | thread->thread_id = next_thread_id++; | 330 | thread->thread_id = next_thread_id++; |
| @@ -355,69 +335,18 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio | |||
| 355 | thread->initial_priority = thread->current_priority = priority; | 335 | thread->initial_priority = thread->current_priority = priority; |
| 356 | thread->processor_id = processor_id; | 336 | thread->processor_id = processor_id; |
| 357 | thread->wait_type = WAITTYPE_NONE; | 337 | thread->wait_type = WAITTYPE_NONE; |
| 358 | thread->wait_handle = 0; | 338 | thread->wait_object = nullptr; |
| 359 | thread->wait_address = 0; | 339 | thread->wait_address = 0; |
| 360 | thread->name = name; | 340 | thread->name = std::move(name); |
| 361 | |||
| 362 | return thread; | ||
| 363 | } | ||
| 364 | |||
| 365 | /// Creates a new thread - wrapper for external user | ||
| 366 | Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, | ||
| 367 | u32 stack_top, int stack_size) { | ||
| 368 | |||
| 369 | if (name == nullptr) { | ||
| 370 | LOG_ERROR(Kernel_SVC, "nullptr name"); | ||
| 371 | return -1; | ||
| 372 | } | ||
| 373 | if ((u32)stack_size < 0x200) { | ||
| 374 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name, | ||
| 375 | stack_size); | ||
| 376 | return -1; | ||
| 377 | } | ||
| 378 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | ||
| 379 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | ||
| 380 | LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", | ||
| 381 | name, priority, new_priority); | ||
| 382 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | ||
| 383 | // validity of this | ||
| 384 | priority = new_priority; | ||
| 385 | } | ||
| 386 | if (!Memory::GetPointer(entry_point)) { | ||
| 387 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point); | ||
| 388 | return -1; | ||
| 389 | } | ||
| 390 | Handle handle; | ||
| 391 | Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, | ||
| 392 | stack_size); | ||
| 393 | |||
| 394 | ResetThread(thread, arg, 0); | ||
| 395 | CallThread(thread); | ||
| 396 | |||
| 397 | return handle; | ||
| 398 | } | ||
| 399 | 341 | ||
| 400 | /// Get the priority of the thread specified by handle | 342 | ResetThread(thread.get(), arg, 0); |
| 401 | ResultVal<u32> GetThreadPriority(const Handle handle) { | 343 | CallThread(thread.get()); |
| 402 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 403 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); | ||
| 404 | 344 | ||
| 405 | return MakeResult<u32>(thread->current_priority); | 345 | return MakeResult<SharedPtr<Thread>>(std::move(thread)); |
| 406 | } | 346 | } |
| 407 | 347 | ||
| 408 | /// Set the priority of the thread specified by handle | 348 | /// Set the priority of the thread specified by handle |
| 409 | ResultCode SetThreadPriority(Handle handle, s32 priority) { | 349 | void Thread::SetPriority(s32 priority) { |
| 410 | Thread* thread = nullptr; | ||
| 411 | if (!handle) { | ||
| 412 | thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? | ||
| 413 | } else { | ||
| 414 | thread = g_handle_table.Get<Thread>(handle); | ||
| 415 | if (thread == nullptr) { | ||
| 416 | return InvalidHandle(ErrorModule::Kernel); | ||
| 417 | } | ||
| 418 | } | ||
| 419 | _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); | ||
| 420 | |||
| 421 | // If priority is invalid, clamp to valid range | 350 | // If priority is invalid, clamp to valid range |
| 422 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | 351 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { |
| 423 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 352 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); |
| @@ -428,31 +357,39 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) { | |||
| 428 | } | 357 | } |
| 429 | 358 | ||
| 430 | // Change thread priority | 359 | // Change thread priority |
| 431 | s32 old = thread->current_priority; | 360 | s32 old = current_priority; |
| 432 | thread_ready_queue.remove(old, handle); | 361 | thread_ready_queue.remove(old, this); |
| 433 | thread->current_priority = priority; | 362 | current_priority = priority; |
| 434 | thread_ready_queue.prepare(thread->current_priority); | 363 | thread_ready_queue.prepare(current_priority); |
| 435 | 364 | ||
| 436 | // Change thread status to "ready" and push to ready queue | 365 | // Change thread status to "ready" and push to ready queue |
| 437 | if (thread->IsRunning()) { | 366 | if (IsRunning()) { |
| 438 | thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; | 367 | status = (status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; |
| 439 | } | 368 | } |
| 440 | if (thread->IsReady()) { | 369 | if (IsReady()) { |
| 441 | thread_ready_queue.push_back(thread->current_priority, handle); | 370 | thread_ready_queue.push_back(current_priority, this); |
| 442 | } | 371 | } |
| 443 | |||
| 444 | return RESULT_SUCCESS; | ||
| 445 | } | 372 | } |
| 446 | 373 | ||
| 447 | /// Sets up the primary application thread | 374 | Handle SetupIdleThread() { |
| 448 | Handle SetupMainThread(s32 priority, int stack_size) { | 375 | // We need to pass a few valid values to get around parameter checking in Thread::Create. |
| 449 | Handle handle; | 376 | auto thread_res = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, |
| 377 | THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE); | ||
| 378 | _dbg_assert_(Kernel, thread_res.Succeeded()); | ||
| 379 | SharedPtr<Thread> thread = std::move(*thread_res); | ||
| 450 | 380 | ||
| 451 | // Initialize new "main" thread | 381 | thread->idle = true; |
| 452 | Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, | 382 | CallThread(thread.get()); |
| 453 | THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); | 383 | return thread->GetHandle(); |
| 384 | } | ||
| 454 | 385 | ||
| 455 | ResetThread(thread, 0, 0); | 386 | SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) { |
| 387 | // Initialize new "main" thread | ||
| 388 | auto thread_res = Thread::Create("main", Core::g_app_core->GetPC(), priority, 0, | ||
| 389 | THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); | ||
| 390 | // TODO(yuriks): Propagate error | ||
| 391 | _dbg_assert_(Kernel, thread_res.Succeeded()); | ||
| 392 | SharedPtr<Thread> thread = std::move(*thread_res); | ||
| 456 | 393 | ||
| 457 | // If running another thread already, set it to "ready" state | 394 | // If running another thread already, set it to "ready" state |
| 458 | Thread* cur = GetCurrentThread(); | 395 | Thread* cur = GetCurrentThread(); |
| @@ -461,11 +398,11 @@ Handle SetupMainThread(s32 priority, int stack_size) { | |||
| 461 | } | 398 | } |
| 462 | 399 | ||
| 463 | // Run new "main" thread | 400 | // Run new "main" thread |
| 464 | SetCurrentThread(thread); | 401 | current_thread = thread.get(); |
| 465 | thread->status = THREADSTATUS_RUNNING; | 402 | thread->status = THREADSTATUS_RUNNING; |
| 466 | LoadContext(thread->context); | 403 | Core::g_app_core->LoadContext(thread->context); |
| 467 | 404 | ||
| 468 | return handle; | 405 | return thread; |
| 469 | } | 406 | } |
| 470 | 407 | ||
| 471 | 408 | ||
| @@ -481,37 +418,19 @@ void Reschedule() { | |||
| 481 | } else { | 418 | } else { |
| 482 | LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); | 419 | LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); |
| 483 | 420 | ||
| 484 | for (Handle handle : thread_queue) { | 421 | for (auto& thread : thread_list) { |
| 485 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 486 | LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", | 422 | LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", |
| 487 | thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle); | 423 | thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, |
| 424 | (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE)); | ||
| 488 | } | 425 | } |
| 489 | } | 426 | } |
| 490 | |||
| 491 | // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put | ||
| 492 | // to sleep. So, we'll just immediately set it to "ready" again after an attempted context | ||
| 493 | // switch has occurred. This results in the current thread yielding on a sleep once, and then it | ||
| 494 | // will immediately be placed back in the queue for execution. | ||
| 495 | |||
| 496 | if (CheckWaitType(prev, WAITTYPE_SLEEP)) | ||
| 497 | ResumeThreadFromWait(prev->GetHandle()); | ||
| 498 | } | ||
| 499 | |||
| 500 | ResultCode GetThreadId(u32* thread_id, Handle handle) { | ||
| 501 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 502 | if (thread == nullptr) | ||
| 503 | return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, | ||
| 504 | ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 505 | |||
| 506 | *thread_id = thread->thread_id; | ||
| 507 | |||
| 508 | return RESULT_SUCCESS; | ||
| 509 | } | 427 | } |
| 510 | 428 | ||
| 511 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 429 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 512 | 430 | ||
| 513 | void ThreadingInit() { | 431 | void ThreadingInit() { |
| 514 | next_thread_id = INITIAL_THREAD_ID; | 432 | next_thread_id = INITIAL_THREAD_ID; |
| 433 | ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); | ||
| 515 | } | 434 | } |
| 516 | 435 | ||
| 517 | void ThreadingShutdown() { | 436 | void ThreadingShutdown() { |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 0e1397cd9..284dec400 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -4,8 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 7 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 8 | 11 | ||
| 12 | #include "core/core.h" | ||
| 9 | #include "core/mem_map.h" | 13 | #include "core/mem_map.h" |
| 10 | 14 | ||
| 11 | #include "core/hle/kernel/kernel.h" | 15 | #include "core/hle/kernel/kernel.h" |
| @@ -43,67 +47,115 @@ enum WaitType { | |||
| 43 | WAITTYPE_MUTEX, | 47 | WAITTYPE_MUTEX, |
| 44 | WAITTYPE_SYNCH, | 48 | WAITTYPE_SYNCH, |
| 45 | WAITTYPE_ARB, | 49 | WAITTYPE_ARB, |
| 50 | WAITTYPE_TIMER, | ||
| 46 | }; | 51 | }; |
| 47 | 52 | ||
| 48 | namespace Kernel { | 53 | namespace Kernel { |
| 49 | 54 | ||
| 50 | /// Creates a new thread - wrapper for external user | 55 | class Thread : public Kernel::Object { |
| 51 | Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, | 56 | public: |
| 52 | u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE); | 57 | static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, |
| 58 | u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); | ||
| 53 | 59 | ||
| 54 | /// Sets up the primary application thread | 60 | std::string GetName() const override { return name; } |
| 55 | Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); | 61 | std::string GetTypeName() const override { return "Thread"; } |
| 56 | 62 | ||
| 57 | /// Reschedules to the next available thread (call after current thread is suspended) | 63 | static const HandleType HANDLE_TYPE = HandleType::Thread; |
| 58 | void Reschedule(); | 64 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 59 | 65 | ||
| 60 | /// Stops the current thread | 66 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } |
| 61 | ResultCode StopThread(Handle thread, const char* reason); | 67 | inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } |
| 68 | inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; } | ||
| 69 | inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } | ||
| 70 | inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } | ||
| 71 | inline bool IsIdle() const { return idle; } | ||
| 62 | 72 | ||
| 63 | /** | 73 | ResultVal<bool> WaitSynchronization() override; |
| 64 | * Retrieves the ID of the specified thread handle | 74 | |
| 65 | * @param thread_id Will contain the output thread id | 75 | s32 GetPriority() const { return current_priority; } |
| 66 | * @param handle Handle to the thread we want | 76 | void SetPriority(s32 priority); |
| 67 | * @return Whether the function was successful or not | 77 | |
| 68 | */ | 78 | u32 GetThreadId() const { return thread_id; } |
| 69 | ResultCode GetThreadId(u32* thread_id, Handle handle); | 79 | |
| 80 | void Stop(const char* reason); | ||
| 81 | /// Resumes a thread from waiting by marking it as "ready". | ||
| 82 | void ResumeFromWait(); | ||
| 83 | |||
| 84 | Core::ThreadContext context; | ||
| 85 | |||
| 86 | u32 thread_id; | ||
| 87 | |||
| 88 | u32 status; | ||
| 89 | u32 entry_point; | ||
| 90 | u32 stack_top; | ||
| 91 | u32 stack_size; | ||
| 92 | |||
| 93 | s32 initial_priority; | ||
| 94 | s32 current_priority; | ||
| 95 | |||
| 96 | s32 processor_id; | ||
| 97 | |||
| 98 | WaitType wait_type; | ||
| 99 | Object* wait_object; | ||
| 100 | VAddr wait_address; | ||
| 70 | 101 | ||
| 71 | /// Resumes a thread from waiting by marking it as "ready" | 102 | std::vector<SharedPtr<Thread>> waiting_threads; |
| 72 | void ResumeThreadFromWait(Handle handle); | 103 | |
| 104 | std::string name; | ||
| 105 | |||
| 106 | /// Whether this thread is intended to never actually be executed, i.e. always idle | ||
| 107 | bool idle = false; | ||
| 108 | |||
| 109 | private: | ||
| 110 | Thread() = default; | ||
| 111 | }; | ||
| 112 | |||
| 113 | /// Sets up the primary application thread | ||
| 114 | SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); | ||
| 115 | |||
| 116 | /// Reschedules to the next available thread (call after current thread is suspended) | ||
| 117 | void Reschedule(); | ||
| 73 | 118 | ||
| 74 | /// Arbitrate the highest priority thread that is waiting | 119 | /// Arbitrate the highest priority thread that is waiting |
| 75 | Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address); | 120 | Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address); |
| 76 | 121 | ||
| 77 | /// Arbitrate all threads currently waiting... | 122 | /// Arbitrate all threads currently waiting... |
| 78 | void ArbitrateAllThreads(u32 arbiter, u32 address); | 123 | void ArbitrateAllThreads(Object* arbiter, u32 address); |
| 79 | 124 | ||
| 80 | /// Gets the current thread handle | 125 | /// Gets the current thread |
| 81 | Handle GetCurrentThreadHandle(); | 126 | Thread* GetCurrentThread(); |
| 82 | 127 | ||
| 83 | /** | 128 | /** |
| 84 | * Puts the current thread in the wait state for the given type | 129 | * Puts the current thread in the wait state for the given type |
| 85 | * @param wait_type Type of wait | 130 | * @param wait_type Type of wait |
| 86 | * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread | 131 | * @param wait_object Kernel object that we are waiting on, defaults to current thread |
| 132 | */ | ||
| 133 | void WaitCurrentThread(WaitType wait_type, Object* wait_object = GetCurrentThread()); | ||
| 134 | |||
| 135 | /** | ||
| 136 | * Schedules an event to wake up the specified thread after the specified delay. | ||
| 137 | * @param handle The thread handle. | ||
| 138 | * @param nanoseconds The time this thread will be allowed to sleep for. | ||
| 87 | */ | 139 | */ |
| 88 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); | 140 | void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); |
| 89 | 141 | ||
| 90 | /** | 142 | /** |
| 91 | * Puts the current thread in the wait state for the given type | 143 | * Puts the current thread in the wait state for the given type |
| 92 | * @param wait_type Type of wait | 144 | * @param wait_type Type of wait |
| 93 | * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread | 145 | * @param wait_object Kernel object that we are waiting on |
| 94 | * @param wait_address Arbitration address used to resume from wait | 146 | * @param wait_address Arbitration address used to resume from wait |
| 95 | */ | 147 | */ |
| 96 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address); | 148 | void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address); |
| 97 | 149 | ||
| 98 | /// Put current thread in a wait state - on WaitSynchronization | ||
| 99 | void WaitThread_Synchronization(); | ||
| 100 | 150 | ||
| 101 | /// Get the priority of the thread specified by handle | ||
| 102 | ResultVal<u32> GetThreadPriority(const Handle handle); | ||
| 103 | |||
| 104 | /// Set the priority of the thread specified by handle | ||
| 105 | ResultCode SetThreadPriority(Handle handle, s32 priority); | ||
| 106 | 151 | ||
| 152 | /** | ||
| 153 | * Sets up the idle thread, this is a thread that is intended to never execute instructions, | ||
| 154 | * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue | ||
| 155 | * and will try to yield on every call. | ||
| 156 | * @returns The handle of the idle thread | ||
| 157 | */ | ||
| 158 | Handle SetupIdleThread(); | ||
| 107 | /// Initialize threading | 159 | /// Initialize threading |
| 108 | void ThreadingInit(); | 160 | void ThreadingInit(); |
| 109 | 161 | ||
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp new file mode 100644 index 000000000..3b0452d4d --- /dev/null +++ b/src/core/hle/kernel/timer.cpp | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <set> | ||
| 6 | |||
| 7 | #include "common/common.h" | ||
| 8 | |||
| 9 | #include "core/core_timing.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/kernel/timer.h" | ||
| 12 | #include "core/hle/kernel/thread.h" | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | |||
| 16 | class Timer : public Object { | ||
| 17 | public: | ||
| 18 | std::string GetTypeName() const override { return "Timer"; } | ||
| 19 | std::string GetName() const override { return name; } | ||
| 20 | |||
| 21 | static const HandleType HANDLE_TYPE = HandleType::Timer; | ||
| 22 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | ||
| 23 | |||
| 24 | ResetType reset_type; ///< The ResetType of this timer | ||
| 25 | |||
| 26 | bool signaled; ///< Whether the timer has been signaled or not | ||
| 27 | std::set<Handle> waiting_threads; ///< Threads that are waiting for the timer | ||
| 28 | std::string name; ///< Name of timer (optional) | ||
| 29 | |||
| 30 | u64 initial_delay; ///< The delay until the timer fires for the first time | ||
| 31 | u64 interval_delay; ///< The delay until the timer fires after the first time | ||
| 32 | |||
| 33 | ResultVal<bool> WaitSynchronization() override { | ||
| 34 | bool wait = !signaled; | ||
| 35 | if (wait) { | ||
| 36 | waiting_threads.insert(GetCurrentThread()->GetHandle()); | ||
| 37 | Kernel::WaitCurrentThread(WAITTYPE_TIMER, this); | ||
| 38 | } | ||
| 39 | return MakeResult<bool>(wait); | ||
| 40 | } | ||
| 41 | }; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Creates a timer. | ||
| 45 | * @param handle Reference to handle for the newly created timer | ||
| 46 | * @param reset_type ResetType describing how to create timer | ||
| 47 | * @param name Optional name of timer | ||
| 48 | * @return Newly created Timer object | ||
| 49 | */ | ||
| 50 | Timer* CreateTimer(Handle& handle, const ResetType reset_type, const std::string& name) { | ||
| 51 | Timer* timer = new Timer; | ||
| 52 | |||
| 53 | handle = Kernel::g_handle_table.Create(timer).ValueOr(INVALID_HANDLE); | ||
| 54 | |||
| 55 | timer->reset_type = reset_type; | ||
| 56 | timer->signaled = false; | ||
| 57 | timer->name = name; | ||
| 58 | timer->initial_delay = 0; | ||
| 59 | timer->interval_delay = 0; | ||
| 60 | return timer; | ||
| 61 | } | ||
| 62 | |||
| 63 | ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name) { | ||
| 64 | CreateTimer(*handle, reset_type, name); | ||
| 65 | return RESULT_SUCCESS; | ||
| 66 | } | ||
| 67 | |||
| 68 | ResultCode ClearTimer(Handle handle) { | ||
| 69 | SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); | ||
| 70 | |||
| 71 | if (timer == nullptr) | ||
| 72 | return InvalidHandle(ErrorModule::Kernel); | ||
| 73 | |||
| 74 | timer->signaled = false; | ||
| 75 | return RESULT_SUCCESS; | ||
| 76 | } | ||
| 77 | |||
| 78 | /// The event type of the generic timer callback event | ||
| 79 | static int TimerCallbackEventType = -1; | ||
| 80 | |||
| 81 | /// The timer callback event, called when a timer is fired | ||
| 82 | static void TimerCallback(u64 timer_handle, int cycles_late) { | ||
| 83 | SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(timer_handle); | ||
| 84 | |||
| 85 | if (timer == nullptr) { | ||
| 86 | LOG_CRITICAL(Kernel, "Callback fired for invalid timer %u", timer_handle); | ||
| 87 | return; | ||
| 88 | } | ||
| 89 | |||
| 90 | LOG_TRACE(Kernel, "Timer %u fired", timer_handle); | ||
| 91 | |||
| 92 | timer->signaled = true; | ||
| 93 | |||
| 94 | // Resume all waiting threads | ||
| 95 | for (Handle thread_handle : timer->waiting_threads) { | ||
| 96 | if (SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(thread_handle)) | ||
| 97 | thread->ResumeFromWait(); | ||
| 98 | } | ||
| 99 | |||
| 100 | timer->waiting_threads.clear(); | ||
| 101 | |||
| 102 | if (timer->reset_type == RESETTYPE_ONESHOT) | ||
| 103 | timer->signaled = false; | ||
| 104 | |||
| 105 | if (timer->interval_delay != 0) { | ||
| 106 | // Reschedule the timer with the interval delay | ||
| 107 | u64 interval_microseconds = timer->interval_delay / 1000; | ||
| 108 | CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late, | ||
| 109 | TimerCallbackEventType, timer_handle); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | ResultCode SetTimer(Handle handle, s64 initial, s64 interval) { | ||
| 114 | SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); | ||
| 115 | |||
| 116 | if (timer == nullptr) | ||
| 117 | return InvalidHandle(ErrorModule::Kernel); | ||
| 118 | |||
| 119 | timer->initial_delay = initial; | ||
| 120 | timer->interval_delay = interval; | ||
| 121 | |||
| 122 | u64 initial_microseconds = initial / 1000; | ||
| 123 | CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), TimerCallbackEventType, handle); | ||
| 124 | return RESULT_SUCCESS; | ||
| 125 | } | ||
| 126 | |||
| 127 | ResultCode CancelTimer(Handle handle) { | ||
| 128 | SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); | ||
| 129 | |||
| 130 | if (timer == nullptr) | ||
| 131 | return InvalidHandle(ErrorModule::Kernel); | ||
| 132 | |||
| 133 | CoreTiming::UnscheduleEvent(TimerCallbackEventType, handle); | ||
| 134 | return RESULT_SUCCESS; | ||
| 135 | } | ||
| 136 | |||
| 137 | void TimersInit() { | ||
| 138 | TimerCallbackEventType = CoreTiming::RegisterEvent("TimerCallback", TimerCallback); | ||
| 139 | } | ||
| 140 | |||
| 141 | void TimersShutdown() { | ||
| 142 | } | ||
| 143 | |||
| 144 | } // namespace | ||
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h new file mode 100644 index 000000000..f8aa66b60 --- /dev/null +++ b/src/core/hle/kernel/timer.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 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 | #include "core/hle/svc.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | /** | ||
| 15 | * Cancels a timer | ||
| 16 | * @param handle Handle of the timer to cancel | ||
| 17 | */ | ||
| 18 | ResultCode CancelTimer(Handle handle); | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Starts a timer with the specified initial delay and interval | ||
| 22 | * @param handle Handle of the timer to start | ||
| 23 | * @param initial Delay until the timer is first fired | ||
| 24 | * @param interval Delay until the timer is fired after the first time | ||
| 25 | */ | ||
| 26 | ResultCode SetTimer(Handle handle, s64 initial, s64 interval); | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Clears a timer | ||
| 30 | * @param handle Handle of the timer to clear | ||
| 31 | */ | ||
| 32 | ResultCode ClearTimer(Handle handle); | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Creates a timer | ||
| 36 | * @param Handle to newly created Timer object | ||
| 37 | * @param reset_type ResetType describing how to create the timer | ||
| 38 | * @param name Optional name of timer | ||
| 39 | * @return ResultCode of the error | ||
| 40 | */ | ||
| 41 | ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name="Unknown"); | ||
| 42 | |||
| 43 | /// Initializes the required variables for timers | ||
| 44 | void TimersInit(); | ||
| 45 | /// Tears down the timer variables | ||
| 46 | void TimersShutdown(); | ||
| 47 | } // namespace | ||
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 0e9c213e0..82dcf5bba 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -369,14 +369,14 @@ private: | |||
| 369 | 369 | ||
| 370 | StorageType storage; | 370 | StorageType storage; |
| 371 | ResultCode result_code; | 371 | ResultCode result_code; |
| 372 | #if _DEBUG | 372 | #ifdef _DEBUG |
| 373 | // The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the | 373 | // The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the |
| 374 | // need to cast `storage` to a pointer or pay attention to `result_code`. | 374 | // need to cast `storage` to a pointer or pay attention to `result_code`. |
| 375 | const T* debug_ptr; | 375 | const T* debug_ptr; |
| 376 | #endif | 376 | #endif |
| 377 | 377 | ||
| 378 | void UpdateDebugPtr() { | 378 | void UpdateDebugPtr() { |
| 379 | #if _DEBUG | 379 | #ifdef _DEBUG |
| 380 | debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage)); | 380 | debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage)); |
| 381 | #endif | 381 | #endif |
| 382 | } | 382 | } |
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp index d8b261ba7..d0ff4e585 100644 --- a/src/core/hle/service/apt_u.cpp +++ b/src/core/hle/service/apt_u.cpp | |||
| @@ -52,8 +52,6 @@ void Initialize(Service::Interface* self) { | |||
| 52 | Kernel::ReleaseMutex(lock_handle); | 52 | Kernel::ReleaseMutex(lock_handle); |
| 53 | 53 | ||
| 54 | cmd_buff[1] = 0; // No error | 54 | cmd_buff[1] = 0; // No error |
| 55 | |||
| 56 | LOG_DEBUG(Service_APT, "called"); | ||
| 57 | } | 55 | } |
| 58 | 56 | ||
| 59 | void GetLockHandle(Service::Interface* self) { | 57 | void GetLockHandle(Service::Interface* self) { |
| @@ -194,8 +192,6 @@ void AppletUtility(Service::Interface* self) { | |||
| 194 | * 4 : Handle to shared font memory | 192 | * 4 : Handle to shared font memory |
| 195 | */ | 193 | */ |
| 196 | void GetSharedFont(Service::Interface* self) { | 194 | void GetSharedFont(Service::Interface* self) { |
| 197 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 198 | |||
| 199 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 195 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 200 | 196 | ||
| 201 | if (!shared_font.empty()) { | 197 | if (!shared_font.empty()) { |
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 161aa8531..8812c49ef 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp | |||
| @@ -161,9 +161,9 @@ ResultCode FormatConfig() { | |||
| 161 | void CFGInit() { | 161 | void CFGInit() { |
| 162 | // TODO(Subv): In the future we should use the FS service to query this archive, | 162 | // TODO(Subv): In the future we should use the FS service to query this archive, |
| 163 | // currently it is not possible because you can only have one open archive of the same type at any time | 163 | // currently it is not possible because you can only have one open archive of the same type at any time |
| 164 | std::string syssavedata_directory = FileUtil::GetUserPath(D_SYSSAVEDATA_IDX); | 164 | std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); |
| 165 | cfg_system_save_data = Common::make_unique<FileSys::Archive_SystemSaveData>( | 165 | cfg_system_save_data = Common::make_unique<FileSys::Archive_SystemSaveData>( |
| 166 | syssavedata_directory, CFG_SAVE_ID); | 166 | nand_directory, CFG_SAVE_ID); |
| 167 | if (!cfg_system_save_data->Initialize()) { | 167 | if (!cfg_system_save_data->Initialize()) { |
| 168 | LOG_CRITICAL(Service_CFG, "Could not initialize SystemSaveData archive for the CFG:U service"); | 168 | LOG_CRITICAL(Service_CFG, "Could not initialize SystemSaveData archive for the CFG:U service"); |
| 169 | return; | 169 | return; |
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index f761c6ab9..958dd9344 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp | |||
| @@ -36,6 +36,10 @@ namespace std { | |||
| 36 | }; | 36 | }; |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | /// TODO(Subv): Confirm length of these strings | ||
| 40 | const std::string SYSTEM_ID = "00000000000000000000000000000000"; | ||
| 41 | const std::string SDCARD_ID = "00000000000000000000000000000000"; | ||
| 42 | |||
| 39 | namespace Service { | 43 | namespace Service { |
| 40 | namespace FS { | 44 | namespace FS { |
| 41 | 45 | ||
| @@ -432,11 +436,11 @@ ResultCode FormatSaveData() { | |||
| 432 | void ArchiveInit() { | 436 | void ArchiveInit() { |
| 433 | next_handle = 1; | 437 | next_handle = 1; |
| 434 | 438 | ||
| 435 | // TODO(Link Mauve): Add the other archive types (see here for the known types: | 439 | // TODO(Subv): Add the other archive types (see here for the known types: |
| 436 | // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished | 440 | // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). |
| 437 | // archive type is SDMC, so it is the only one getting exposed. | ||
| 438 | 441 | ||
| 439 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); | 442 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); |
| 443 | std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); | ||
| 440 | auto sdmc_archive = Common::make_unique<FileSys::Archive_SDMC>(sdmc_directory); | 444 | auto sdmc_archive = Common::make_unique<FileSys::Archive_SDMC>(sdmc_directory); |
| 441 | if (sdmc_archive->Initialize()) | 445 | if (sdmc_archive->Initialize()) |
| 442 | CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC); | 446 | CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC); |
| @@ -444,28 +448,24 @@ void ArchiveInit() { | |||
| 444 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | 448 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); |
| 445 | 449 | ||
| 446 | // Create the SaveData archive | 450 | // Create the SaveData archive |
| 447 | std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX); | 451 | auto savedata_archive = Common::make_unique<FileSys::Archive_SaveData>(sdmc_directory); |
| 448 | auto savedata_archive = Common::make_unique<FileSys::Archive_SaveData>(savedata_directory); | ||
| 449 | CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData); | 452 | CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData); |
| 450 | 453 | ||
| 451 | std::string extsavedata_directory = FileUtil::GetUserPath(D_EXTSAVEDATA); | 454 | auto extsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(sdmc_directory, false); |
| 452 | auto extsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(extsavedata_directory); | ||
| 453 | if (extsavedata_archive->Initialize()) | 455 | if (extsavedata_archive->Initialize()) |
| 454 | CreateArchive(std::move(extsavedata_archive), ArchiveIdCode::ExtSaveData); | 456 | CreateArchive(std::move(extsavedata_archive), ArchiveIdCode::ExtSaveData); |
| 455 | else | 457 | else |
| 456 | LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_directory.c_str()); | 458 | LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_archive->GetMountPoint().c_str()); |
| 457 | 459 | ||
| 458 | std::string sharedextsavedata_directory = FileUtil::GetUserPath(D_EXTSAVEDATA); | 460 | auto sharedextsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(nand_directory, true); |
| 459 | auto sharedextsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(sharedextsavedata_directory); | ||
| 460 | if (sharedextsavedata_archive->Initialize()) | 461 | if (sharedextsavedata_archive->Initialize()) |
| 461 | CreateArchive(std::move(sharedextsavedata_archive), ArchiveIdCode::SharedExtSaveData); | 462 | CreateArchive(std::move(sharedextsavedata_archive), ArchiveIdCode::SharedExtSaveData); |
| 462 | else | 463 | else |
| 463 | LOG_ERROR(Service_FS, "Can't instantiate SharedExtSaveData archive with path %s", | 464 | LOG_ERROR(Service_FS, "Can't instantiate SharedExtSaveData archive with path %s", |
| 464 | sharedextsavedata_directory.c_str()); | 465 | sharedextsavedata_archive->GetMountPoint().c_str()); |
| 465 | 466 | ||
| 466 | // Create the SaveDataCheck archive, basically a small variation of the RomFS archive | 467 | // Create the SaveDataCheck archive, basically a small variation of the RomFS archive |
| 467 | std::string savedatacheck_directory = FileUtil::GetUserPath(D_SAVEDATACHECK_IDX); | 468 | auto savedatacheck_archive = Common::make_unique<FileSys::Archive_SaveDataCheck>(nand_directory); |
| 468 | auto savedatacheck_archive = Common::make_unique<FileSys::Archive_SaveDataCheck>(savedatacheck_directory); | ||
| 469 | CreateArchive(std::move(savedatacheck_archive), ArchiveIdCode::SaveDataCheck); | 469 | CreateArchive(std::move(savedatacheck_archive), ArchiveIdCode::SaveDataCheck); |
| 470 | } | 470 | } |
| 471 | 471 | ||
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 9e9efa019..b3f2134f2 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h | |||
| @@ -10,6 +10,11 @@ | |||
| 10 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| 12 | 12 | ||
| 13 | /// The unique system identifier hash, also known as ID0 | ||
| 14 | extern const std::string SYSTEM_ID; | ||
| 15 | /// The scrambled SD card CID, also known as ID1 | ||
| 16 | extern const std::string SDCARD_ID; | ||
| 17 | |||
| 13 | namespace Service { | 18 | namespace Service { |
| 14 | namespace FS { | 19 | namespace FS { |
| 15 | 20 | ||
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 7eb32146d..56f3117f4 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp | |||
| @@ -27,8 +27,6 @@ static void Initialize(Service::Interface* self) { | |||
| 27 | // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per | 27 | // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per |
| 28 | // http://3dbrew.org/wiki/FS:Initialize#Request | 28 | // http://3dbrew.org/wiki/FS:Initialize#Request |
| 29 | cmd_buff[1] = RESULT_SUCCESS.raw; | 29 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 30 | |||
| 31 | LOG_DEBUG(Service_FS, "called"); | ||
| 32 | } | 30 | } |
| 33 | 31 | ||
| 34 | /** | 32 | /** |
| @@ -104,8 +102,8 @@ static void OpenFileDirectly(Service::Interface* self) { | |||
| 104 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | 102 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); |
| 105 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); | 103 | FileSys::Path file_path(filename_type, filename_size, filename_ptr); |
| 106 | 104 | ||
| 107 | LOG_DEBUG(Service_FS, "archive_path=%s file_path=%s, mode=%u attributes=%d", | 105 | LOG_DEBUG(Service_FS, "archive_id=0x%08X archive_path=%s file_path=%s, mode=%u attributes=%d", |
| 108 | archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes); | 106 | archive_id, archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes); |
| 109 | 107 | ||
| 110 | ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path); | 108 | ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path); |
| 111 | if (archive_handle.Failed()) { | 109 | if (archive_handle.Failed()) { |
| @@ -367,7 +365,7 @@ static void OpenArchive(Service::Interface* self) { | |||
| 367 | u32 archivename_ptr = cmd_buff[5]; | 365 | u32 archivename_ptr = cmd_buff[5]; |
| 368 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | 366 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); |
| 369 | 367 | ||
| 370 | LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str()); | 368 | LOG_DEBUG(Service_FS, "archive_id=0x%08X archive_path=%s", archive_id, archive_path.DebugStr().c_str()); |
| 371 | 369 | ||
| 372 | ResultVal<ArchiveHandle> handle = OpenArchive(archive_id, archive_path); | 370 | ResultVal<ArchiveHandle> handle = OpenArchive(archive_id, archive_path); |
| 373 | cmd_buff[1] = handle.Code().raw; | 371 | cmd_buff[1] = handle.Code().raw; |
| @@ -408,8 +406,6 @@ static void IsSdmcDetected(Service::Interface* self) { | |||
| 408 | 406 | ||
| 409 | cmd_buff[1] = 0; | 407 | cmd_buff[1] = 0; |
| 410 | cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; | 408 | cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; |
| 411 | |||
| 412 | LOG_DEBUG(Service_FS, "called"); | ||
| 413 | } | 409 | } |
| 414 | 410 | ||
| 415 | /** | 411 | /** |
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 0127d4ee5..2b115240f 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp | |||
| @@ -291,8 +291,11 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 291 | // Update framebuffer information if requested | 291 | // Update framebuffer information if requested |
| 292 | for (int screen_id = 0; screen_id < 2; ++screen_id) { | 292 | for (int screen_id = 0; screen_id < 2; ++screen_id) { |
| 293 | FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id); | 293 | FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id); |
| 294 | if (info->is_dirty) | 294 | |
| 295 | if (info->is_dirty) { | ||
| 295 | SetBufferSwap(screen_id, info->framebuffer_info[info->index]); | 296 | SetBufferSwap(screen_id, info->framebuffer_info[info->index]); |
| 297 | info->framebuffer_info->active_fb = info->framebuffer_info->active_fb ^ 1; | ||
| 298 | } | ||
| 296 | 299 | ||
| 297 | info->is_dirty = false; | 300 | info->is_dirty = false; |
| 298 | } | 301 | } |
| @@ -328,9 +331,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { | |||
| 328 | 331 | ||
| 329 | /// This triggers handling of the GX command written to the command buffer in shared memory. | 332 | /// This triggers handling of the GX command written to the command buffer in shared memory. |
| 330 | static void TriggerCmdReqQueue(Service::Interface* self) { | 333 | static void TriggerCmdReqQueue(Service::Interface* self) { |
| 331 | |||
| 332 | LOG_TRACE(Service_GSP, "called"); | ||
| 333 | |||
| 334 | // Iterate through each thread's command queue... | 334 | // Iterate through each thread's command queue... |
| 335 | for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { | 335 | for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { |
| 336 | CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id); | 336 | CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id); |
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp index 99b0ea5a0..1403b1de9 100644 --- a/src/core/hle/service/hid_user.cpp +++ b/src/core/hle/service/hid_user.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/log.h" | 5 | #include "common/log.h" |
| 6 | 6 | ||
| 7 | #include "core/arm/arm_interface.h" | ||
| 7 | #include "core/hle/hle.h" | 8 | #include "core/hle/hle.h" |
| 8 | #include "core/hle/kernel/event.h" | 9 | #include "core/hle/kernel/event.h" |
| 9 | #include "core/hle/kernel/shared_memory.h" | 10 | #include "core/hle/kernel/shared_memory.h" |
| @@ -162,8 +163,6 @@ static void GetIPCHandles(Service::Interface* self) { | |||
| 162 | cmd_buff[6] = event_accelerometer; | 163 | cmd_buff[6] = event_accelerometer; |
| 163 | cmd_buff[7] = event_gyroscope; | 164 | cmd_buff[7] = event_gyroscope; |
| 164 | cmd_buff[8] = event_debug_pad; | 165 | cmd_buff[8] = event_debug_pad; |
| 165 | |||
| 166 | LOG_TRACE(Service_HID, "called"); | ||
| 167 | } | 166 | } |
| 168 | 167 | ||
| 169 | const Interface::FunctionInfo FunctionTable[] = { | 168 | const Interface::FunctionInfo FunctionTable[] = { |
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp index 9cc700c46..753180add 100644 --- a/src/core/hle/service/ptm_u.cpp +++ b/src/core/hle/service/ptm_u.cpp | |||
| @@ -76,8 +76,6 @@ static void GetShellState(Service::Interface* self) { | |||
| 76 | 76 | ||
| 77 | cmd_buff[1] = 0; | 77 | cmd_buff[1] = 0; |
| 78 | cmd_buff[2] = shell_open ? 1 : 0; | 78 | cmd_buff[2] = shell_open ? 1 : 0; |
| 79 | |||
| 80 | LOG_TRACE(Service_PTM, "PTM_U::GetShellState called"); | ||
| 81 | } | 79 | } |
| 82 | 80 | ||
| 83 | /** | 81 | /** |
| @@ -142,10 +140,10 @@ Interface::Interface() { | |||
| 142 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | 140 | Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |
| 143 | // Create the SharedExtSaveData archive 0xF000000B and the gamecoin.dat file | 141 | // Create the SharedExtSaveData archive 0xF000000B and the gamecoin.dat file |
| 144 | // TODO(Subv): In the future we should use the FS service to query this archive | 142 | // TODO(Subv): In the future we should use the FS service to query this archive |
| 145 | std::string extsavedata_directory = FileUtil::GetUserPath(D_EXTSAVEDATA); | 143 | std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); |
| 146 | ptm_shared_extsavedata = Common::make_unique<FileSys::Archive_ExtSaveData>(extsavedata_directory); | 144 | ptm_shared_extsavedata = Common::make_unique<FileSys::Archive_ExtSaveData>(nand_directory, true); |
| 147 | if (!ptm_shared_extsavedata->Initialize()) { | 145 | if (!ptm_shared_extsavedata->Initialize()) { |
| 148 | LOG_CRITICAL(Service_PTM, "Could not initialize ExtSaveData archive for the PTM:U service"); | 146 | LOG_CRITICAL(Service_PTM, "Could not initialize SharedExtSaveData archive for the PTM:U service"); |
| 149 | return; | 147 | return; |
| 150 | } | 148 | } |
| 151 | FileSys::Path archive_path(ptm_shared_extdata_id); | 149 | FileSys::Path archive_path(ptm_shared_extdata_id); |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index c5233e687..33c29a4a0 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -46,36 +46,23 @@ Manager* g_manager = nullptr; ///< Service manager | |||
| 46 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 46 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 47 | // Service Manager class | 47 | // Service Manager class |
| 48 | 48 | ||
| 49 | Manager::Manager() { | ||
| 50 | } | ||
| 51 | |||
| 52 | Manager::~Manager() { | ||
| 53 | for(Interface* service : m_services) { | ||
| 54 | DeleteService(service->GetPortName()); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Add a service to the manager (does not create it though) | ||
| 59 | void Manager::AddService(Interface* service) { | 49 | void Manager::AddService(Interface* service) { |
| 60 | // TOOD(yuriks): Fix error reporting | 50 | // TOOD(yuriks): Fix error reporting |
| 61 | m_port_map[service->GetPortName()] = Kernel::g_handle_table.Create(service).ValueOr(INVALID_HANDLE); | 51 | m_port_map[service->GetPortName()] = Kernel::g_handle_table.Create(service).ValueOr(INVALID_HANDLE); |
| 62 | m_services.push_back(service); | 52 | m_services.push_back(service); |
| 63 | } | 53 | } |
| 64 | 54 | ||
| 65 | /// Removes a service from the manager, also frees memory | ||
| 66 | void Manager::DeleteService(const std::string& port_name) { | 55 | void Manager::DeleteService(const std::string& port_name) { |
| 67 | Interface* service = FetchFromPortName(port_name); | 56 | Interface* service = FetchFromPortName(port_name); |
| 68 | m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end()); | 57 | m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end()); |
| 69 | m_port_map.erase(port_name); | 58 | m_port_map.erase(port_name); |
| 70 | delete service; | ||
| 71 | } | 59 | } |
| 72 | 60 | ||
| 73 | /// Get a Service Interface from its Handle | ||
| 74 | Interface* Manager::FetchFromHandle(Handle handle) { | 61 | Interface* Manager::FetchFromHandle(Handle handle) { |
| 75 | return Kernel::g_handle_table.Get<Interface>(handle); | 62 | // TODO(yuriks): This function is very suspicious and should probably be exterminated. |
| 63 | return Kernel::g_handle_table.Get<Interface>(handle).get(); | ||
| 76 | } | 64 | } |
| 77 | 65 | ||
| 78 | /// Get a Service Interface from its port | ||
| 79 | Interface* Manager::FetchFromPortName(const std::string& port_name) { | 66 | Interface* Manager::FetchFromPortName(const std::string& port_name) { |
| 80 | auto itr = m_port_map.find(port_name); | 67 | auto itr = m_port_map.find(port_name); |
| 81 | if (itr == m_port_map.end()) { | 68 | if (itr == m_port_map.end()) { |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 28b4ccd17..e75d5008b 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -33,6 +33,22 @@ class Interface : public Kernel::Session { | |||
| 33 | // processes. | 33 | // processes. |
| 34 | 34 | ||
| 35 | friend class Manager; | 35 | friend class Manager; |
| 36 | |||
| 37 | /** | ||
| 38 | * Creates a function string for logging, complete with the name (or header code, depending | ||
| 39 | * on what's passed in) the port name, and all the cmd_buff arguments. | ||
| 40 | */ | ||
| 41 | std::string MakeFunctionString(const std::string& name, const std::string& port_name, const u32* cmd_buff) { | ||
| 42 | // Number of params == bits 0-5 + bits 6-11 | ||
| 43 | int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); | ||
| 44 | |||
| 45 | std::string function_string = Common::StringFromFormat("function '%s': port=%s", name.c_str(), port_name.c_str()); | ||
| 46 | for (int i = 1; i <= num_params; ++i) { | ||
| 47 | function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); | ||
| 48 | } | ||
| 49 | return function_string; | ||
| 50 | } | ||
| 51 | |||
| 36 | public: | 52 | public: |
| 37 | std::string GetName() const override { return GetPortName(); } | 53 | std::string GetName() const override { return GetPortName(); } |
| 38 | 54 | ||
| @@ -72,21 +88,14 @@ public: | |||
| 72 | auto itr = m_functions.find(cmd_buff[0]); | 88 | auto itr = m_functions.find(cmd_buff[0]); |
| 73 | 89 | ||
| 74 | if (itr == m_functions.end() || itr->second.func == nullptr) { | 90 | if (itr == m_functions.end() || itr->second.func == nullptr) { |
| 75 | // Number of params == bits 0-5 + bits 6-11 | 91 | std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name; |
| 76 | int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); | 92 | LOG_ERROR(Service, "%s %s", "unknown/unimplemented", MakeFunctionString(function_name, GetPortName(), cmd_buff).c_str()); |
| 77 | |||
| 78 | std::string error = "unknown/unimplemented function '%s': port=%s"; | ||
| 79 | for (int i = 1; i <= num_params; ++i) { | ||
| 80 | error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); | ||
| 81 | } | ||
| 82 | |||
| 83 | std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name; | ||
| 84 | |||
| 85 | LOG_ERROR(Service, error.c_str(), name.c_str(), GetPortName().c_str()); | ||
| 86 | 93 | ||
| 87 | // TODO(bunnei): Hack - ignore error | 94 | // TODO(bunnei): Hack - ignore error |
| 88 | cmd_buff[1] = 0; | 95 | cmd_buff[1] = 0; |
| 89 | return MakeResult<bool>(false); | 96 | return MakeResult<bool>(false); |
| 97 | } else { | ||
| 98 | LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName(), cmd_buff).c_str()); | ||
| 90 | } | 99 | } |
| 91 | 100 | ||
| 92 | itr->second.func(this); | 101 | itr->second.func(this); |
| @@ -114,29 +123,22 @@ private: | |||
| 114 | 123 | ||
| 115 | /// Simple class to manage accessing services from ports and UID handles | 124 | /// Simple class to manage accessing services from ports and UID handles |
| 116 | class Manager { | 125 | class Manager { |
| 117 | |||
| 118 | public: | 126 | public: |
| 119 | Manager(); | 127 | /// Add a service to the manager |
| 120 | |||
| 121 | ~Manager(); | ||
| 122 | |||
| 123 | /// Add a service to the manager (does not create it though) | ||
| 124 | void AddService(Interface* service); | 128 | void AddService(Interface* service); |
| 125 | 129 | ||
| 126 | /// Removes a service from the manager (does not delete it though) | 130 | /// Removes a service from the manager |
| 127 | void DeleteService(const std::string& port_name); | 131 | void DeleteService(const std::string& port_name); |
| 128 | 132 | ||
| 129 | /// Get a Service Interface from its UID | 133 | /// Get a Service Interface from its Handle |
| 130 | Interface* FetchFromHandle(u32 uid); | 134 | Interface* FetchFromHandle(Handle handle); |
| 131 | 135 | ||
| 132 | /// Get a Service Interface from its port | 136 | /// Get a Service Interface from its port |
| 133 | Interface* FetchFromPortName(const std::string& port_name); | 137 | Interface* FetchFromPortName(const std::string& port_name); |
| 134 | 138 | ||
| 135 | private: | 139 | private: |
| 136 | |||
| 137 | std::vector<Interface*> m_services; | 140 | std::vector<Interface*> m_services; |
| 138 | std::map<std::string, u32> m_port_map; | 141 | std::map<std::string, u32> m_port_map; |
| 139 | |||
| 140 | }; | 142 | }; |
| 141 | 143 | ||
| 142 | /// Initialize ServiceManager | 144 | /// Initialize ServiceManager |
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 912b52adf..ac5f30a28 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp | |||
| @@ -14,16 +14,12 @@ namespace SRV { | |||
| 14 | static Handle g_event_handle = 0; | 14 | static Handle g_event_handle = 0; |
| 15 | 15 | ||
| 16 | static void Initialize(Service::Interface* self) { | 16 | static void Initialize(Service::Interface* self) { |
| 17 | LOG_DEBUG(Service_SRV, "called"); | ||
| 18 | |||
| 19 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 17 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 20 | 18 | ||
| 21 | cmd_buff[1] = 0; // No error | 19 | cmd_buff[1] = 0; // No error |
| 22 | } | 20 | } |
| 23 | 21 | ||
| 24 | static void GetProcSemaphore(Service::Interface* self) { | 22 | static void GetProcSemaphore(Service::Interface* self) { |
| 25 | LOG_TRACE(Service_SRV, "called"); | ||
| 26 | |||
| 27 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 23 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 28 | 24 | ||
| 29 | // TODO(bunnei): Change to a semaphore once these have been implemented | 25 | // TODO(bunnei): Change to a semaphore once these have been implemented |
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index f9e3619dd..b3d873ef0 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp | |||
| @@ -12,6 +12,21 @@ | |||
| 12 | 12 | ||
| 13 | namespace Y2R_U { | 13 | namespace Y2R_U { |
| 14 | 14 | ||
| 15 | /** | ||
| 16 | * Y2R_U::IsBusyConversion service function | ||
| 17 | * Outputs: | ||
| 18 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 19 | * 2 : Whether the current conversion is of type busy conversion (?) | ||
| 20 | */ | ||
| 21 | static void IsBusyConversion(Service::Interface* self) { | ||
| 22 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 23 | |||
| 24 | cmd_buff[1] = RESULT_SUCCESS.raw;; | ||
| 25 | cmd_buff[2] = 0; | ||
| 26 | |||
| 27 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 28 | } | ||
| 29 | |||
| 15 | const Interface::FunctionInfo FunctionTable[] = { | 30 | const Interface::FunctionInfo FunctionTable[] = { |
| 16 | {0x00010040, nullptr, "SetInputFormat"}, | 31 | {0x00010040, nullptr, "SetInputFormat"}, |
| 17 | {0x00030040, nullptr, "SetOutputFormat"}, | 32 | {0x00030040, nullptr, "SetOutputFormat"}, |
| @@ -29,7 +44,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 29 | {0x00220040, nullptr, "SetAlpha"}, | 44 | {0x00220040, nullptr, "SetAlpha"}, |
| 30 | {0x00260000, nullptr, "StartConversion"}, | 45 | {0x00260000, nullptr, "StartConversion"}, |
| 31 | {0x00270000, nullptr, "StopConversion"}, | 46 | {0x00270000, nullptr, "StopConversion"}, |
| 32 | {0x00280000, nullptr, "IsBusyConversion"}, | 47 | {0x00280000, IsBusyConversion, "IsBusyConversion"}, |
| 33 | {0x002A0000, nullptr, "PingProcess"}, | 48 | {0x002A0000, nullptr, "PingProcess"}, |
| 34 | {0x002B0000, nullptr, "DriverInitialize"}, | 49 | {0x002B0000, nullptr, "DriverInitialize"}, |
| 35 | {0x002C0000, nullptr, "DriverFinalize"} | 50 | {0x002C0000, nullptr, "DriverFinalize"} |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index c25409a9f..d3b4483ca 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/string_util.h" | 7 | #include "common/string_util.h" |
| 8 | #include "common/symbols.h" | 8 | #include "common/symbols.h" |
| 9 | 9 | ||
| 10 | #include "core/arm/arm_interface.h" | ||
| 10 | #include "core/mem_map.h" | 11 | #include "core/mem_map.h" |
| 11 | 12 | ||
| 12 | #include "core/hle/kernel/address_arbiter.h" | 13 | #include "core/hle/kernel/address_arbiter.h" |
| @@ -15,6 +16,7 @@ | |||
| 15 | #include "core/hle/kernel/semaphore.h" | 16 | #include "core/hle/kernel/semaphore.h" |
| 16 | #include "core/hle/kernel/shared_memory.h" | 17 | #include "core/hle/kernel/shared_memory.h" |
| 17 | #include "core/hle/kernel/thread.h" | 18 | #include "core/hle/kernel/thread.h" |
| 19 | #include "core/hle/kernel/timer.h" | ||
| 18 | 20 | ||
| 19 | #include "core/hle/function_wrappers.h" | 21 | #include "core/hle/function_wrappers.h" |
| 20 | #include "core/hle/result.h" | 22 | #include "core/hle/result.h" |
| @@ -23,6 +25,8 @@ | |||
| 23 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 25 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 24 | // Namespace SVC | 26 | // Namespace SVC |
| 25 | 27 | ||
| 28 | using Kernel::SharedPtr; | ||
| 29 | |||
| 26 | namespace SVC { | 30 | namespace SVC { |
| 27 | 31 | ||
| 28 | enum ControlMemoryOperation { | 32 | enum ControlMemoryOperation { |
| @@ -92,7 +96,7 @@ static Result ConnectToPort(Handle* out, const char* port_name) { | |||
| 92 | 96 | ||
| 93 | /// Synchronize to an OS service | 97 | /// Synchronize to an OS service |
| 94 | static Result SendSyncRequest(Handle handle) { | 98 | static Result SendSyncRequest(Handle handle) { |
| 95 | Kernel::Session* session = Kernel::g_handle_table.Get<Kernel::Session>(handle); | 99 | SharedPtr<Kernel::Session> session = Kernel::g_handle_table.Get<Kernel::Session>(handle); |
| 96 | if (session == nullptr) { | 100 | if (session == nullptr) { |
| 97 | return InvalidHandle(ErrorModule::Kernel).raw; | 101 | return InvalidHandle(ErrorModule::Kernel).raw; |
| 98 | } | 102 | } |
| @@ -119,12 +123,12 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { | |||
| 119 | // TODO(bunnei): Do something with nano_seconds, currently ignoring this | 123 | // TODO(bunnei): Do something with nano_seconds, currently ignoring this |
| 120 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated | 124 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated |
| 121 | 125 | ||
| 122 | Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handle); | 126 | SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handle); |
| 123 | if (object == nullptr) | 127 | if (object == nullptr) |
| 124 | return InvalidHandle(ErrorModule::Kernel).raw; | 128 | return InvalidHandle(ErrorModule::Kernel).raw; |
| 125 | 129 | ||
| 126 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), | 130 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, |
| 127 | object->GetName().c_str(), nano_seconds); | 131 | object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); |
| 128 | 132 | ||
| 129 | ResultVal<bool> wait = object->WaitSynchronization(); | 133 | ResultVal<bool> wait = object->WaitSynchronization(); |
| 130 | 134 | ||
| @@ -139,6 +143,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { | |||
| 139 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | 143 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 140 | static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, | 144 | static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, |
| 141 | s64 nano_seconds) { | 145 | s64 nano_seconds) { |
| 146 | |||
| 142 | // TODO(bunnei): Do something with nano_seconds, currently ignoring this | 147 | // TODO(bunnei): Do something with nano_seconds, currently ignoring this |
| 143 | bool unlock_all = true; | 148 | bool unlock_all = true; |
| 144 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated | 149 | bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated |
| @@ -148,12 +153,12 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, | |||
| 148 | 153 | ||
| 149 | // Iterate through each handle, synchronize kernel object | 154 | // Iterate through each handle, synchronize kernel object |
| 150 | for (s32 i = 0; i < handle_count; i++) { | 155 | for (s32 i = 0; i < handle_count; i++) { |
| 151 | Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handles[i]); | 156 | SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[i]); |
| 152 | if (object == nullptr) | 157 | if (object == nullptr) |
| 153 | return InvalidHandle(ErrorModule::Kernel).raw; | 158 | return InvalidHandle(ErrorModule::Kernel).raw; |
| 154 | 159 | ||
| 155 | LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(), | 160 | LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], |
| 156 | object->GetName().c_str()); | 161 | object->GetTypeName().c_str(), object->GetName().c_str()); |
| 157 | 162 | ||
| 158 | // TODO(yuriks): Verify how the real function behaves when an error happens here | 163 | // TODO(yuriks): Verify how the real function behaves when an error happens here |
| 159 | ResultVal<bool> wait_result = object->WaitSynchronization(); | 164 | ResultVal<bool> wait_result = object->WaitSynchronization(); |
| @@ -180,7 +185,6 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, | |||
| 180 | 185 | ||
| 181 | /// Create an address arbiter (to allocate access to shared resources) | 186 | /// Create an address arbiter (to allocate access to shared resources) |
| 182 | static Result CreateAddressArbiter(u32* arbiter) { | 187 | static Result CreateAddressArbiter(u32* arbiter) { |
| 183 | LOG_TRACE(Kernel_SVC, "called"); | ||
| 184 | Handle handle = Kernel::CreateAddressArbiter(); | 188 | Handle handle = Kernel::CreateAddressArbiter(); |
| 185 | *arbiter = handle; | 189 | *arbiter = handle; |
| 186 | return 0; | 190 | return 0; |
| @@ -220,6 +224,8 @@ static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, | |||
| 220 | 224 | ||
| 221 | /// Creates a new thread | 225 | /// Creates a new thread |
| 222 | static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { | 226 | static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { |
| 227 | using Kernel::Thread; | ||
| 228 | |||
| 223 | std::string name; | 229 | std::string name; |
| 224 | if (Symbols::HasSymbol(entry_point)) { | 230 | if (Symbols::HasSymbol(entry_point)) { |
| 225 | TSymbol symbol = Symbols::GetSymbol(entry_point); | 231 | TSymbol symbol = Symbols::GetSymbol(entry_point); |
| @@ -228,41 +234,53 @@ static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top | |||
| 228 | name = Common::StringFromFormat("unknown-%08x", entry_point); | 234 | name = Common::StringFromFormat("unknown-%08x", entry_point); |
| 229 | } | 235 | } |
| 230 | 236 | ||
| 231 | Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, arg, processor_id, | 237 | ResultVal<SharedPtr<Thread>> thread_res = Kernel::Thread::Create( |
| 232 | stack_top); | 238 | name, entry_point, priority, arg, processor_id, stack_top, Kernel::DEFAULT_STACK_SIZE); |
| 239 | if (thread_res.Failed()) | ||
| 240 | return thread_res.Code().raw; | ||
| 241 | SharedPtr<Thread> thread = std::move(*thread_res); | ||
| 233 | 242 | ||
| 234 | Core::g_app_core->SetReg(1, thread); | 243 | // TODO(yuriks): Create new handle instead of using built-in |
| 244 | Core::g_app_core->SetReg(1, thread->GetHandle()); | ||
| 235 | 245 | ||
| 236 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | 246 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " |
| 237 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, | 247 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, |
| 238 | name.c_str(), arg, stack_top, priority, processor_id, thread); | 248 | name.c_str(), arg, stack_top, priority, processor_id, thread->GetHandle()); |
| 249 | |||
| 250 | if (THREADPROCESSORID_1 == processor_id) { | ||
| 251 | LOG_WARNING(Kernel_SVC, | ||
| 252 | "thread designated for system CPU core (UNIMPLEMENTED) will be run with app core scheduling"); | ||
| 253 | } | ||
| 239 | 254 | ||
| 240 | return 0; | 255 | return 0; |
| 241 | } | 256 | } |
| 242 | 257 | ||
| 243 | /// Called when a thread exits | 258 | /// Called when a thread exits |
| 244 | static u32 ExitThread() { | 259 | static void ExitThread() { |
| 245 | Handle thread = Kernel::GetCurrentThreadHandle(); | 260 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); |
| 246 | |||
| 247 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C | ||
| 248 | 261 | ||
| 249 | Kernel::StopThread(thread, __func__); | 262 | Kernel::GetCurrentThread()->Stop(__func__); |
| 250 | HLE::Reschedule(__func__); | 263 | HLE::Reschedule(__func__); |
| 251 | return 0; | ||
| 252 | } | 264 | } |
| 253 | 265 | ||
| 254 | /// Gets the priority for the specified thread | 266 | /// Gets the priority for the specified thread |
| 255 | static Result GetThreadPriority(s32* priority, Handle handle) { | 267 | static Result GetThreadPriority(s32* priority, Handle handle) { |
| 256 | ResultVal<u32> priority_result = Kernel::GetThreadPriority(handle); | 268 | const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); |
| 257 | if (priority_result.Succeeded()) { | 269 | if (thread == nullptr) |
| 258 | *priority = *priority_result; | 270 | return InvalidHandle(ErrorModule::Kernel).raw; |
| 259 | } | 271 | |
| 260 | return priority_result.Code().raw; | 272 | *priority = thread->GetPriority(); |
| 273 | return RESULT_SUCCESS.raw; | ||
| 261 | } | 274 | } |
| 262 | 275 | ||
| 263 | /// Sets the priority for the specified thread | 276 | /// Sets the priority for the specified thread |
| 264 | static Result SetThreadPriority(Handle handle, s32 priority) { | 277 | static Result SetThreadPriority(Handle handle, s32 priority) { |
| 265 | return Kernel::SetThreadPriority(handle, priority).raw; | 278 | SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); |
| 279 | if (thread == nullptr) | ||
| 280 | return InvalidHandle(ErrorModule::Kernel).raw; | ||
| 281 | |||
| 282 | thread->SetPriority(priority); | ||
| 283 | return RESULT_SUCCESS.raw; | ||
| 266 | } | 284 | } |
| 267 | 285 | ||
| 268 | /// Create a mutex | 286 | /// Create a mutex |
| @@ -283,8 +301,13 @@ static Result ReleaseMutex(Handle handle) { | |||
| 283 | /// Get the ID for the specified thread. | 301 | /// Get the ID for the specified thread. |
| 284 | static Result GetThreadId(u32* thread_id, Handle handle) { | 302 | static Result GetThreadId(u32* thread_id, Handle handle) { |
| 285 | LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle); | 303 | LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle); |
| 286 | ResultCode result = Kernel::GetThreadId(thread_id, handle); | 304 | |
| 287 | return result.raw; | 305 | const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); |
| 306 | if (thread == nullptr) | ||
| 307 | return InvalidHandle(ErrorModule::Kernel).raw; | ||
| 308 | |||
| 309 | *thread_id = thread->GetThreadId(); | ||
| 310 | return RESULT_SUCCESS.raw; | ||
| 288 | } | 311 | } |
| 289 | 312 | ||
| 290 | /// Creates a semaphore | 313 | /// Creates a semaphore |
| @@ -338,12 +361,42 @@ static Result ClearEvent(Handle evt) { | |||
| 338 | return Kernel::ClearEvent(evt).raw; | 361 | return Kernel::ClearEvent(evt).raw; |
| 339 | } | 362 | } |
| 340 | 363 | ||
| 364 | /// Creates a timer | ||
| 365 | static Result CreateTimer(Handle* handle, u32 reset_type) { | ||
| 366 | ResultCode res = Kernel::CreateTimer(handle, static_cast<ResetType>(reset_type)); | ||
| 367 | LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", | ||
| 368 | reset_type, *handle); | ||
| 369 | return res.raw; | ||
| 370 | } | ||
| 371 | |||
| 372 | /// Clears a timer | ||
| 373 | static Result ClearTimer(Handle handle) { | ||
| 374 | LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); | ||
| 375 | return Kernel::ClearTimer(handle).raw; | ||
| 376 | } | ||
| 377 | |||
| 378 | /// Starts a timer | ||
| 379 | static Result SetTimer(Handle handle, s64 initial, s64 interval) { | ||
| 380 | LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); | ||
| 381 | return Kernel::SetTimer(handle, initial, interval).raw; | ||
| 382 | } | ||
| 383 | |||
| 384 | /// Cancels a timer | ||
| 385 | static Result CancelTimer(Handle handle) { | ||
| 386 | LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); | ||
| 387 | return Kernel::CancelTimer(handle).raw; | ||
| 388 | } | ||
| 389 | |||
| 341 | /// Sleep the current thread | 390 | /// Sleep the current thread |
| 342 | static void SleepThread(s64 nanoseconds) { | 391 | static void SleepThread(s64 nanoseconds) { |
| 343 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); | 392 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); |
| 344 | 393 | ||
| 345 | // Sleep current thread and check for next thread to schedule | 394 | // Sleep current thread and check for next thread to schedule |
| 346 | Kernel::WaitCurrentThread(WAITTYPE_SLEEP); | 395 | Kernel::WaitCurrentThread(WAITTYPE_SLEEP); |
| 396 | |||
| 397 | // Create an event to wake the thread up after the specified nanosecond delay has passed | ||
| 398 | Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds); | ||
| 399 | |||
| 347 | HLE::Reschedule(__func__); | 400 | HLE::Reschedule(__func__); |
| 348 | } | 401 | } |
| 349 | 402 | ||
| @@ -374,7 +427,7 @@ const HLE::FunctionDef SVC_Table[] = { | |||
| 374 | {0x06, nullptr, "GetProcessIdealProcessor"}, | 427 | {0x06, nullptr, "GetProcessIdealProcessor"}, |
| 375 | {0x07, nullptr, "SetProcessIdealProcessor"}, | 428 | {0x07, nullptr, "SetProcessIdealProcessor"}, |
| 376 | {0x08, HLE::Wrap<CreateThread>, "CreateThread"}, | 429 | {0x08, HLE::Wrap<CreateThread>, "CreateThread"}, |
| 377 | {0x09, HLE::Wrap<ExitThread>, "ExitThread"}, | 430 | {0x09, ExitThread, "ExitThread"}, |
| 378 | {0x0A, HLE::Wrap<SleepThread>, "SleepThread"}, | 431 | {0x0A, HLE::Wrap<SleepThread>, "SleepThread"}, |
| 379 | {0x0B, HLE::Wrap<GetThreadPriority>, "GetThreadPriority"}, | 432 | {0x0B, HLE::Wrap<GetThreadPriority>, "GetThreadPriority"}, |
| 380 | {0x0C, HLE::Wrap<SetThreadPriority>, "SetThreadPriority"}, | 433 | {0x0C, HLE::Wrap<SetThreadPriority>, "SetThreadPriority"}, |
| @@ -391,10 +444,10 @@ const HLE::FunctionDef SVC_Table[] = { | |||
| 391 | {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, | 444 | {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, |
| 392 | {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, | 445 | {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, |
| 393 | {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, | 446 | {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, |
| 394 | {0x1A, nullptr, "CreateTimer"}, | 447 | {0x1A, HLE::Wrap<CreateTimer>, "CreateTimer"}, |
| 395 | {0x1B, nullptr, "SetTimer"}, | 448 | {0x1B, HLE::Wrap<SetTimer>, "SetTimer"}, |
| 396 | {0x1C, nullptr, "CancelTimer"}, | 449 | {0x1C, HLE::Wrap<CancelTimer>, "CancelTimer"}, |
| 397 | {0x1D, nullptr, "ClearTimer"}, | 450 | {0x1D, HLE::Wrap<ClearTimer>, "ClearTimer"}, |
| 398 | {0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"}, | 451 | {0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"}, |
| 399 | {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"}, | 452 | {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"}, |
| 400 | {0x20, nullptr, "UnmapMemoryBlock"}, | 453 | {0x20, nullptr, "UnmapMemoryBlock"}, |
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h index ad780818e..5d020a5ba 100644 --- a/src/core/hle/svc.h +++ b/src/core/hle/svc.h | |||
| @@ -20,21 +20,6 @@ struct PageInfo { | |||
| 20 | u32 flags; | 20 | u32 flags; |
| 21 | }; | 21 | }; |
| 22 | 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 | // These are not part of native ThreadContext, but needed by emu | ||
| 34 | u32 reg_15; | ||
| 35 | u32 mode; | ||
| 36 | }; | ||
| 37 | |||
| 38 | enum ResetType { | 23 | enum ResetType { |
| 39 | RESETTYPE_ONESHOT, | 24 | RESETTYPE_ONESHOT, |
| 40 | RESETTYPE_STICKY, | 25 | RESETTYPE_STICKY, |
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index e346e0ad6..3b730a0de 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
| 6 | 6 | ||
| 7 | #include "core/arm/arm_interface.h" | ||
| 8 | |||
| 7 | #include "core/settings.h" | 9 | #include "core/settings.h" |
| 8 | #include "core/core.h" | 10 | #include "core/core.h" |
| 9 | #include "core/mem_map.h" | 11 | #include "core/mem_map.h" |
diff --git a/src/core/system.cpp b/src/core/system.cpp index d6188f055..f4c2df1cd 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp | |||
| @@ -21,11 +21,11 @@ void UpdateState(State state) { | |||
| 21 | 21 | ||
| 22 | void Init(EmuWindow* emu_window) { | 22 | void Init(EmuWindow* emu_window) { |
| 23 | Core::Init(); | 23 | Core::Init(); |
| 24 | CoreTiming::Init(); | ||
| 24 | Memory::Init(); | 25 | Memory::Init(); |
| 25 | HW::Init(); | 26 | HW::Init(); |
| 26 | Kernel::Init(); | 27 | Kernel::Init(); |
| 27 | HLE::Init(); | 28 | HLE::Init(); |
| 28 | CoreTiming::Init(); | ||
| 29 | VideoCore::Init(emu_window); | 29 | VideoCore::Init(emu_window); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| @@ -38,11 +38,11 @@ void RunLoopUntil(u64 global_cycles) { | |||
| 38 | 38 | ||
| 39 | void Shutdown() { | 39 | void Shutdown() { |
| 40 | VideoCore::Shutdown(); | 40 | VideoCore::Shutdown(); |
| 41 | CoreTiming::Shutdown(); | ||
| 42 | HLE::Shutdown(); | 41 | HLE::Shutdown(); |
| 43 | Kernel::Shutdown(); | 42 | Kernel::Shutdown(); |
| 44 | HW::Shutdown(); | 43 | HW::Shutdown(); |
| 45 | Memory::Shutdown(); | 44 | Memory::Shutdown(); |
| 45 | CoreTiming::Shutdown(); | ||
| 46 | Core::Shutdown(); | 46 | Core::Shutdown(); |
| 47 | } | 47 | } |
| 48 | 48 | ||
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 4df3a5e25..29d220e8d 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -87,8 +87,11 @@ void RendererOpenGL::SwapBuffers() { | |||
| 87 | */ | 87 | */ |
| 88 | void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, | 88 | void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, |
| 89 | const TextureInfo& texture) { | 89 | const TextureInfo& texture) { |
| 90 | |||
| 91 | // TODO: Why are active_fb and the valid framebuffer flipped compared to 3dbrew documentation | ||
| 92 | // and GSP definitions? | ||
| 90 | const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress( | 93 | const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress( |
| 91 | framebuffer.active_fb == 1 ? framebuffer.address_left2 : framebuffer.address_left1); | 94 | framebuffer.active_fb == 0 ? framebuffer.address_left2 : framebuffer.address_left1); |
| 92 | 95 | ||
| 93 | LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x", | 96 | LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x", |
| 94 | framebuffer.stride * framebuffer.height, | 97 | framebuffer.stride * framebuffer.height, |