summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis-deps.sh2
-rw-r--r--.travis.yml5
-rw-r--r--CMakeLists.txt23
-rw-r--r--appveyor.yml17
m---------externals/boost0
-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
73 files changed, 1402 insertions, 1287 deletions
diff --git a/.travis-deps.sh b/.travis-deps.sh
index 2a0f6b284..bd09da0d0 100644
--- a/.travis-deps.sh
+++ b/.travis-deps.sh
@@ -24,8 +24,6 @@ if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then
24 curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \ 24 curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \
25 | sudo tar -xz -C /usr/local --strip-components=1 25 | sudo tar -xz -C /usr/local --strip-components=1
26elif [ "$TRAVIS_OS_NAME" = osx ]; then 26elif [ "$TRAVIS_OS_NAME" = osx ]; then
27 export HOMEBREW_CACHE="$PWD/.homebrew-cache"
28 mkdir -p "$HOMEBREW_CACHE"
29 brew tap homebrew/versions 27 brew tap homebrew/versions
30 brew install qt5 glfw3 pkgconfig 28 brew install qt5 glfw3 pkgconfig
31fi 29fi
diff --git a/.travis.yml b/.travis.yml
index 8c5aceb7c..1cb369d5b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,11 +4,6 @@ os:
4 4
5language: cpp 5language: cpp
6 6
7cache:
8 apt: true
9 directories:
10 - .homebrew-cache
11
12before_install: 7before_install:
13 - sh .travis-deps.sh 8 - sh .travis-deps.sh
14 9
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 884520cef..20a5a011a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,8 +5,8 @@ cmake_minimum_required(VERSION 2.8.11)
5project(citra) 5project(citra)
6 6
7if (NOT MSVC) 7if (NOT MSVC)
8 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes") 8 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes -pthread")
9 add_definitions(-pthread) 9 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
10else() 10else()
11 # Silence deprecation warnings 11 # Silence deprecation warnings
12 add_definitions(/D_CRT_SECURE_NO_WARNINGS) 12 add_definitions(/D_CRT_SECURE_NO_WARNINGS)
@@ -17,14 +17,14 @@ else()
17 # As far as I can tell, there's no way to override the CMake defaults while leaving user 17 # As far as I can tell, there's no way to override the CMake defaults while leaving user
18 # changes intact, so we'll just clobber everything and say sorry. 18 # changes intact, so we'll just clobber everything and say sorry.
19 message(STATUS "Cache compiler flags ignored, please edit CMakeFiles.txt to change the flags.") 19 message(STATUS "Cache compiler flags ignored, please edit CMakeFiles.txt to change the flags.")
20 # /MP - Multi-threaded compilation
20 # /MD - Multi-threaded runtime 21 # /MD - Multi-threaded runtime
21 # /Ox - Full optimization 22 # /Ox - Full optimization
22 # /Oi - Use intrinsic functions
23 # /Oy- - Don't omit frame pointer 23 # /Oy- - Don't omit frame pointer
24 # /GR- - Disable RTTI 24 # /GR- - Disable RTTI
25 # /GS- - No stack buffer overflow checks 25 # /GS- - No stack buffer overflow checks
26 # /EHsc - C++-only exception handling semantics 26 # /EHsc - C++-only exception handling semantics
27 set(optimization_flags "/MD /Ox /Oi /Oy- /DNDEBUG /GR- /GS- /EHsc") 27 set(optimization_flags "/MP /MD /Ox /Oy- /GR- /GS- /EHsc")
28 # /Zi - Output debugging information 28 # /Zi - Output debugging information
29 # /Zo - enahnced debug info for optimized builds 29 # /Zo - enahnced debug info for optimized builds
30 set(CMAKE_C_FLAGS_RELEASE "${optimization_flags} /Zi" CACHE STRING "" FORCE) 30 set(CMAKE_C_FLAGS_RELEASE "${optimization_flags} /Zi" CACHE STRING "" FORCE)
@@ -32,7 +32,11 @@ else()
32 set(CMAKE_C_FLAGS_RELWITHDEBINFO "${optimization_flags} /Zi /Zo" CACHE STRING "" FORCE) 32 set(CMAKE_C_FLAGS_RELWITHDEBINFO "${optimization_flags} /Zi /Zo" CACHE STRING "" FORCE)
33 set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${optimization_flags} /Zi /Zo" CACHE STRING "" FORCE) 33 set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${optimization_flags} /Zi /Zo" CACHE STRING "" FORCE)
34endif() 34endif()
35
35add_definitions(-DSINGLETHREADED) 36add_definitions(-DSINGLETHREADED)
37# CMake seems to only define _DEBUG on Windows
38set_property(DIRECTORY APPEND PROPERTY
39 COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
36 40
37find_package(PNG QUIET) 41find_package(PNG QUIET)
38if (PNG_FOUND) 42if (PNG_FOUND)
@@ -106,10 +110,17 @@ if (ENABLE_GLFW)
106endif() 110endif()
107 111
108IF (APPLE) 112IF (APPLE)
109 # CoreFoundation is required only on OSX 113 FIND_LIBRARY(COCOA_LIBRARY Cocoa) # Umbrella framework for everything GUI-related
110 FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation) 114 FIND_LIBRARY(IOKIT_LIBRARY IOKit) # GLFW dependency
115 FIND_LIBRARY(COREVIDEO_LIBRARY CoreVideo) # GLFW dependency
116 set(PLATFORM_LIBRARIES iconv ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
117
111 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") 118 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
112 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++") 119 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
120ELSEIF(WIN32)
121 set(PLATFORM_LIBRARIES winmm ws2_32)
122ELSE()
123 set(PLATFORM_LIBRARIES rt)
113ENDIF (APPLE) 124ENDIF (APPLE)
114 125
115option(ENABLE_QT "Enable the Qt frontend" ON) 126option(ENABLE_QT "Enable the Qt frontend" ON)
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 000000000..83d3b900e
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,17 @@
1# it seems that Qt is only installed by default on unstable os at the moment
2os: unstable
3
4# shallow clone
5clone_depth: 1
6
7environment:
8 QTDIR: C:\Qt\5.4\msvc2013_opengl
9
10install:
11 - git submodule update --init --recursive
12
13before_build:
14 - mkdir build
15 - cd build
16 - cmake ..
17 - cd ..
diff --git a/externals/boost b/externals/boost
Subproject 97052c28acb141dbf3c5e14114af99045344b69 Subproject a1afc91d3aaa3da06bdbc13c78613e146665340
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,