summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/common/common.h6
-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/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-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/arm/arm_interface.h2
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp8
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp75
-rw-r--r--src/core/core.cpp13
-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/kernel/kernel.cpp2
-rw-r--r--src/core/hle/kernel/thread.cpp25
-rw-r--r--src/core/hle/kernel/thread.h11
-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/gsp_gpu.cpp5
-rw-r--r--src/core/hle/service/ptm_u.cpp6
-rw-r--r--src/core/hle/service/service.cpp14
-rw-r--r--src/core/hle/service/service.h15
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp5
35 files changed, 673 insertions, 770 deletions
diff --git a/src/common/common.h b/src/common/common.h
index ba33373ae..bf48ae667 100644
--- a/src/common/common.h
+++ b/src/common/common.h
@@ -154,16 +154,10 @@ enum EMUSTATE_CHANGE
#ifdef _MSC_VER
-#ifndef _XBOX
inline unsigned long long bswap64(unsigned long long x) { return _byteswap_uint64(x); }
inline unsigned int bswap32(unsigned int x) { return _byteswap_ulong(x); }
inline unsigned short bswap16(unsigned short x) { return _byteswap_ushort(x); }
#else
-inline unsigned long long bswap64(unsigned long long x) { return __loaddoublewordbytereverse(0, &x); }
-inline unsigned int bswap32(unsigned int x) { return __loadwordbytereverse(0, &x); }
-inline unsigned short bswap16(unsigned short x) { return __loadshortbytereverse(0, &x); }
-#endif
-#else
// TODO: speedup
inline unsigned short bswap16(unsigned short x) { return (x << 8) | (x >> 8); }
inline unsigned int bswap32(unsigned int x) { return (x >> 24) | ((x & 0xFF0000) >> 8) | ((x & 0xFF00) << 8) | (x << 24);}
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index e692e5492..0ecf2d9de 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -35,26 +35,23 @@
#define JAP_DIR "JAP"
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
-#define CONFIG_DIR "config"
-#define GAMECONFIG_DIR "game_config"
-#define MAPS_DIR "maps"
-#define CACHE_DIR "cache"
-#define SDMC_DIR "sdmc"
-#define EXTSAVEDATA_DIR "extsavedata"
-#define SAVEDATA_DIR "savedata"
-#define SAVEDATACHECK_DIR "savedatacheck"
-#define SYSDATA_DIR "sysdata"
-#define SYSSAVEDATA_DIR "syssavedata"
-#define SHADERCACHE_DIR "shader_cache"
-#define STATESAVES_DIR "state_saves"
-#define SCREENSHOTS_DIR "screenShots"
-#define DUMP_DIR "dump"
-#define DUMP_TEXTURES_DIR "textures"
-#define DUMP_FRAMES_DIR "frames"
-#define DUMP_AUDIO_DIR "audio"
-#define LOGS_DIR "logs"
-#define SHADERS_DIR "shaders"
-#define SYSCONF_DIR "sysconf"
+#define CONFIG_DIR "config"
+#define GAMECONFIG_DIR "game_config"
+#define MAPS_DIR "maps"
+#define CACHE_DIR "cache"
+#define SDMC_DIR "sdmc"
+#define NAND_DIR "nand"
+#define SYSDATA_DIR "sysdata"
+#define SHADERCACHE_DIR "shader_cache"
+#define STATESAVES_DIR "state_saves"
+#define SCREENSHOTS_DIR "screenShots"
+#define DUMP_DIR "dump"
+#define DUMP_TEXTURES_DIR "textures"
+#define DUMP_FRAMES_DIR "frames"
+#define DUMP_AUDIO_DIR "audio"
+#define LOGS_DIR "logs"
+#define SHADERS_DIR "shaders"
+#define SYSCONF_DIR "sysconf"
// Filenames
// 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
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
- paths[D_EXTSAVEDATA] = paths[D_USER_IDX] + EXTSAVEDATA_DIR DIR_SEP;
- paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
- paths[D_SAVEDATACHECK_IDX] = paths[D_USER_IDX] + SAVEDATACHECK_DIR DIR_SEP;
+ paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
- paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP;
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
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
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
- paths[D_EXTSAVEDATA] = paths[D_USER_IDX] + EXTSAVEDATA_DIR DIR_SEP;
- paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
- paths[D_SAVEDATACHECK_IDX] = paths[D_USER_IDX] + SAVEDATACHECK_DIR DIR_SEP;
- paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP;
+ paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
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 {
D_STATESAVES_IDX,
D_SCREENSHOTS_IDX,
D_SDMC_IDX,
- D_EXTSAVEDATA,
- D_SAVEDATA_IDX,
- D_SAVEDATACHECK_IDX,
+ D_NAND_IDX,
D_SYSDATA_IDX,
- D_SYSSAVEDATA_IDX,
D_HIRESTEXTURES_IDX,
D_DUMP_IDX,
D_DUMPFRAMES_IDX,
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;
SUB(Common, Memory) \
CLS(Core) \
SUB(Core, ARM11) \
+ SUB(Core, Timing) \
CLS(Config) \
CLS(Debug) \
SUB(Debug, Emulated) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 06b99b07a..bda3d633a 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -41,6 +41,7 @@ enum class Class : ClassType {
Common_Memory, ///< Memory mapping and management functions
Core, ///< LLE emulation core
Core_ARM11, ///< ARM11 CPU core
+ Core_Timing, ///< CoreTiming functions
Config, ///< Emulator configuration (including commandline)
Debug, ///< Debugging tools
Debug_Emulated, ///< Debug messages from the emulated programs
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 @@
#endif
#endif
-#ifdef IOS
-void* globalbase = nullptr;
-#endif
-
#ifdef ANDROID
// Hopefully this ABI will never change...
@@ -95,7 +91,7 @@ int ashmem_unpin_region(int fd, size_t offset, size_t len)
#endif // Android
-#if defined(_WIN32) && !defined(_XBOX)
+#if defined(_WIN32)
SYSTEM_INFO sysInfo;
#endif
@@ -103,11 +99,7 @@ SYSTEM_INFO sysInfo;
// Windows mappings need to be on 64K boundaries, due to Alpha legacy.
#ifdef _WIN32
size_t roundup(size_t x) {
-#ifndef _XBOX
int gran = sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x10000;
-#else
- int gran = 0x10000; // 64k in 360
-#endif
return (x + gran - 1) & ~(gran - 1);
}
#else
@@ -120,10 +112,8 @@ size_t roundup(size_t x) {
void MemArena::GrabLowMemSpace(size_t size)
{
#ifdef _WIN32
-#ifndef _XBOX
hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, (DWORD)(size), nullptr);
GetSystemInfo(&sysInfo);
-#endif
#elif defined(ANDROID)
// Use ashmem so we don't have to allocate a file on disk!
fd = ashmem_create_region("PPSSPP_RAM", size);
@@ -163,9 +153,6 @@ void MemArena::ReleaseSpace()
#ifdef _WIN32
CloseHandle(hMemoryMapping);
hMemoryMapping = 0;
-#elif defined(__SYMBIAN32__)
- memmap->Close();
- delete memmap;
#else
close(fd);
#endif
@@ -175,22 +162,13 @@ void MemArena::ReleaseSpace()
void *MemArena::CreateView(s64 offset, size_t size, void *base)
{
#ifdef _WIN32
-#ifdef _XBOX
- size = roundup(size);
- // use 64kb pages
- void * ptr = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
- return ptr;
-#else
size = roundup(size);
void *ptr = MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base);
return ptr;
-#endif
#else
void *retval = mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED |
// Do not sync memory to underlying file. Linux has this by default.
-#ifdef BLACKBERRY
- MAP_NOSYNCFILE |
-#elif defined(__FreeBSD__)
+#ifdef __FreeBSD__
MAP_NOSYNC |
#endif
((base == nullptr) ? 0 : MAP_FIXED), fd, offset);
@@ -208,17 +186,12 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base)
void MemArena::ReleaseView(void* view, size_t size)
{
#ifdef _WIN32
-#ifndef _XBOX
UnmapViewOfFile(view);
-#endif
-#elif defined(__SYMBIAN32__)
- memmap->Decommit(((int)view - (int)memmap->Base()) & 0x3FFFFFFF, size);
#else
munmap(view, size);
#endif
}
-#ifndef __SYMBIAN32__
u8* MemArena::Find4GBBase()
{
#ifdef _M_X64
@@ -242,20 +215,6 @@ u8* MemArena::Find4GBBase()
}
return base;
#else
-#ifdef IOS
- void* base = nullptr;
- if (globalbase == nullptr){
- base = mmap(0, 0x08000000, PROT_READ | PROT_WRITE,
- MAP_ANON | MAP_SHARED, -1, 0);
- if (base == MAP_FAILED) {
- PanicAlert("Failed to map 128 MB of memory space: %s", strerror(errno));
- return 0;
- }
- munmap(base, 0x08000000);
- globalbase = base;
- }
- else{ base = globalbase; }
-#else
void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_SHARED, -1, 0);
if (base == MAP_FAILED) {
@@ -263,12 +222,10 @@ u8* MemArena::Find4GBBase()
return 0;
}
munmap(base, 0x10000000);
-#endif
return static_cast<u8*>(base);
#endif
#endif
}
-#endif
// 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
size_t position = 0;
size_t last_position = 0;
-#if defined(_XBOX)
- void *ptr;
-#endif
-
// Zero all the pointers to be sure.
for (int i = 0; i < num_views; i++)
{
@@ -308,18 +261,6 @@ static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32
position = last_position;
}
else {
-#ifdef __SYMBIAN32__
- *(view.out_ptr_low) = (u8*)((int)arena->memmap->Base() + view.virtual_address);
- arena->memmap->Commit(view.virtual_address & 0x3FFFFFFF, view.size);
- }
- *(view.out_ptr) = (u8*)((int)arena->memmap->Base() + view.virtual_address & 0x3FFFFFFF);
-#elif defined(_XBOX)
- *(view.out_ptr_low) = (u8*)(base + view.virtual_address);
- //arena->memmap->Commit(view.virtual_address & 0x3FFFFFFF, view.size);
- ptr = VirtualAlloc(base + (view.virtual_address & 0x3FFFFFFF), view.size, MEM_COMMIT, PAGE_READWRITE);
- }
- *(view.out_ptr) = (u8*)base + (view.virtual_address & 0x3FFFFFFF);
-#else
*(view.out_ptr_low) = (u8*)arena->CreateView(position, view.size);
if (!*view.out_ptr_low)
goto bail;
@@ -340,7 +281,6 @@ static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32
}
#endif
-#endif
last_position = position;
position += roundup(view.size);
}
@@ -389,9 +329,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
total_mem += roundup(views[i].size);
}
// Grab some pagefile backed memory out of the void ...
-#ifndef __SYMBIAN32__
arena->GrabLowMemSpace(total_mem);
-#endif
// Now, create views in high memory where there's plenty of space.
#ifdef _M_X64
@@ -403,15 +341,6 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
return 0;
}
-#elif defined(_XBOX)
- // Reserve 256MB
- u8 *base = (u8*)VirtualAlloc(0, 0x10000000, MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE);
- if (!Memory_TryBase(base, views, num_views, flags, arena))
- {
- PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
- exit(0);
- return 0;
- }
#elif defined(_WIN32)
// Try a whole range of possible bases. Return once we got a valid one.
u32 max_base_addr = 0x7FFF0000 - 0x10000000;
@@ -428,15 +357,6 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
break;
}
}
-#elif defined(__SYMBIAN32__)
- arena->memmap = new RChunk();
- arena->memmap->CreateDisconnectedLocal(0, 0, 0x10000000);
- if (!Memory_TryBase(arena->memmap->Base(), views, num_views, flags, arena))
- {
- PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
- return 0;
- }
- u8* base = arena->memmap->Base();
#else
// Linux32 is fine with the x64 method, although limited to 32-bit with no automirrors.
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 @@
#include <windows.h>
#endif
-#ifdef __SYMBIAN32__
-#include <e32std.h>
-#endif
-
#include "common/common.h"
// This class lets you create a block of anonymous RAM, and then arbitrarily map views into it.
@@ -39,12 +35,8 @@ public:
void *CreateView(s64 offset, size_t size, void *base = 0);
void ReleaseView(void *view, size_t size);
-#ifdef __SYMBIAN32__
- RChunk* memmap;
-#else
// This only finds 1 GB in 32-bit
static u8 *Find4GBBase();
-#endif
private:
#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 @@
#define PLATFORM_MACOSX 2
#define PLATFORM_LINUX 3
#define PLATFORM_ANDROID 4
-#define PLATFORM_IOS 5
////////////////////////////////////////////////////////////////////////////////////////////////////
// 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 @@
// MSVC
#elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN)
-#ifdef _XBOX
-#define COMMON_BIG_ENDIAN 1
-#else
#define COMMON_LITTLE_ENDIAN 1
-#endif
#endif
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 @@
#pragma once
+#include <array>
+#include <deque>
+
+#include <boost/range/algorithm_ext/erase.hpp>
+
#include "common/common.h"
namespace Common {
-template<class IdType>
+template<class T, unsigned int N>
struct ThreadQueueList {
- // Number of queues (number of priority levels starting at 0.)
- static const int NUM_QUEUES = 128;
+ // TODO(yuriks): If performance proves to be a problem, the std::deques can be replaced with
+ // (dynamically resizable) circular buffers to remove their overhead when
+ // inserting and popping.
- // Initial number of threads a single queue can handle.
- static const int INITIAL_CAPACITY = 32;
+ typedef unsigned int Priority;
- struct Queue {
- // Next ever-been-used queue (worse priority.)
- Queue *next;
- // First valid item in data.
- int first;
- // One after last valid item in data.
- int end;
- // A too-large array with room on the front and end.
- IdType *data;
- // Size of data array.
- int capacity;
- };
+ // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
+ static const Priority NUM_QUEUES = N;
ThreadQueueList() {
- memset(queues, 0, sizeof(queues));
- first = invalid();
- }
-
- ~ThreadQueueList() {
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- if (queues[i].data != nullptr)
- free(queues[i].data);
- }
+ first = nullptr;
}
// Only for debugging, returns priority level.
- int contains(const IdType uid) {
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- if (queues[i].data == nullptr)
- continue;
-
- Queue *cur = &queues[i];
- for (int j = cur->first; j < cur->end; ++j)
- {
- if (cur->data[j] == uid)
- return i;
+ Priority contains(const T& uid) {
+ for (Priority i = 0; i < NUM_QUEUES; ++i) {
+ Queue& cur = queues[i];
+ if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
+ return i;
}
}
return -1;
}
- inline IdType pop_first() {
+ T pop_first() {
Queue *cur = first;
- while (cur != invalid())
- {
- if (cur->end - cur->first > 0)
- return cur->data[cur->first++];
- cur = cur->next;
+ while (cur != nullptr) {
+ if (!cur->data.empty()) {
+ auto tmp = std::move(cur->data.front());
+ cur->data.pop_front();
+ return tmp;
+ }
+ cur = cur->next_nonempty;
}
- //_dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty.");
- return 0;
+ return T();
}
- inline IdType pop_first_better(u32 priority) {
+ T pop_first_better(Priority priority) {
Queue *cur = first;
Queue *stop = &queues[priority];
- while (cur < stop)
- {
- if (cur->end - cur->first > 0)
- return cur->data[cur->first++];
- cur = cur->next;
+ while (cur < stop) {
+ if (!cur->data.empty()) {
+ auto tmp = std::move(cur->data.front());
+ cur->data.pop_front();
+ return tmp;
+ }
+ cur = cur->next_nonempty;
}
- return 0;
+ return T();
}
- inline void push_front(u32 priority, const IdType threadID) {
+ void push_front(Priority priority, const T& thread_id) {
Queue *cur = &queues[priority];
- cur->data[--cur->first] = threadID;
- if (cur->first == 0)
- rebalance(priority);
+ cur->data.push_front(thread_id);
}
- inline void push_back(u32 priority, const IdType threadID) {
+ void push_back(Priority priority, const T& thread_id) {
Queue *cur = &queues[priority];
- cur->data[cur->end++] = threadID;
- if (cur->end == cur->capacity)
- rebalance(priority);
+ cur->data.push_back(thread_id);
}
- inline void remove(u32 priority, const IdType threadID) {
+ void remove(Priority priority, const T& thread_id) {
Queue *cur = &queues[priority];
- //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
-
- for (int i = cur->first; i < cur->end; ++i)
- {
- if (cur->data[i] == threadID)
- {
- int remaining = --cur->end - i;
- if (remaining > 0)
- memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(IdType));
- return;
- }
- }
-
- // Wasn't there.
+ boost::remove_erase(cur->data, thread_id);
}
- inline void rotate(u32 priority) {
+ void rotate(Priority priority) {
Queue *cur = &queues[priority];
- //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
- if (cur->end - cur->first > 1)
- {
- cur->data[cur->end++] = cur->data[cur->first++];
- if (cur->end == cur->capacity)
- rebalance(priority);
+ if (cur->data.size() > 1) {
+ cur->data.push_back(std::move(cur->data.front()));
+ cur->data.pop_front();
}
}
- inline void clear() {
- for (int i = 0; i < NUM_QUEUES; ++i)
- {
- if (queues[i].data != nullptr)
- free(queues[i].data);
- }
- memset(queues, 0, sizeof(queues));
- first = invalid();
+ void clear() {
+ queues.fill(Queue());
+ first = nullptr;
}
- inline bool empty(u32 priority) const {
+ bool empty(Priority priority) const {
const Queue *cur = &queues[priority];
- return cur->first == cur->end;
+ return cur->data.empty();
}
- inline void prepare(u32 priority) {
- Queue *cur = &queues[priority];
- if (cur->next == nullptr)
- link(priority, INITIAL_CAPACITY);
+ void prepare(Priority priority) {
+ Queue* cur = &queues[priority];
+ if (cur->next_nonempty == UnlinkedTag())
+ link(priority);
}
private:
- Queue *invalid() const {
- return (Queue *) -1;
+ struct Queue {
+ // Points to the next active priority, skipping over ones that have never been used.
+ Queue* next_nonempty = UnlinkedTag();
+ // Double-ended queue of threads in this priority level
+ std::deque<T> data;
+ };
+
+ /// Special tag used to mark priority levels that have never been used.
+ static Queue* UnlinkedTag() {
+ return reinterpret_cast<Queue*>(1);
}
- void link(u32 priority, int size) {
- //_dbg_assert_msg_(SCEKERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once.");
-
- if (size <= INITIAL_CAPACITY)
- size = INITIAL_CAPACITY;
- else
- {
- int goal = size;
- size = INITIAL_CAPACITY;
- while (size < goal)
- size *= 2;
- }
+ void link(Priority priority) {
Queue *cur = &queues[priority];
- cur->data = (IdType *) malloc(sizeof(IdType) * size);
- cur->capacity = size;
- cur->first = size / 2;
- cur->end = size / 2;
-
- for (int i = (int) priority - 1; i >= 0; --i)
- {
- if (queues[i].next != nullptr)
- {
- cur->next = queues[i].next;
- queues[i].next = cur;
+
+ for (int i = priority - 1; i >= 0; --i) {
+ if (queues[i].next_nonempty != UnlinkedTag()) {
+ cur->next_nonempty = queues[i].next_nonempty;
+ queues[i].next_nonempty = cur;
return;
}
}
- cur->next = first;
+ cur->next_nonempty = first;
first = cur;
}
- void rebalance(u32 priority) {
- Queue *cur = &queues[priority];
- int size = cur->end - cur->first;
- if (size >= cur->capacity - 2) {
- IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType));
- if (new_data != nullptr) {
- cur->capacity *= 2;
- cur->data = new_data;
- }
- }
-
- int newFirst = (cur->capacity - size) / 2;
- if (newFirst != cur->first) {
- memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(IdType));
- cur->first = newFirst;
- cur->end = newFirst + size;
- }
- }
-
// The first queue that's ever been used.
- Queue *first;
+ Queue* first;
// The priority level queues of thread ids.
- Queue queues[NUM_QUEUES];
+ std::array<Queue, NUM_QUEUES> queues;
};
} // namespace
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 3b7209418..d3bd4a9a3 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -103,6 +103,8 @@ public:
return num_instructions;
}
+ s64 down_count; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop
+
protected:
/**
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index a838fd25a..c779e3fd4 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -9,6 +9,8 @@
#include "core/arm/dyncom/arm_dyncom.h"
#include "core/arm/dyncom/arm_dyncom_interpreter.h"
+#include "core/core_timing.h"
+
const static cpu_config_t s_arm11_cpu_info = {
"armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE
};
@@ -77,6 +79,9 @@ u64 ARM_DynCom::GetTicks() const {
void ARM_DynCom::AddTicks(u64 ticks) {
this->ticks += ticks;
+ down_count -= ticks;
+ if (down_count < 0)
+ CoreTiming::Advance();
}
void ARM_DynCom::ExecuteInstructions(int num_instructions) {
@@ -85,7 +90,8 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) {
// Dyncom only breaks on instruction dispatch. This only happens on every instruction when
// executing one instruction at a time. Otherwise, if a block is being executed, more
// instructions may actually be executed than specified.
- ticks += InterpreterMainLoop(state.get());
+ unsigned ticks_executed = InterpreterMainLoop(state.get());
+ AddTicks(ticks_executed);
}
void ARM_DynCom::SaveContext(ThreadContext& ctx) {
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 9b291862c..7c710ccde 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 {
unsigned int Rn;
} smla_inst;
+typedef struct smlalxy_inst {
+ unsigned int x;
+ unsigned int y;
+ unsigned int RdLo;
+ unsigned int RdHi;
+ unsigned int Rm;
+ unsigned int Rn;
+} smlalxy_inst;
+
typedef struct ssat_inst {
unsigned int Rn;
unsigned int Rd;
@@ -2403,7 +2412,25 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(smlal)(unsigned int inst, int index)
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALXY"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index)
+{
+ arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(smlalxy_inst));
+ smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component;
+
+ inst_base->cond = BITS(inst, 28, 31);
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
+ inst_base->load_r15 = 0;
+
+ inst_cream->x = BIT(inst, 5);
+ inst_cream->y = BIT(inst, 6);
+ inst_cream->RdLo = BITS(inst, 12, 15);
+ inst_cream->RdHi = BITS(inst, 16, 19);
+ inst_cream->Rn = BITS(inst, 0, 4);
+ inst_cream->Rm = BITS(inst, 8, 11);
+
+ return inst_base;
+}
ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index)
{
@@ -5686,6 +5713,34 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
SMLALXY_INST:
+ {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlalxy_inst* const inst_cream = (smlalxy_inst*)inst_base->component;
+
+ u64 operand1 = RN;
+ u64 operand2 = RM;
+
+ if (inst_cream->x != 0)
+ operand1 >>= 16;
+ if (inst_cream->y != 0)
+ operand2 >>= 16;
+ operand1 &= 0xFFFF;
+ if (operand1 & 0x8000)
+ operand1 -= 65536;
+ operand2 &= 0xFFFF;
+ if (operand2 & 0x8000)
+ operand2 -= 65536;
+
+ u64 dest = ((u64)RDHI << 32 | RDLO) + (operand1 * operand2);
+ RDLO = (dest & 0xFFFFFFFF);
+ RDHI = ((dest >> 32) & 0xFFFFFFFF);
+ }
+
+ cpu->Reg[15] += GET_INST_SIZE(cpu);
+ INC_PC(sizeof(smlalxy_inst));
+ FETCH_INST;
+ GOTO_NEXT_INST;
+ }
SMLAW_INST:
{
@@ -5836,16 +5891,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
SMULW_INST:
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- smlad_inst *inst_cream = (smlad_inst *)inst_base->component;
- int64_t rm = RM;
- int64_t rn = RN;
- if (inst_cream->m)
- rm = BITS(rm, 16, 31);
- else
- rm = BITS(rm, 0, 15);
- int64_t rst = rm * rn;
- RD = BITS(rst, 16, 47);
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ smlad_inst* const inst_cream = (smlad_inst*)inst_base->component;
+
+ s16 rm = (inst_cream->m == 1) ? ((RM >> 16) & 0xFFFF) : (RM & 0xFFFF);
+
+ s64 result = (s64)rm * (s64)(s32)RN;
+ RD = BITS(result, 16, 47);
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(smlad_inst));
@@ -6267,6 +6319,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
addr = RN;
unsigned int value = Memory::Read8(addr);
Memory::Write8(addr, (RM & 0xFF));
+ RD = value;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(swp_inst));
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 8ac4481cc..98f8a7dff 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -5,6 +5,7 @@
#include "common/common_types.h"
#include "core/core.h"
+#include "core/core_timing.h"
#include "core/settings.h"
#include "core/arm/disassembler/arm_disasm.h"
@@ -23,7 +24,17 @@ ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
/// Run the core CPU loop
void RunLoop(int tight_loop) {
- g_app_core->Run(tight_loop);
+ // If the current thread is an idle thread, then don't execute instructions,
+ // instead advance to the next event and try to yield to the next thread
+ if (Kernel::IsIdleThread(Kernel::GetCurrentThreadHandle())) {
+ LOG_TRACE(Core_ARM11, "Idling");
+ CoreTiming::Idle();
+ CoreTiming::Advance();
+ HLE::Reschedule(__func__);
+ } else {
+ g_app_core->Run(tight_loop);
+ }
+
HW::Update();
if (HLE::g_reschedule) {
Kernel::Reschedule();
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 321648b37..833199680 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -1,16 +1,14 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Copyright (c) 2012- PPSSPP Project / Dolphin Project.
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <vector>
-#include <cstdio>
#include <atomic>
+#include <cstdio>
#include <mutex>
+#include <vector>
#include "common/chunk_file.h"
-#include "common/msg_handler.h"
-#include "common/string_util.h"
-
+#include "common/log.h"
#include "core/core.h"
#include "core/core_timing.h"
@@ -22,16 +20,15 @@ int g_clock_rate_arm11 = 268123480;
namespace CoreTiming
{
-
struct EventType
{
EventType() {}
- EventType(TimedCallback cb, const char *n)
+ EventType(TimedCallback cb, const char* n)
: callback(cb), name(n) {}
TimedCallback callback;
- const char *name;
+ const char* name;
};
std::vector<EventType> event_types;
@@ -41,262 +38,247 @@ struct BaseEvent
s64 time;
u64 userdata;
int type;
- // Event *next;
};
typedef LinkedListItem<BaseEvent> Event;
-Event *first;
-Event *tsFirst;
-Event *tsLast;
+Event* first;
+Event* ts_first;
+Event* ts_last;
// event pools
-Event *eventPool = 0;
-Event *eventTsPool = 0;
-int allocatedTsEvents = 0;
+Event* event_pool = 0;
+Event* event_ts_pool = 0;
+int allocated_ts_events = 0;
// Optimization to skip MoveEvents when possible.
-std::atomic<u32> hasTsEvents;
+std::atomic<bool> has_ts_events(false);
-// Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block
-// as we can already reach that structure through a register.
-int slicelength;
+int g_slice_length;
-MEMORY_ALIGNED16(s64) globalTimer;
-s64 idledCycles;
+s64 global_timer;
+s64 idled_cycles;
+s64 last_global_time_ticks;
+s64 last_global_time_us;
-static std::recursive_mutex externalEventSection;
+static std::recursive_mutex external_event_section;
// Warning: not included in save state.
-void(*advanceCallback)(int cyclesExecuted) = nullptr;
+using AdvanceCallback = void(int cycles_executed);
+AdvanceCallback* advance_callback = nullptr;
+std::vector<MHzChangeCallback> mhz_change_callbacks;
-void SetClockFrequencyMHz(int cpuMhz)
-{
- g_clock_rate_arm11 = cpuMhz * 1000000;
+void FireMhzChange() {
+ for (auto callback : mhz_change_callbacks)
+ callback();
+}
+
+void SetClockFrequencyMHz(int cpu_mhz) {
+ // When the mhz changes, we keep track of what "time" it was before hand.
+ // This way, time always moves forward, even if mhz is changed.
+ last_global_time_us = GetGlobalTimeUs();
+ last_global_time_ticks = GetTicks();
+
+ g_clock_rate_arm11 = cpu_mhz * 1000000;
// TODO: Rescale times of scheduled events?
+
+ FireMhzChange();
}
-int GetClockFrequencyMHz()
-{
+int GetClockFrequencyMHz() {
return g_clock_rate_arm11 / 1000000;
}
+u64 GetGlobalTimeUs() {
+ s64 ticks_since_last = GetTicks() - last_global_time_ticks;
+ int freq = GetClockFrequencyMHz();
+ s64 us_since_last = ticks_since_last / freq;
+ return last_global_time_us + us_since_last;
+}
-Event* GetNewEvent()
-{
- if (!eventPool)
+Event* GetNewEvent() {
+ if (!event_pool)
return new Event;
- Event* ev = eventPool;
- eventPool = ev->next;
- return ev;
+ Event* event = event_pool;
+ event_pool = event->next;
+ return event;
}
-Event* GetNewTsEvent()
-{
- allocatedTsEvents++;
+Event* GetNewTsEvent() {
+ allocated_ts_events++;
- if (!eventTsPool)
+ if (!event_ts_pool)
return new Event;
- Event* ev = eventTsPool;
- eventTsPool = ev->next;
- return ev;
+ Event* event = event_ts_pool;
+ event_ts_pool = event->next;
+ return event;
}
-void FreeEvent(Event* ev)
-{
- ev->next = eventPool;
- eventPool = ev;
+void FreeEvent(Event* event) {
+ event->next = event_pool;
+ event_pool = event;
}
-void FreeTsEvent(Event* ev)
-{
- ev->next = eventTsPool;
- eventTsPool = ev;
- allocatedTsEvents--;
+void FreeTsEvent(Event* event) {
+ event->next = event_ts_pool;
+ event_ts_pool = event;
+ allocated_ts_events--;
}
-int RegisterEvent(const char *name, TimedCallback callback)
-{
+int RegisterEvent(const char* name, TimedCallback callback) {
event_types.push_back(EventType(callback, name));
return (int)event_types.size() - 1;
}
-void AntiCrashCallback(u64 userdata, int cyclesLate)
-{
- LOG_CRITICAL(Core, "Savestate broken: an unregistered event was called.");
+void AntiCrashCallback(u64 userdata, int cycles_late) {
+ LOG_CRITICAL(Core_Timing, "Savestate broken: an unregistered event was called.");
Core::Halt("invalid timing events");
}
-void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback)
-{
+void RestoreRegisterEvent(int event_type, const char* name, TimedCallback callback) {
if (event_type >= (int)event_types.size())
event_types.resize(event_type + 1, EventType(AntiCrashCallback, "INVALID EVENT"));
event_types[event_type] = EventType(callback, name);
}
-void UnregisterAllEvents()
-{
+void UnregisterAllEvents() {
if (first)
PanicAlert("Cannot unregister events with events pending");
event_types.clear();
}
-void Init()
-{
- //currentMIPS->downcount = INITIAL_SLICE_LENGTH;
- //slicelength = INITIAL_SLICE_LENGTH;
- globalTimer = 0;
- idledCycles = 0;
- hasTsEvents = 0;
+void Init() {
+ Core::g_app_core->down_count = INITIAL_SLICE_LENGTH;
+ g_slice_length = INITIAL_SLICE_LENGTH;
+ global_timer = 0;
+ idled_cycles = 0;
+ last_global_time_ticks = 0;
+ last_global_time_us = 0;
+ has_ts_events = 0;
+ mhz_change_callbacks.clear();
}
-void Shutdown()
-{
+void Shutdown() {
MoveEvents();
ClearPendingEvents();
UnregisterAllEvents();
- while (eventPool)
- {
- Event *ev = eventPool;
- eventPool = ev->next;
- delete ev;
+ while (event_pool) {
+ Event* event = event_pool;
+ event_pool = event->next;
+ delete event;
}
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
- while (eventTsPool)
- {
- Event *ev = eventTsPool;
- eventTsPool = ev->next;
- delete ev;
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ while (event_ts_pool) {
+ Event* event = event_ts_pool;
+ event_ts_pool = event->next;
+ delete event;
}
}
-u64 GetTicks()
-{
- LOG_ERROR(Core, "Unimplemented function!");
- return 0;
- //return (u64)globalTimer + slicelength - currentMIPS->downcount;
+u64 GetTicks() {
+ return (u64)global_timer + g_slice_length - Core::g_app_core->down_count;
}
-u64 GetIdleTicks()
-{
- return (u64)idledCycles;
+u64 GetIdleTicks() {
+ return (u64)idled_cycles;
}
// This is to be called when outside threads, such as the graphics thread, wants to
// schedule things to be executed on the main thread.
-void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata)
-{
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
- Event *ne = GetNewTsEvent();
- ne->time = GetTicks() + cyclesIntoFuture;
- ne->type = event_type;
- ne->next = 0;
- ne->userdata = userdata;
- if (!tsFirst)
- tsFirst = ne;
- if (tsLast)
- tsLast->next = ne;
- tsLast = ne;
-
- hasTsEvents.store(1, std::memory_order_release);
+void ScheduleEvent_Threadsafe(s64 cycles_into_future, int event_type, u64 userdata) {
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ Event* new_event = GetNewTsEvent();
+ new_event->time = GetTicks() + cycles_into_future;
+ new_event->type = event_type;
+ new_event->next = 0;
+ new_event->userdata = userdata;
+ if (!ts_first)
+ ts_first = new_event;
+ if (ts_last)
+ ts_last->next = new_event;
+ ts_last = new_event;
+
+ has_ts_events = true;
}
// Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread
// in which case the event will get handled immediately, before returning.
-void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata)
-{
+void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) {
if (false) //Core::IsCPUThread())
{
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
event_types[event_type].callback(userdata, 0);
}
else
ScheduleEvent_Threadsafe(0, event_type, userdata);
}
-void ClearPendingEvents()
-{
- while (first)
- {
- Event *e = first->next;
+void ClearPendingEvents() {
+ while (first) {
+ Event* event = first->next;
FreeEvent(first);
- first = e;
+ first = event;
}
}
-void AddEventToQueue(Event* ne)
-{
- Event* prev = nullptr;
- Event** pNext = &first;
- for (;;)
- {
- Event*& next = *pNext;
- if (!next || ne->time < next->time)
- {
- ne->next = next;
- next = ne;
+void AddEventToQueue(Event* new_event) {
+ Event* prev_event = nullptr;
+ Event** next_event = &first;
+ for (;;) {
+ Event*& next = *next_event;
+ if (!next || new_event->time < next->time) {
+ new_event->next = next;
+ next = new_event;
break;
}
- prev = next;
- pNext = &prev->next;
+ prev_event = next;
+ next_event = &prev_event->next;
}
}
-// This must be run ONLY from within the cpu thread
-// cyclesIntoFuture may be VERY inaccurate if called from anything else
-// than Advance
-void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata)
-{
- Event *ne = GetNewEvent();
- ne->userdata = userdata;
- ne->type = event_type;
- ne->time = GetTicks() + cyclesIntoFuture;
- AddEventToQueue(ne);
+void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata) {
+ Event* new_event = GetNewEvent();
+ new_event->userdata = userdata;
+ new_event->type = event_type;
+ new_event->time = GetTicks() + cycles_into_future;
+ AddEventToQueue(new_event);
}
-// Returns cycles left in timer.
-s64 UnscheduleEvent(int event_type, u64 userdata)
-{
+s64 UnscheduleEvent(int event_type, u64 userdata) {
s64 result = 0;
if (!first)
return result;
- while (first)
- {
- if (first->type == event_type && first->userdata == userdata)
- {
- result = first->time - globalTimer;
+ while (first) {
+ if (first->type == event_type && first->userdata == userdata) {
+ result = first->time - GetTicks();
- Event *next = first->next;
+ Event* next = first->next;
FreeEvent(first);
first = next;
- }
- else
- {
+ } else {
break;
}
}
if (!first)
return result;
- Event *prev = first;
- Event *ptr = prev->next;
- while (ptr)
- {
- if (ptr->type == event_type && ptr->userdata == userdata)
- {
- result = ptr->time - globalTimer;
- prev->next = ptr->next;
+ Event* prev_event = first;
+ Event* ptr = prev_event->next;
+
+ while (ptr) {
+ if (ptr->type == event_type && ptr->userdata == userdata) {
+ result = ptr->time - GetTicks();
+
+ prev_event->next = ptr->next;
FreeEvent(ptr);
- ptr = prev->next;
- }
- else
- {
- prev = ptr;
+ ptr = prev_event->next;
+ } else {
+ prev_event = ptr;
ptr = ptr->next;
}
}
@@ -304,51 +286,44 @@ s64 UnscheduleEvent(int event_type, u64 userdata)
return result;
}
-s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata)
-{
+s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) {
s64 result = 0;
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
- if (!tsFirst)
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ if (!ts_first)
return result;
- while (tsFirst)
- {
- if (tsFirst->type == event_type && tsFirst->userdata == userdata)
- {
- result = tsFirst->time - globalTimer;
- Event *next = tsFirst->next;
- FreeTsEvent(tsFirst);
- tsFirst = next;
- }
- else
- {
+ while (ts_first) {
+ if (ts_first->type == event_type && ts_first->userdata == userdata) {
+ result = ts_first->time - GetTicks();
+
+ Event* next = ts_first->next;
+ FreeTsEvent(ts_first);
+ ts_first = next;
+ } else {
break;
}
}
- if (!tsFirst)
+
+ if (!ts_first)
{
- tsLast = nullptr;
+ ts_last = nullptr;
return result;
}
- Event *prev = tsFirst;
- Event *ptr = prev->next;
- while (ptr)
- {
- if (ptr->type == event_type && ptr->userdata == userdata)
- {
- result = ptr->time - globalTimer;
-
- prev->next = ptr->next;
- if (ptr == tsLast)
- tsLast = prev;
- FreeTsEvent(ptr);
- ptr = prev->next;
- }
- else
- {
- prev = ptr;
- ptr = ptr->next;
+ Event* prev_event = ts_first;
+ Event* next = prev_event->next;
+ while (next) {
+ if (next->type == event_type && next->userdata == userdata) {
+ result = next->time - GetTicks();
+
+ prev_event->next = next->next;
+ if (next == ts_last)
+ ts_last = prev_event;
+ FreeTsEvent(next);
+ next = prev_event->next;
+ } else {
+ prev_event = next;
+ next = next->next;
}
}
@@ -356,271 +331,217 @@ s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata)
}
// Warning: not included in save state.
-void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted))
-{
- advanceCallback = callback;
+void RegisterAdvanceCallback(AdvanceCallback* callback) {
+ advance_callback = callback;
}
-bool IsScheduled(int event_type)
-{
+void RegisterMHzChangeCallback(MHzChangeCallback callback) {
+ mhz_change_callbacks.push_back(callback);
+}
+
+bool IsScheduled(int event_type) {
if (!first)
return false;
- Event *e = first;
- while (e) {
- if (e->type == event_type)
+ Event* event = first;
+ while (event) {
+ if (event->type == event_type)
return true;
- e = e->next;
+ event = event->next;
}
return false;
}
-void RemoveEvent(int event_type)
-{
+void RemoveEvent(int event_type) {
if (!first)
return;
- while (first)
- {
- if (first->type == event_type)
- {
+ while (first) {
+ if (first->type == event_type) {
Event *next = first->next;
FreeEvent(first);
first = next;
- }
- else
- {
+ } else {
break;
}
}
if (!first)
return;
- Event *prev = first;
- Event *ptr = prev->next;
- while (ptr)
- {
- if (ptr->type == event_type)
- {
- prev->next = ptr->next;
- FreeEvent(ptr);
- ptr = prev->next;
- }
- else
- {
- prev = ptr;
- ptr = ptr->next;
+ Event* prev = first;
+ Event* next = prev->next;
+ while (next) {
+ if (next->type == event_type) {
+ prev->next = next->next;
+ FreeEvent(next);
+ next = prev->next;
+ } else {
+ prev = next;
+ next = next->next;
}
}
}
-void RemoveThreadsafeEvent(int event_type)
-{
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
- if (!tsFirst)
- {
+void RemoveThreadsafeEvent(int event_type) {
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
+ if (!ts_first)
return;
- }
- while (tsFirst)
- {
- if (tsFirst->type == event_type)
- {
- Event *next = tsFirst->next;
- FreeTsEvent(tsFirst);
- tsFirst = next;
- }
- else
- {
+
+ while (ts_first) {
+ if (ts_first->type == event_type) {
+ Event* next = ts_first->next;
+ FreeTsEvent(ts_first);
+ ts_first = next;
+ } else {
break;
}
}
- if (!tsFirst)
- {
- tsLast = nullptr;
+
+ if (!ts_first) {
+ ts_last = nullptr;
return;
}
- Event *prev = tsFirst;
- Event *ptr = prev->next;
- while (ptr)
- {
- if (ptr->type == event_type)
- {
- prev->next = ptr->next;
- if (ptr == tsLast)
- tsLast = prev;
- FreeTsEvent(ptr);
- ptr = prev->next;
- }
- else
- {
- prev = ptr;
- ptr = ptr->next;
+
+ Event* prev = ts_first;
+ Event* next = prev->next;
+ while (next) {
+ if (next->type == event_type) {
+ prev->next = next->next;
+ if (next == ts_last)
+ ts_last = prev;
+ FreeTsEvent(next);
+ next = prev->next;
+ } else {
+ prev = next;
+ next = next->next;
}
}
}
-void RemoveAllEvents(int event_type)
-{
+void RemoveAllEvents(int event_type) {
RemoveThreadsafeEvent(event_type);
RemoveEvent(event_type);
}
-//This raise only the events required while the fifo is processing data
-void ProcessFifoWaitEvents()
-{
- while (first)
- {
- if (first->time <= globalTimer)
- {
- //LOG(TIMER, "[Scheduler] %s (%lld, %lld) ",
- // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
+// This raise only the events required while the fifo is processing data
+void ProcessFifoWaitEvents() {
+ while (first) {
+ if (first->time <= (s64)GetTicks()) {
Event* evt = first;
first = first->next;
- event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time));
+ event_types[evt->type].callback(evt->userdata, (int)(GetTicks() - evt->time));
FreeEvent(evt);
- }
- else
- {
+ } else {
break;
}
}
}
-void MoveEvents()
-{
- hasTsEvents.store(0, std::memory_order_release);
+void MoveEvents() {
+ has_ts_events = false;
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
+ std::lock_guard<std::recursive_mutex> lock(external_event_section);
// Move events from async queue into main queue
- while (tsFirst)
- {
- Event *next = tsFirst->next;
- AddEventToQueue(tsFirst);
- tsFirst = next;
+ while (ts_first) {
+ Event* next = ts_first->next;
+ AddEventToQueue(ts_first);
+ ts_first = next;
}
- tsLast = nullptr;
+ ts_last = nullptr;
// Move free events to threadsafe pool
- while (allocatedTsEvents > 0 && eventPool)
- {
- Event *ev = eventPool;
- eventPool = ev->next;
- ev->next = eventTsPool;
- eventTsPool = ev;
- allocatedTsEvents--;
+ while (allocated_ts_events > 0 && event_pool) {
+ Event* event = event_pool;
+ event_pool = event->next;
+ event->next = event_ts_pool;
+ event_ts_pool = event;
+ allocated_ts_events--;
}
}
-void Advance()
-{
- LOG_ERROR(Core, "Unimplemented function!");
- //int cyclesExecuted = slicelength - currentMIPS->downcount;
- //globalTimer += cyclesExecuted;
- //currentMIPS->downcount = slicelength;
-
- //if (Common::AtomicLoadAcquire(hasTsEvents))
- // MoveEvents();
- //ProcessFifoWaitEvents();
-
- //if (!first)
- //{
- // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000");
- // currentMIPS->downcount += 10000;
- //}
- //else
- //{
- // slicelength = (int)(first->time - globalTimer);
- // if (slicelength > MAX_SLICE_LENGTH)
- // slicelength = MAX_SLICE_LENGTH;
- // currentMIPS->downcount = slicelength;
- //}
- //if (advanceCallback)
- // advanceCallback(cyclesExecuted);
-}
-
-void LogPendingEvents()
-{
- Event *ptr = first;
- while (ptr)
- {
- //INFO_LOG(TIMER, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type);
- ptr = ptr->next;
- }
+void ForceCheck() {
+ int cycles_executed = g_slice_length - Core::g_app_core->down_count;
+ global_timer += cycles_executed;
+ // This will cause us to check for new events immediately.
+ Core::g_app_core->down_count = 0;
+ // But let's not eat a bunch more time in Advance() because of this.
+ g_slice_length = 0;
}
-void Idle(int maxIdle)
-{
- LOG_ERROR(Core, "Unimplemented function!");
- //int cyclesDown = currentMIPS->downcount;
- //if (maxIdle != 0 && cyclesDown > maxIdle)
- // cyclesDown = maxIdle;
-
- //if (first && cyclesDown > 0)
- //{
- // int cyclesExecuted = slicelength - currentMIPS->downcount;
- // int cyclesNextEvent = (int) (first->time - globalTimer);
-
- // if (cyclesNextEvent < cyclesExecuted + cyclesDown)
- // {
- // cyclesDown = cyclesNextEvent - cyclesExecuted;
- // // Now, now... no time machines, please.
- // if (cyclesDown < 0)
- // cyclesDown = 0;
- // }
- //}
-
- //INFO_LOG(TIME, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(g_clock_rate_arm11 * 0.001f));
-
- //idledCycles += cyclesDown;
- //currentMIPS->downcount -= cyclesDown;
- //if (currentMIPS->downcount == 0)
- // currentMIPS->downcount = -1;
-}
-
-std::string GetScheduledEventsSummary()
-{
- Event *ptr = first;
- std::string text = "Scheduled events\n";
- text.reserve(1000);
- while (ptr)
- {
- unsigned int t = ptr->type;
- if (t >= event_types.size())
- PanicAlert("Invalid event type"); // %i", t);
- const char *name = event_types[ptr->type].name;
- if (!name)
- name = "[unknown]";
+void Advance() {
+ int cycles_executed = g_slice_length - Core::g_app_core->down_count;
+ global_timer += cycles_executed;
+ Core::g_app_core->down_count = g_slice_length;
- text += Common::StringFromFormat("%s : %i %08x%08x\n", name, (int)ptr->time,
- (u32)(ptr->userdata >> 32), (u32)(ptr->userdata));
+ if (has_ts_events)
+ MoveEvents();
+ ProcessFifoWaitEvents();
- ptr = ptr->next;
+ if (!first) {
+ if (g_slice_length < 10000) {
+ g_slice_length += 10000;
+ Core::g_app_core->down_count += g_slice_length;
+ }
+ } else {
+ // Note that events can eat cycles as well.
+ int target = (int)(first->time - global_timer);
+ if (target > MAX_SLICE_LENGTH)
+ target = MAX_SLICE_LENGTH;
+
+ const int diff = target - g_slice_length;
+ g_slice_length += diff;
+ Core::g_app_core->down_count += diff;
}
- return text;
+ if (advance_callback)
+ advance_callback(cycles_executed);
}
-void Event_DoState(PointerWrap &p, BaseEvent *ev)
-{
- p.Do(*ev);
+void LogPendingEvents() {
+ Event* event = first;
+ while (event) {
+ //LOG_TRACE(Core_Timing, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, next->time, next->type);
+ event = event->next;
+ }
}
-void DoState(PointerWrap &p)
-{
- std::lock_guard<std::recursive_mutex> lk(externalEventSection);
+void Idle(int max_idle) {
+ int cycles_down = Core::g_app_core->down_count;
+ if (max_idle != 0 && cycles_down > max_idle)
+ cycles_down = max_idle;
- auto s = p.Section("CoreTiming", 1);
- if (!s)
- return;
+ if (first && cycles_down > 0) {
+ int cycles_executed = g_slice_length - Core::g_app_core->down_count;
+ int cycles_next_event = (int)(first->time - global_timer);
- int n = (int)event_types.size();
- p.Do(n);
- // These (should) be filled in later by the modules.
- event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT"));
+ if (cycles_next_event < cycles_executed + cycles_down) {
+ cycles_down = cycles_next_event - cycles_executed;
+ // Now, now... no time machines, please.
+ if (cycles_down < 0)
+ cycles_down = 0;
+ }
+ }
- p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)nullptr);
- p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast);
+ LOG_TRACE(Core_Timing, "Idle for %i cycles! (%f ms)", cycles_down, cycles_down / (float)(g_clock_rate_arm11 * 0.001f));
- p.Do(g_clock_rate_arm11);
- p.Do(slicelength);
- p.Do(globalTimer);
- p.Do(idledCycles);
+ idled_cycles += cycles_down;
+ Core::g_app_core->down_count -= cycles_down;
+ if (Core::g_app_core->down_count == 0)
+ Core::g_app_core->down_count = -1;
+}
+
+std::string GetScheduledEventsSummary() {
+ Event* event = first;
+ std::string text = "Scheduled events\n";
+ text.reserve(1000);
+ while (event) {
+ unsigned int t = event->type;
+ if (t >= event_types.size())
+ PanicAlert("Invalid event type"); // %i", t);
+ const char* name = event_types[event->type].name;
+ if (!name)
+ name = "[unknown]";
+ text += Common::StringFromFormat("%s : %i %08x%08x\n", name, (int)event->time,
+ (u32)(event->userdata >> 32), (u32)(event->userdata));
+ event = event->next;
+ }
+ return text;
}
} // 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 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
+// Copyright (c) 2012- PPSSPP Project / Dolphin Project.
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
+#include <string>
+
// This is a system to schedule events into the emulated machine's future. Time is measured
// in main CPU clock cycles.
@@ -12,14 +14,14 @@
// See HW/SystemTimers.cpp for the main part of Dolphin's usage of this scheduler.
-// The int cyclesLate that the callbacks get is how many cycles late it was.
+// The int cycles_late that the callbacks get is how many cycles late it was.
// So to schedule a new event on a regular basis:
// inside callback:
-// ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
+// ScheduleEvent(periodInCycles - cycles_late, callback, "whatever")
-#include "common/common.h"
+#include <functional>
-class PointerWrap;
+#include "common/common.h"
extern int g_clock_rate_arm11;
@@ -55,55 +57,84 @@ inline s64 cyclesToUs(s64 cycles) {
return cycles / (g_clock_rate_arm11 / 1000000);
}
-namespace CoreTiming {
+inline u64 cyclesToMs(s64 cycles) {
+ return cycles / (g_clock_rate_arm11 / 1000);
+}
+namespace CoreTiming
+{
void Init();
void Shutdown();
-typedef void(*TimedCallback)(u64 userdata, int cyclesLate);
+typedef void(*MHzChangeCallback)();
+typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
u64 GetTicks();
u64 GetIdleTicks();
-
-// Returns the event_type identifier.
-int RegisterEvent(const char *name, TimedCallback callback);
-// For save states.
+u64 GetGlobalTimeUs();
+
+/**
+ * Registers an event type with the specified name and callback
+ * @param name Name of the event type
+ * @param callback Function that will execute when this event fires
+ * @returns An identifier for the event type that was registered
+ */
+int RegisterEvent(const char* name, TimedCallback callback);
+/// For save states.
void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback);
void UnregisterAllEvents();
-// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk,
-// when we implement state saves.
-void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata = 0);
-void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata = 0);
+/// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk,
+/// when we implement state saves.
+/**
+ * Schedules an event to run after the specified number of cycles,
+ * with an optional parameter to be passed to the callback handler.
+ * This must be run ONLY from within the cpu thread.
+ * @param cycles_into_future The number of cycles after which this event will be fired
+ * @param event_type The event type to fire, as returned from RegisterEvent
+ * @param userdata Optional parameter to pass to the callback when fired
+ */
+void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata = 0);
+
+void ScheduleEvent_Threadsafe(s64 cycles_into_future, int event_type, u64 userdata = 0);
void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata = 0);
+
+/**
+ * Unschedules an event with the specified type and userdata
+ * @param event_type The type of event to unschedule, as returned from RegisterEvent
+ * @param userdata The userdata that identifies this event, as passed to ScheduleEvent
+ * @returns The remaining ticks until the next invocation of the event callback
+ */
s64 UnscheduleEvent(int event_type, u64 userdata);
+
s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata);
void RemoveEvent(int event_type);
void RemoveThreadsafeEvent(int event_type);
void RemoveAllEvents(int event_type);
bool IsScheduled(int event_type);
+/// Runs any pending events and updates downcount for the next slice of cycles
void Advance();
void MoveEvents();
void ProcessFifoWaitEvents();
+void ForceCheck();
-// Pretend that the main CPU has executed enough cycles to reach the next event.
+/// Pretend that the main CPU has executed enough cycles to reach the next event.
void Idle(int maxIdle = 0);
-// Clear all pending events. This should ONLY be done on exit or state load.
+/// Clear all pending events. This should ONLY be done on exit or state load.
void ClearPendingEvents();
void LogPendingEvents();
-// Warning: not included in save states.
-void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted));
+/// Warning: not included in save states.
+void RegisterAdvanceCallback(void(*callback)(int cycles_executed));
+void RegisterMHzChangeCallback(MHzChangeCallback callback);
std::string GetScheduledEventsSummary();
-void DoState(PointerWrap &p);
-
-void SetClockFrequencyMHz(int cpuMhz);
+void SetClockFrequencyMHz(int cpu_mhz);
int GetClockFrequencyMHz();
-extern int slicelength;
+extern int g_slice_length;
} // 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 @@
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/disk_archive.h"
+#include "core/hle/service/fs/archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -19,15 +20,22 @@ namespace FileSys {
static std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) {
std::vector<u8> vec_data = path.AsBinary();
const u32* data = reinterpret_cast<const u32*>(vec_data.data());
- u32 media_type = data[0];
u32 save_low = data[1];
u32 save_high = data[2];
- return Common::StringFromFormat("%s%s/%08X/%08X/", mount_point.c_str(), media_type == 0 ? "nand" : "sdmc", save_high, save_low);
+ return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_high, save_low);
}
-Archive_ExtSaveData::Archive_ExtSaveData(const std::string& mount_point)
- : DiskArchive(mount_point), concrete_mount_point(mount_point) {
- LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", this->mount_point.c_str());
+static std::string GetExtDataContainerPath(const std::string& mount_point, bool shared) {
+ if (shared)
+ return Common::StringFromFormat("%sdata/%s/extdata/", mount_point.c_str(), SYSTEM_ID.c_str());
+
+ return Common::StringFromFormat("%sNintendo 3DS/%s/%s/extdata/", mount_point.c_str(),
+ SYSTEM_ID.c_str(), SDCARD_ID.c_str());
+}
+
+Archive_ExtSaveData::Archive_ExtSaveData(const std::string& mount_location, bool shared)
+ : DiskArchive(GetExtDataContainerPath(mount_location, shared)) {
+ LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str());
}
bool Archive_ExtSaveData::Initialize() {
diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h
index a3a144799..fb7f209d2 100644
--- a/src/core/file_sys/archive_extsavedata.h
+++ b/src/core/file_sys/archive_extsavedata.h
@@ -17,7 +17,7 @@ namespace FileSys {
/// File system interface to the ExtSaveData archive
class Archive_ExtSaveData final : public DiskArchive {
public:
- Archive_ExtSaveData(const std::string& mount_point);
+ Archive_ExtSaveData(const std::string& mount_point, bool shared);
/**
* 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 @@
#include "core/file_sys/archive_savedata.h"
#include "core/file_sys/disk_archive.h"
+#include "core/hle/service/fs/archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -16,14 +17,25 @@
namespace FileSys {
-Archive_SaveData::Archive_SaveData(const std::string& mount_point)
- : DiskArchive(mount_point) {
+static std::string GetSaveDataContainerPath(const std::string& sdmc_directory) {
+ return Common::StringFromFormat("%sNintendo 3DS/%s/%s/title/", sdmc_directory.c_str(),
+ SYSTEM_ID.c_str(), SDCARD_ID.c_str());
+}
+
+static std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) {
+ u32 high = program_id >> 32;
+ u32 low = program_id & 0xFFFFFFFF;
+ return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high, low);
+}
+
+Archive_SaveData::Archive_SaveData(const std::string& sdmc_directory)
+ : DiskArchive(GetSaveDataContainerPath(sdmc_directory)) {
LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str());
}
ResultCode Archive_SaveData::Open(const Path& path) {
if (concrete_mount_point.empty())
- concrete_mount_point = Common::StringFromFormat("%s%016X", mount_point.c_str(), Kernel::g_program_id) + DIR_SEP;
+ concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_program_id);
if (!FileUtil::Exists(concrete_mount_point)) {
// When a SaveData archive is created for the first time, it is not yet formatted
// 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 @@
#include "common/file_util.h"
#include "core/file_sys/archive_savedatacheck.h"
+#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
-Archive_SaveDataCheck::Archive_SaveDataCheck(const std::string& mount_loc) : mount_point(mount_loc) {
+static std::string GetSaveDataCheckContainerPath(const std::string& nand_directory) {
+ return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID.c_str());
+}
+
+static std::string GetSaveDataCheckPath(const std::string& mount_point, u32 high, u32 low) {
+ return Common::StringFromFormat("%s%08x/%08x/content/00000000.app.romfs",
+ mount_point.c_str(), high, low);
+}
+
+Archive_SaveDataCheck::Archive_SaveDataCheck(const std::string& nand_directory) :
+ mount_point(GetSaveDataCheckContainerPath(nand_directory)) {
}
ResultCode Archive_SaveDataCheck::Open(const Path& path) {
@@ -23,7 +34,7 @@ ResultCode Archive_SaveDataCheck::Open(const Path& path) {
// this archive again with a different path, will corrupt the previously open file.
auto vec = path.AsBinary();
const u32* data = reinterpret_cast<u32*>(vec.data());
- std::string file_path = Common::StringFromFormat("%s%08x%08x.bin", mount_point.c_str(), data[1], data[0]);
+ std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]);
FileUtil::IOFile file(file_path, "rb");
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 @@
namespace FileSys {
-Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) {
- LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str());
+Archive_SDMC::Archive_SDMC(const std::string& sdmc_directory) : DiskArchive(sdmc_directory) {
+ LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str());
}
bool Archive_SDMC::Initialize() {
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp
index 0da32d510..c2a5d641a 100644
--- a/src/core/file_sys/archive_systemsavedata.cpp
+++ b/src/core/file_sys/archive_systemsavedata.cpp
@@ -9,6 +9,7 @@
#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/disk_archive.h"
+#include "core/hle/service/fs/archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -22,8 +23,12 @@ static std::string GetSystemSaveDataPath(const std::string& mount_point, u64 sav
return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high);
}
+static std::string GetSystemSaveDataContainerPath(const std::string& mount_point) {
+ return Common::StringFromFormat("%sdata/%s/sysdata/", mount_point.c_str(), SYSTEM_ID.c_str());
+}
+
Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id)
- : DiskArchive(GetSystemSaveDataPath(mount_point, save_id)) {
+ : DiskArchive(GetSystemSaveDataPath(GetSystemSaveDataContainerPath(mount_point), save_id)) {
LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str());
}
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 @@
namespace FileSys {
/// File system interface to the SystemSaveData archive
-/// TODO(Subv): This archive should point to a location in the NAND,
-/// specifically nand:/data/<ID0>/sysdata/<SaveID-Low>/<SaveID-High>
class Archive_SystemSaveData final : public DiskArchive {
public:
Archive_SystemSaveData(const std::string& mount_point, u64 save_id);
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e59ed1b57..ae2c11a1c 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -124,6 +124,8 @@ bool LoadExec(u32 entry_point) {
// 0x30 is the typical main thread priority I've seen used so far
g_main_thread = Kernel::SetupMainThread(0x30);
+ // Setup the idle thread
+ Kernel::SetupIdleThread();
return true;
}
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 872df2d14..58fb62e89 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -11,6 +11,7 @@
#include "common/thread_queue_list.h"
#include "core/core.h"
+#include "core/core_timing.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
@@ -34,6 +35,7 @@ public:
inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; }
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
+ inline bool IsIdle() const { return idle; }
ResultVal<bool> WaitSynchronization() override {
const bool wait = status != THREADSTATUS_DORMANT;
@@ -69,13 +71,16 @@ public:
std::vector<Handle> waiting_threads;
std::string name;
+
+ /// Whether this thread is intended to never actually be executed, i.e. always idle
+ bool idle = false;
};
// Lists all thread ids that aren't deleted/etc.
static std::vector<Handle> thread_queue;
// Lists only ready thread ids.
-static Common::ThreadQueueList<Handle> thread_ready_queue;
+static Common::ThreadQueueList<Handle, THREADPRIO_LOWEST+1> thread_ready_queue;
static Handle current_thread_handle;
static Thread* current_thread;
@@ -444,7 +449,14 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
return RESULT_SUCCESS;
}
-/// Sets up the primary application thread
+Handle SetupIdleThread() {
+ Handle handle;
+ Thread* thread = CreateThread(handle, "idle", 0, THREADPRIO_LOWEST, THREADPROCESSORID_0, 0, 0);
+ thread->idle = true;
+ CallThread(thread);
+ return handle;
+}
+
Handle SetupMainThread(s32 priority, int stack_size) {
Handle handle;
@@ -497,6 +509,15 @@ void Reschedule() {
ResumeThreadFromWait(prev->GetHandle());
}
+bool IsIdleThread(Handle handle) {
+ Thread* thread = g_handle_table.Get<Thread>(handle);
+ if (!thread) {
+ LOG_ERROR(Kernel, "Thread not found %u", handle);
+ return false;
+ }
+ return thread->IsIdle();
+}
+
ResultCode GetThreadId(u32* thread_id, Handle handle) {
Thread* thread = g_handle_table.Get<Thread>(handle);
if (thread == nullptr)
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 0e1397cd9..dfe92d162 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -104,6 +104,17 @@ ResultVal<u32> GetThreadPriority(const Handle handle);
/// Set the priority of the thread specified by handle
ResultCode SetThreadPriority(Handle handle, s32 priority);
+/**
+ * Sets up the idle thread, this is a thread that is intended to never execute instructions,
+ * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue
+ * and will try to yield on every call.
+ * @returns The handle of the idle thread
+ */
+Handle SetupIdleThread();
+
+/// Whether the current thread is an idle thread
+bool IsIdleThread(Handle thread);
+
/// Initialize threading
void ThreadingInit();
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() {
void CFGInit() {
// TODO(Subv): In the future we should use the FS service to query this archive,
// currently it is not possible because you can only have one open archive of the same type at any time
- std::string syssavedata_directory = FileUtil::GetUserPath(D_SYSSAVEDATA_IDX);
+ std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
cfg_system_save_data = Common::make_unique<FileSys::Archive_SystemSaveData>(
- syssavedata_directory, CFG_SAVE_ID);
+ nand_directory, CFG_SAVE_ID);
if (!cfg_system_save_data->Initialize()) {
LOG_CRITICAL(Service_CFG, "Could not initialize SystemSaveData archive for the CFG:U service");
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 {
};
}
+/// TODO(Subv): Confirm length of these strings
+const std::string SYSTEM_ID = "00000000000000000000000000000000";
+const std::string SDCARD_ID = "00000000000000000000000000000000";
+
namespace Service {
namespace FS {
@@ -432,11 +436,11 @@ ResultCode FormatSaveData() {
void ArchiveInit() {
next_handle = 1;
- // TODO(Link Mauve): Add the other archive types (see here for the known types:
- // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
- // archive type is SDMC, so it is the only one getting exposed.
+ // TODO(Subv): Add the other archive types (see here for the known types:
+ // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes).
std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
+ std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
auto sdmc_archive = Common::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
if (sdmc_archive->Initialize())
CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC);
@@ -444,28 +448,24 @@ void ArchiveInit() {
LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
// Create the SaveData archive
- std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX);
- auto savedata_archive = Common::make_unique<FileSys::Archive_SaveData>(savedata_directory);
+ auto savedata_archive = Common::make_unique<FileSys::Archive_SaveData>(sdmc_directory);
CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData);
- std::string extsavedata_directory = FileUtil::GetUserPath(D_EXTSAVEDATA);
- auto extsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(extsavedata_directory);
+ auto extsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(sdmc_directory, false);
if (extsavedata_archive->Initialize())
CreateArchive(std::move(extsavedata_archive), ArchiveIdCode::ExtSaveData);
else
- LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_directory.c_str());
+ LOG_ERROR(Service_FS, "Can't instantiate ExtSaveData archive with path %s", extsavedata_archive->GetMountPoint().c_str());
- std::string sharedextsavedata_directory = FileUtil::GetUserPath(D_EXTSAVEDATA);
- auto sharedextsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(sharedextsavedata_directory);
+ auto sharedextsavedata_archive = Common::make_unique<FileSys::Archive_ExtSaveData>(nand_directory, true);
if (sharedextsavedata_archive->Initialize())
CreateArchive(std::move(sharedextsavedata_archive), ArchiveIdCode::SharedExtSaveData);
else
LOG_ERROR(Service_FS, "Can't instantiate SharedExtSaveData archive with path %s",
- sharedextsavedata_directory.c_str());
+ sharedextsavedata_archive->GetMountPoint().c_str());
// Create the SaveDataCheck archive, basically a small variation of the RomFS archive
- std::string savedatacheck_directory = FileUtil::GetUserPath(D_SAVEDATACHECK_IDX);
- auto savedatacheck_archive = Common::make_unique<FileSys::Archive_SaveDataCheck>(savedatacheck_directory);
+ auto savedatacheck_archive = Common::make_unique<FileSys::Archive_SaveDataCheck>(nand_directory);
CreateArchive(std::move(savedatacheck_archive), ArchiveIdCode::SaveDataCheck);
}
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 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
+/// The unique system identifier hash, also known as ID0
+extern const std::string SYSTEM_ID;
+/// The scrambled SD card CID, also known as ID1
+extern const std::string SDCARD_ID;
+
namespace Service {
namespace FS {
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 0127d4ee5..26a43217e 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) {
// Update framebuffer information if requested
for (int screen_id = 0; screen_id < 2; ++screen_id) {
FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
- if (info->is_dirty)
+
+ if (info->is_dirty) {
SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
+ info->framebuffer_info->active_fb = info->framebuffer_info->active_fb ^ 1;
+ }
info->is_dirty = false;
}
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp
index 9cc700c46..fd79cd8ab 100644
--- a/src/core/hle/service/ptm_u.cpp
+++ b/src/core/hle/service/ptm_u.cpp
@@ -142,10 +142,10 @@ Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
// Create the SharedExtSaveData archive 0xF000000B and the gamecoin.dat file
// TODO(Subv): In the future we should use the FS service to query this archive
- std::string extsavedata_directory = FileUtil::GetUserPath(D_EXTSAVEDATA);
- ptm_shared_extsavedata = Common::make_unique<FileSys::Archive_ExtSaveData>(extsavedata_directory);
+ std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
+ ptm_shared_extsavedata = Common::make_unique<FileSys::Archive_ExtSaveData>(nand_directory, true);
if (!ptm_shared_extsavedata->Initialize()) {
- LOG_CRITICAL(Service_PTM, "Could not initialize ExtSaveData archive for the PTM:U service");
+ LOG_CRITICAL(Service_PTM, "Could not initialize SharedExtSaveData archive for the PTM:U service");
return;
}
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..0c5597283 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -46,36 +46,22 @@ Manager* g_manager = nullptr; ///< Service manager
////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Manager class
-Manager::Manager() {
-}
-
-Manager::~Manager() {
- for(Interface* service : m_services) {
- DeleteService(service->GetPortName());
- }
-}
-
-/// Add a service to the manager (does not create it though)
void Manager::AddService(Interface* service) {
// TOOD(yuriks): Fix error reporting
m_port_map[service->GetPortName()] = Kernel::g_handle_table.Create(service).ValueOr(INVALID_HANDLE);
m_services.push_back(service);
}
-/// Removes a service from the manager, also frees memory
void Manager::DeleteService(const std::string& port_name) {
Interface* service = FetchFromPortName(port_name);
m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end());
m_port_map.erase(port_name);
- delete service;
}
-/// Get a Service Interface from its Handle
Interface* Manager::FetchFromHandle(Handle handle) {
return Kernel::g_handle_table.Get<Interface>(handle);
}
-/// Get a Service Interface from its port
Interface* Manager::FetchFromPortName(const std::string& port_name) {
auto itr = m_port_map.find(port_name);
if (itr == m_port_map.end()) {
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 28b4ccd17..41ba1e554 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -114,29 +114,22 @@ private:
/// Simple class to manage accessing services from ports and UID handles
class Manager {
-
public:
- Manager();
-
- ~Manager();
-
- /// Add a service to the manager (does not create it though)
+ /// Add a service to the manager
void AddService(Interface* service);
- /// Removes a service from the manager (does not delete it though)
+ /// Removes a service from the manager
void DeleteService(const std::string& port_name);
- /// Get a Service Interface from its UID
- Interface* FetchFromHandle(u32 uid);
+ /// Get a Service Interface from its Handle
+ Interface* FetchFromHandle(Handle handle);
/// Get a Service Interface from its port
Interface* FetchFromPortName(const std::string& port_name);
private:
-
std::vector<Interface*> m_services;
std::map<std::string, u32> m_port_map;
-
};
/// Initialize ServiceManager
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() {
*/
void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
const TextureInfo& texture) {
+
+ // TODO: Why are active_fb and the valid framebuffer flipped compared to 3dbrew documentation
+ // and GSP definitions?
const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress(
- framebuffer.active_fb == 1 ? framebuffer.address_left2 : framebuffer.address_left1);
+ framebuffer.active_fb == 0 ? framebuffer.address_left2 : framebuffer.address_left1);
LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x",
framebuffer.stride * framebuffer.height,