summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/CMakeLists.txt16
-rw-r--r--src/citra_qt/CMakeLists.txt13
-rw-r--r--src/citra_qt/debugger/callstack.cpp3
-rw-r--r--src/citra_qt/debugger/disassembler.cpp1
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.cpp8
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.h4
-rw-r--r--src/common/common.h13
-rw-r--r--src/common/common_paths.h37
-rw-r--r--src/common/file_util.cpp10
-rw-r--r--src/common/file_util.h5
-rw-r--r--src/common/log.h2
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h24
-rw-r--r--src/common/mem_arena.cpp84
-rw-r--r--src/common/mem_arena.h8
-rw-r--r--src/common/platform.h1
-rw-r--r--src/common/swap.h4
-rw-r--r--src/common/thread_queue_list.h218
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/arm_interface.h10
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp19
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h7
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp83
-rw-r--r--src/core/arm/interpreter/arm_interpreter.cpp6
-rw-r--r--src/core/arm/interpreter/arm_interpreter.h4
-rw-r--r--src/core/core.cpp20
-rw-r--r--src/core/core.h20
-rw-r--r--src/core/core_timing.cpp709
-rw-r--r--src/core/core_timing.h77
-rw-r--r--src/core/file_sys/archive_extsavedata.cpp18
-rw-r--r--src/core/file_sys/archive_extsavedata.h2
-rw-r--r--src/core/file_sys/archive_savedata.cpp18
-rw-r--r--src/core/file_sys/archive_savedatacheck.cpp15
-rw-r--r--src/core/file_sys/archive_sdmc.cpp4
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp7
-rw-r--r--src/core/file_sys/archive_systemsavedata.h2
-rw-r--r--src/core/hle/function_wrappers.h8
-rw-r--r--src/core/hle/hle.cpp1
-rw-r--r--src/core/hle/hle.h2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp12
-rw-r--r--src/core/hle/kernel/event.cpp16
-rw-r--r--src/core/hle/kernel/kernel.cpp34
-rw-r--r--src/core/hle/kernel/kernel.h26
-rw-r--r--src/core/hle/kernel/mutex.cpp23
-rw-r--r--src/core/hle/kernel/semaphore.cpp10
-rw-r--r--src/core/hle/kernel/shared_memory.cpp4
-rw-r--r--src/core/hle/kernel/thread.cpp425
-rw-r--r--src/core/hle/kernel/thread.h118
-rw-r--r--src/core/hle/kernel/timer.cpp144
-rw-r--r--src/core/hle/kernel/timer.h47
-rw-r--r--src/core/hle/result.h4
-rw-r--r--src/core/hle/service/apt_u.cpp4
-rw-r--r--src/core/hle/service/cfg/cfg.cpp4
-rw-r--r--src/core/hle/service/fs/archive.cpp26
-rw-r--r--src/core/hle/service/fs/archive.h5
-rw-r--r--src/core/hle/service/fs/fs_user.cpp10
-rw-r--r--src/core/hle/service/gsp_gpu.cpp8
-rw-r--r--src/core/hle/service/hid_user.cpp3
-rw-r--r--src/core/hle/service/ptm_u.cpp8
-rw-r--r--src/core/hle/service/service.cpp17
-rw-r--r--src/core/hle/service/service.h46
-rw-r--r--src/core/hle/service/srv.cpp4
-rw-r--r--src/core/hle/service/y2r_u.cpp17
-rw-r--r--src/core/hle/svc.cpp115
-rw-r--r--src/core/hle/svc.h15
-rw-r--r--src/core/hw/gpu.cpp2
-rw-r--r--src/core/system.cpp4
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp5
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})
16add_executable(citra ${SRCS} ${HEADERS}) 16add_executable(citra ${SRCS} ${HEADERS})
17target_link_libraries(citra core common video_core) 17target_link_libraries(citra core common video_core)
18target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih) 18target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih)
19 19target_link_libraries(citra ${PLATFORM_LIBRARIES})
20if (UNIX)
21 target_link_libraries(citra -pthread)
22endif()
23
24if (APPLE)
25 target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY})
26elseif (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()
31else() # Unix
32 target_link_libraries(citra rt)
33endif()
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()
60add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) 60add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
61target_link_libraries(citra-qt core common video_core qhexedit) 61target_link_libraries(citra-qt core common video_core qhexedit)
62target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) 62target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
63 63target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
64if (UNIX)
65 target_link_libraries(citra-qt -pthread)
66endif()
67
68if (APPLE)
69 target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY})
70elseif (WIN32)
71 target_link_libraries(citra-qt winmm wsock32 ws2_32)
72else() # Unix
73 target_link_libraries(citra-qt rt)
74endif()
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
161void GraphicsFramebufferWidget::OnFramebufferWidthChanged(unsigned int new_value) 161void 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
171void GraphicsFramebufferWidget::OnFramebufferHeightChanged(unsigned int new_value) 171void 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:
62public slots: 62public 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
158inline unsigned long long bswap64(unsigned long long x) { return _byteswap_uint64(x); } 150inline unsigned long long bswap64(unsigned long long x) { return _byteswap_uint64(x); }
159inline unsigned int bswap32(unsigned int x) { return _byteswap_ulong(x); } 151inline unsigned int bswap32(unsigned int x) { return _byteswap_ulong(x); }
160inline unsigned short bswap16(unsigned short x) { return _byteswap_ushort(x); } 152inline unsigned short bswap16(unsigned short x) { return _byteswap_ushort(x); }
161#else 153#else
162inline unsigned long long bswap64(unsigned long long x) { return __loaddoublewordbytereverse(0, &x); }
163inline unsigned int bswap32(unsigned int x) { return __loadwordbytereverse(0, &x); }
164inline unsigned short bswap16(unsigned short x) { return __loadshortbytereverse(0, &x); }
165#endif
166#else
167// TODO: speedup 154// TODO: speedup
168inline unsigned short bswap16(unsigned short x) { return (x << 8) | (x >> 8); } 155inline unsigned short bswap16(unsigned short x) { return (x << 8) | (x >> 8); }
169inline unsigned int bswap32(unsigned int x) { return (x >> 24) | ((x & 0xFF0000) >> 8) | ((x & 0xFF00) << 8) | (x << 24);} 156inline 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 */
79const 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
33void* 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)
99SYSTEM_INFO sysInfo; 95SYSTEM_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
105size_t roundup(size_t x) { 101size_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) {
120void MemArena::GrabLowMemSpace(size_t size) 112void 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()
175void *MemArena::CreateView(s64 offset, size_t size, void *base) 162void *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)
208void MemArena::ReleaseView(void* view, size_t size) 186void 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__
222u8* MemArena::Find4GBBase() 195u8* 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
48private: 40private:
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
9namespace Common { 14namespace Common {
10 15
11template<class IdType> 16template<class T, unsigned int N>
12struct ThreadQueueList { 17struct 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
154private: 112private:
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" 10namespace Core {
11 struct ThreadContext;
12}
11 13
12/// Generic ARM11 CPU interface 14/// Generic ARM11 CPU interface
13class ARM_Interface : NonCopyable { 15class 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
106protected: 110protected:
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
12const static cpu_config_t s_arm11_cpu_info = { 15const static cpu_config_t s_arm11_cpu_info = {
13 "armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE 16 "armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE
14}; 17};
15 18
16ARM_DynCom::ARM_DynCom() : ticks(0) { 19ARM_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
74u64 ARM_DynCom::GetTicks() const { 77u64 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
78void ARM_DynCom::AddTicks(u64 ticks) { 82void 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
82void ARM_DynCom::ExecuteInstructions(int num_instructions) { 88void 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
91void ARM_DynCom::SaveContext(ThreadContext& ctx) { 98void 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
107void ARM_DynCom::LoadContext(const ThreadContext& ctx) { 114void 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
91private: 91private:
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
950typedef 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
950typedef struct ssat_inst { 959typedef 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
2406ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALXY"); } 2415ARM_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
2408ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index) 2435ARM_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
7const static cpu_config_t arm11_cpu_info = { 9const 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
78void ARM_Interpreter::SaveContext(ThreadContext& ctx) { 80void 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
94void ARM_Interpreter::LoadContext(const ThreadContext& ctx) { 96void 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
17namespace Core { 19namespace Core {
18 20
19static u64 last_ticks = 0; ///< Last CPU ticks
20static ARM_Disasm* disasm = nullptr; ///< ARM disassembler
21ARM_Interface* g_app_core = nullptr; ///< ARM11 application core 21ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
22ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core 22ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
23 23
24/// Run the core CPU loop 24/// Run the core CPU loop
25void RunLoop(int tight_loop) { 25void 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() {
49int Init() { 59int 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
70void Shutdown() { 77void 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
9class ARM_Interface;
9 10
10//////////////////////////////////////////////////////////////////////////////////////////////////// 11////////////////////////////////////////////////////////////////////////////////////////////////////
11 12
@@ -16,6 +17,21 @@ enum CPUCore {
16 CPU_OldInterpreter, 17 CPU_OldInterpreter,
17}; 18};
18 19
20struct 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
19extern ARM_Interface* g_app_core; ///< ARM11 application core 35extern ARM_Interface* g_app_core; ///< ARM11 application core
20extern ARM_Interface* g_sys_core; ///< ARM11 system (OS) core 36extern 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
23namespace CoreTiming 23namespace CoreTiming
24{ 24{
25
26struct EventType 25struct 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
37std::vector<EventType> event_types; 36std::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
47typedef LinkedListItem<BaseEvent> Event; 45typedef LinkedListItem<BaseEvent> Event;
48 46
49Event *first; 47Event* first;
50Event *tsFirst; 48Event* ts_first;
51Event *tsLast; 49Event* ts_last;
52 50
53// event pools 51// event pools
54Event *eventPool = 0; 52Event* event_pool = 0;
55Event *eventTsPool = 0; 53Event* event_ts_pool = 0;
56int allocatedTsEvents = 0; 54int allocated_ts_events = 0;
57// Optimization to skip MoveEvents when possible. 55// Optimization to skip MoveEvents when possible.
58std::atomic<u32> hasTsEvents; 56std::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 58int g_slice_length;
61// as we can already reach that structure through a register.
62int slicelength;
63 59
64MEMORY_ALIGNED16(s64) globalTimer; 60s64 global_timer;
65s64 idledCycles; 61s64 idled_cycles;
62s64 last_global_time_ticks;
63s64 last_global_time_us;
66 64
67static std::recursive_mutex externalEventSection; 65static std::recursive_mutex external_event_section;
68 66
69// Warning: not included in save state. 67// Warning: not included in save state.
70void(*advanceCallback)(int cyclesExecuted) = nullptr; 68using AdvanceCallback = void(int cycles_executed);
69AdvanceCallback* advance_callback = nullptr;
70std::vector<MHzChangeCallback> mhz_change_callbacks;
71 71
72void SetClockFrequencyMHz(int cpuMhz) 72void FireMhzChange() {
73{ 73 for (auto callback : mhz_change_callbacks)
74 g_clock_rate_arm11 = cpuMhz * 1000000; 74 callback();
75}
76
77void 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
78int GetClockFrequencyMHz() 89int GetClockFrequencyMHz() {
79{
80 return g_clock_rate_arm11 / 1000000; 90 return g_clock_rate_arm11 / 1000000;
81} 91}
82 92
93u64 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
84Event* GetNewEvent() 100Event* 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
94Event* GetNewTsEvent() 109Event* 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
106void FreeEvent(Event* ev) 120void FreeEvent(Event* event) {
107{ 121 event->next = event_pool;
108 ev->next = eventPool; 122 event_pool = event;
109 eventPool = ev;
110} 123}
111 124
112void FreeTsEvent(Event* ev) 125void 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
119int RegisterEvent(const char *name, TimedCallback callback) 131int 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
125void AntiCrashCallback(u64 userdata, int cyclesLate) 136void 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
131void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback) 141void 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
139void UnregisterAllEvents() 148void 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
146void Init() 154void 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
155void Shutdown() 165void 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
177u64 GetTicks() 184u64 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
184u64 GetIdleTicks() 188u64 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.
192void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata) 195void 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.
211void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) 213void 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
222void ClearPendingEvents() 223void 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
232void AddEventToQueue(Event* ne) 231void 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 246void 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;
253void 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. 254s64 UnscheduleEvent(int event_type, u64 userdata) {
263s64 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
307s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) 291s64 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.
359void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted)) 336void RegisterAdvanceCallback(AdvanceCallback* callback) {
360{ 337 advance_callback = callback;
361 advanceCallback = callback;
362} 338}
363 339
364bool IsScheduled(int event_type) 340void RegisterMHzChangeCallback(MHzChangeCallback callback) {
365{ 341 mhz_change_callbacks.push_back(callback);
342}
343
344bool 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
377void RemoveEvent(int event_type) 356void 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
414void RemoveThreadsafeEvent(int event_type) 384void 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
459void RemoveAllEvents(int event_type) 420void 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
466void ProcessFifoWaitEvents() 426void 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
486void MoveEvents() 439void 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
511void Advance() 461void 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
538void 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
548void Idle(int maxIdle) 470void 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
577std::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
599void Event_DoState(PointerWrap &p, BaseEvent *ev) 498void 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
604void DoState(PointerWrap &p) 506void 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
531std::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
22class PointerWrap; 24#include "common/common.h"
23 25
24extern int g_clock_rate_arm11; 26extern 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
58namespace CoreTiming { 60inline u64 cyclesToMs(s64 cycles) {
61 return cycles / (g_clock_rate_arm11 / 1000);
62}
59 63
64namespace CoreTiming
65{
60void Init(); 66void Init();
61void Shutdown(); 67void Shutdown();
62 68
63typedef void(*TimedCallback)(u64 userdata, int cyclesLate); 69typedef void(*MHzChangeCallback)();
70typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
64 71
65u64 GetTicks(); 72u64 GetTicks();
66u64 GetIdleTicks(); 73u64 GetIdleTicks();
67 74u64 GetGlobalTimeUs();
68// Returns the event_type identifier. 75
69int 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 */
82int RegisterEvent(const char* name, TimedCallback callback);
83/// For save states.
71void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback); 84void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback);
72void UnregisterAllEvents(); 85void 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.
76void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata = 0); 89/**
77void 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 */
97void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata = 0);
98
99void ScheduleEvent_Threadsafe(s64 cycles_into_future, int event_type, u64 userdata = 0);
78void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata = 0); 100void 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 */
79s64 UnscheduleEvent(int event_type, u64 userdata); 108s64 UnscheduleEvent(int event_type, u64 userdata);
109
80s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata); 110s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata);
81 111
82void RemoveEvent(int event_type); 112void RemoveEvent(int event_type);
83void RemoveThreadsafeEvent(int event_type); 113void RemoveThreadsafeEvent(int event_type);
84void RemoveAllEvents(int event_type); 114void RemoveAllEvents(int event_type);
85bool IsScheduled(int event_type); 115bool IsScheduled(int event_type);
116/// Runs any pending events and updates downcount for the next slice of cycles
86void Advance(); 117void Advance();
87void MoveEvents(); 118void MoveEvents();
88void ProcessFifoWaitEvents(); 119void ProcessFifoWaitEvents();
120void 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.
91void Idle(int maxIdle = 0); 123void 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.
94void ClearPendingEvents(); 126void ClearPendingEvents();
95 127
96void LogPendingEvents(); 128void LogPendingEvents();
97 129
98// Warning: not included in save states. 130/// Warning: not included in save states.
99void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted)); 131void RegisterAdvanceCallback(void(*callback)(int cycles_executed));
132void RegisterMHzChangeCallback(MHzChangeCallback callback);
100 133
101std::string GetScheduledEventsSummary(); 134std::string GetScheduledEventsSummary();
102 135
103void DoState(PointerWrap &p); 136void SetClockFrequencyMHz(int cpu_mhz);
104
105void SetClockFrequencyMHz(int cpuMhz);
106int GetClockFrequencyMHz(); 137int GetClockFrequencyMHz();
107extern int slicelength; 138extern 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 {
19static std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) { 20static 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
28Archive_ExtSaveData::Archive_ExtSaveData(const std::string& mount_point) 28static 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
36Archive_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
33bool Archive_ExtSaveData::Initialize() { 41bool 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
18class Archive_ExtSaveData final : public DiskArchive { 18class Archive_ExtSaveData final : public DiskArchive {
19public: 19public:
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
17namespace FileSys { 18namespace FileSys {
18 19
19Archive_SaveData::Archive_SaveData(const std::string& mount_point) 20static 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
25static 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
31Archive_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
24ResultCode Archive_SaveData::Open(const Path& path) { 36ResultCode 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
12namespace FileSys { 13namespace FileSys {
13 14
14Archive_SaveDataCheck::Archive_SaveDataCheck(const std::string& mount_loc) : mount_point(mount_loc) { 15static 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
19static 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
24Archive_SaveDataCheck::Archive_SaveDataCheck(const std::string& nand_directory) :
25 mount_point(GetSaveDataCheckContainerPath(nand_directory)) {
15} 26}
16 27
17ResultCode Archive_SaveDataCheck::Open(const Path& path) { 28ResultCode 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
17namespace FileSys { 17namespace FileSys {
18 18
19Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) { 19Archive_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
23bool Archive_SDMC::Initialize() { 23bool 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
26static 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
25Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id) 30Archive_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 @@
15namespace FileSys { 15namespace 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>
20class Archive_SystemSaveData final : public DiskArchive { 18class Archive_SystemSaveData final : public DiskArchive {
21public: 19public:
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
140template<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
32ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { 32ResultCode 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 */
55ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { 55ResultCode 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 */
69ResultCode SetEventLocked(const Handle handle, const bool locked) { 69ResultCode 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 */
84ResultCode SignalEvent(const Handle handle) { 84ResultCode 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 */
112ResultCode ClearEvent(Handle handle) { 114ResultCode 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
13namespace Kernel { 15namespace Kernel {
14 16
15Handle g_main_thread = 0; 17SharedPtr<Thread> g_main_thread = nullptr;
16HandleTable g_handle_table; 18HandleTable g_handle_table;
17u64 g_program_id = 0; 19u64 g_program_id = 0;
18 20
@@ -21,7 +23,7 @@ HandleTable::HandleTable() {
21 Clear(); 23 Clear();
22} 24}
23 25
24ResultVal<Handle> HandleTable::Create(Object* obj) { 26ResultVal<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
49ResultVal<Handle> HandleTable::Duplicate(Handle handle) { 52ResultVal<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
58ResultCode HandleTable::Close(Handle handle) { 61ResultCode 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
80Object* HandleTable::GetGeneric(Handle handle) const { 82SharedPtr<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 {
95void HandleTable::Clear() { 96void 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
106void Init() { 105void Init() {
107 Kernel::ThreadingInit(); 106 Kernel::ThreadingInit();
107 Kernel::TimersInit();
108} 108}
109 109
110/// Shutdown the kernel 110/// Shutdown the kernel
111void Shutdown() { 111void 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
17namespace Kernel { 19namespace Kernel {
18 20
21class Thread;
22
19// TODO: Verify code 23// TODO: Verify code
20const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel, 24const 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
44enum { 49enum {
@@ -49,7 +54,7 @@ class HandleTable;
49 54
50class Object : NonCopyable { 55class Object : NonCopyable {
51 friend class HandleTable; 56 friend class HandleTable;
52 u32 handle; 57 u32 handle = INVALID_HANDLE;
53public: 58public:
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
77inline void intrusive_ptr_add_ref(Object* object) { 82inline 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
92template <typename T>
93using 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
191extern HandleTable g_handle_table; 199extern HandleTable g_handle_table;
192extern Handle g_main_thread; 200extern 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 */
43void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) { 43void 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
48bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { 48bool 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 */
117ResultCode ReleaseMutex(Handle handle) { 124ResultCode 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) {
168ResultVal<bool> Mutex::WaitSynchronization() { 175ResultVal<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
72ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { 72ResultCode 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
74ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { 74ResultVal<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
21namespace Kernel { 23namespace Kernel {
22 24
23class Thread : public Kernel::Object { 25ResultVal<bool> Thread::WaitSynchronization() {
24public: 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.
75static std::vector<Handle> thread_queue; 39static std::vector<SharedPtr<Thread>> thread_list;
76 40
77// Lists only ready thread ids. 41// Lists only ready thread ids.
78static Common::ThreadQueueList<Handle> thread_ready_queue; 42static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> thread_ready_queue;
79 43
80static Handle current_thread_handle;
81static Thread* current_thread; 44static Thread* current_thread;
82 45
83static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup 46static 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
91Handle GetCurrentThreadHandle() {
92 return GetCurrentThread()->GetHandle();
93}
94
95/// Sets the current thread
96inline void SetCurrentThread(Thread* t) {
97 current_thread = t;
98 current_thread_handle = t->GetHandle();
99}
100
101/// Saves the current CPU context
102void SaveContext(ThreadContext& ctx) {
103 Core::g_app_core->SaveContext(ctx);
104}
105
106/// Loads a CPU context
107void LoadContext(ThreadContext& ctx) {
108 Core::g_app_core->LoadContext(ctx);
109}
110
111/// Resets a thread 53/// Resets a thread
112void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { 54static 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
134void ChangeReadyState(Thread* t, bool ready) { 76static 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
156static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) { 97static 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
161static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { 102static 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
166ResultCode StopThread(Handle handle, const char* reason) { 107void 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
192void ChangeThreadState(Thread* t, ThreadStatus new_status) { 126static 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
207Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { 141Thread* 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
234void ArbitrateAllThreads(u32 arbiter, u32 address) { 168void 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)
246void CallThread(Thread* t) { 178static 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
255void SwitchContext(Thread* t) { 187static 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
279Thread* NextThread() { 211static 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
294void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { 226void 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
301void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) { 233void 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
239static int ThreadWakeupEventType = -1;
240
241/// Callback that will wake up the thread it was scheduled for
242static 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
254void 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"
307void ResumeThreadFromWait(Handle handle) { 265void 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
320void DebugThreadQueue() { 278static 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 292ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority,
336Thread* 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
366Handle 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);
401ResultVal<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
409ResultCode SetThreadPriority(Handle handle, s32 priority) { 349void 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 374Handle SetupIdleThread() {
448Handle 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); 386SharedPtr<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
500ResultCode 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
513void ThreadingInit() { 431void 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
517void ThreadingShutdown() { 436void 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
48namespace Kernel { 53namespace Kernel {
49 54
50/// Creates a new thread - wrapper for external user 55class Thread : public Kernel::Object {
51Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, 56public:
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; }
55Handle 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;
58void 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; }
61ResultCode 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; }
69ResultCode 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;
72void 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
109private:
110 Thread() = default;
111};
112
113/// Sets up the primary application thread
114SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size);
115
116/// Reschedules to the next available thread (call after current thread is suspended)
117void Reschedule();
73 118
74/// Arbitrate the highest priority thread that is waiting 119/// Arbitrate the highest priority thread that is waiting
75Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address); 120Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address);
76 121
77/// Arbitrate all threads currently waiting... 122/// Arbitrate all threads currently waiting...
78void ArbitrateAllThreads(u32 arbiter, u32 address); 123void ArbitrateAllThreads(Object* arbiter, u32 address);
79 124
80/// Gets the current thread handle 125/// Gets the current thread
81Handle GetCurrentThreadHandle(); 126Thread* 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 */
133void 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 */
88void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); 140void 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 */
96void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address); 148void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address);
97 149
98/// Put current thread in a wait state - on WaitSynchronization
99void WaitThread_Synchronization();
100 150
101/// Get the priority of the thread specified by handle
102ResultVal<u32> GetThreadPriority(const Handle handle);
103
104/// Set the priority of the thread specified by handle
105ResultCode 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 */
158Handle SetupIdleThread();
107/// Initialize threading 159/// Initialize threading
108void ThreadingInit(); 160void 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
14namespace Kernel {
15
16class Timer : public Object {
17public:
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 */
50Timer* 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
63ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name) {
64 CreateTimer(*handle, reset_type, name);
65 return RESULT_SUCCESS;
66}
67
68ResultCode 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
79static int TimerCallbackEventType = -1;
80
81/// The timer callback event, called when a timer is fired
82static 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
113ResultCode 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
127ResultCode 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
137void TimersInit() {
138 TimerCallbackEventType = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
139}
140
141void 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
12namespace Kernel {
13
14/**
15 * Cancels a timer
16 * @param handle Handle of the timer to cancel
17 */
18ResultCode 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 */
26ResultCode SetTimer(Handle handle, s64 initial, s64 interval);
27
28/**
29 * Clears a timer
30 * @param handle Handle of the timer to clear
31 */
32ResultCode 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 */
41ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name="Unknown");
42
43/// Initializes the required variables for timers
44void TimersInit();
45/// Tears down the timer variables
46void 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
59void GetLockHandle(Service::Interface* self) { 57void 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 */
196void GetSharedFont(Service::Interface* self) { 194void 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() {
161void CFGInit() { 161void 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
40const std::string SYSTEM_ID = "00000000000000000000000000000000";
41const std::string SDCARD_ID = "00000000000000000000000000000000";
42
39namespace Service { 43namespace Service {
40namespace FS { 44namespace FS {
41 45
@@ -432,11 +436,11 @@ ResultCode FormatSaveData() {
432void ArchiveInit() { 436void 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
14extern const std::string SYSTEM_ID;
15/// The scrambled SD card CID, also known as ID1
16extern const std::string SDCARD_ID;
17
13namespace Service { 18namespace Service {
14namespace FS { 19namespace 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.
330static void TriggerCmdReqQueue(Service::Interface* self) { 333static 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
169const Interface::FunctionInfo FunctionTable[] = { 168const 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
49Manager::Manager() {
50}
51
52Manager::~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)
59void Manager::AddService(Interface* service) { 49void 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
66void Manager::DeleteService(const std::string& port_name) { 55void 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
74Interface* Manager::FetchFromHandle(Handle handle) { 61Interface* 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
79Interface* Manager::FetchFromPortName(const std::string& port_name) { 66Interface* 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
36public: 52public:
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
116class Manager { 125class Manager {
117
118public: 126public:
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
135private: 139private:
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 {
14static Handle g_event_handle = 0; 14static Handle g_event_handle = 0;
15 15
16static void Initialize(Service::Interface* self) { 16static 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
24static void GetProcSemaphore(Service::Interface* self) { 22static 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
13namespace Y2R_U { 13namespace 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 */
21static 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
15const Interface::FunctionInfo FunctionTable[] = { 30const 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
28using Kernel::SharedPtr;
29
26namespace SVC { 30namespace SVC {
27 31
28enum ControlMemoryOperation { 32enum 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
94static Result SendSyncRequest(Handle handle) { 98static 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
140static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, 144static 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)
182static Result CreateAddressArbiter(u32* arbiter) { 187static 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
222static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { 226static 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
244static u32 ExitThread() { 259static 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
255static Result GetThreadPriority(s32* priority, Handle handle) { 267static 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
264static Result SetThreadPriority(Handle handle, s32 priority) { 277static 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.
284static Result GetThreadId(u32* thread_id, Handle handle) { 302static 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
365static 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
373static 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
379static 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
385static 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
342static void SleepThread(s64 nanoseconds) { 391static 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
23struct 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
38enum ResetType { 23enum 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
22void Init(EmuWindow* emu_window) { 22void 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
39void Shutdown() { 39void 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 */
88void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, 88void 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,