diff options
Diffstat (limited to 'src/core')
30 files changed, 568 insertions, 246 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 48241c3d4..8f6792791 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -59,10 +59,10 @@ set(SRCS hle/svc.cpp hw/gpu.cpp hw/hw.cpp - hw/ndma.cpp loader/elf.cpp loader/loader.cpp loader/ncch.cpp + loader/3dsx.cpp core.cpp core_timing.cpp mem_map.cpp @@ -139,10 +139,10 @@ set(HEADERS hle/svc.h hw/gpu.h hw/hw.h - hw/ndma.h loader/elf.h loader/loader.h loader/ncch.h + loader/3dsx.h core.h core_timing.h mem_map.h diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp index 73223874e..d717bd2c8 100644 --- a/src/core/arm/interpreter/armemu.cpp +++ b/src/core/arm/interpreter/armemu.cpp @@ -5724,7 +5724,7 @@ L_stm_s_takeabort: s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); s16 b1 = (state->Reg[src2] & 0xFFFF); s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); - state->Reg[tar] = (a1 - a2)&0xFFFF | (((b1 - b2)&0xFFFF)<< 0x10); + state->Reg[tar] = ((a1 - a2) & 0xFFFF) | (((b1 - b2)&0xFFFF)<< 0x10); return 1; } else if ((instr & 0xFF0) == 0xf10)//sadd16 @@ -5736,7 +5736,7 @@ L_stm_s_takeabort: s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); s16 b1 = (state->Reg[src2] & 0xFFFF); s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); - state->Reg[tar] = (a1 + a2)&0xFFFF | (((b1 + b2)&0xFFFF)<< 0x10); + state->Reg[tar] = ((a1 + a2) & 0xFFFF) | (((b1 + b2)&0xFFFF)<< 0x10); return 1; } else if ((instr & 0xFF0) == 0xf50)//ssax @@ -5748,7 +5748,7 @@ L_stm_s_takeabort: s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); s16 b1 = (state->Reg[src2] & 0xFFFF); s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); - state->Reg[tar] = (a1 - b2) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10); + state->Reg[tar] = ((a1 + b2) & 0xFFFF) | (((a2 - b1) & 0xFFFF) << 0x10); return 1; } else if ((instr & 0xFF0) == 0xf30)//sasx @@ -5760,7 +5760,7 @@ L_stm_s_takeabort: s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); s16 b1 = (state->Reg[src2] & 0xFFFF); s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); - state->Reg[tar] = (a2 - b1) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10); + state->Reg[tar] = ((a1 - b2) & 0xFFFF) | (((a2 + b1) & 0xFFFF) << 0x10); return 1; } else printf ("Unhandled v6 insn: sadd/ssub\n"); diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 169ab0f1c..fc0b9b72d 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -100,6 +100,8 @@ bool Archive_SDMC::RenameDirectory(const FileSys::Path& src_path, const FileSys: std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const Path& path) const { DEBUG_LOG(FILESYS, "called path=%s", path.DebugStr().c_str()); Directory_SDMC* directory = new Directory_SDMC(this, path); + if (!directory->Open()) + return nullptr; return std::unique_ptr<Directory>(directory); } diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h index e10431337..1bb4101d6 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory.h @@ -42,6 +42,12 @@ public: virtual ~Directory() { } /** + * Open the directory + * @return true if the directory opened correctly + */ + virtual bool Open() = 0; + + /** * List files contained in the directory * @param count Number of entries to return at once in entries * @param entries Buffer to read data into diff --git a/src/core/file_sys/directory_romfs.cpp b/src/core/file_sys/directory_romfs.cpp index 4e8f4c04d..e6d571391 100644 --- a/src/core/file_sys/directory_romfs.cpp +++ b/src/core/file_sys/directory_romfs.cpp @@ -17,6 +17,10 @@ Directory_RomFS::Directory_RomFS() { Directory_RomFS::~Directory_RomFS() { } +bool Directory_RomFS::Open() { + return false; +} + /** * List files contained in the directory * @param count Number of entries to return at once in entries diff --git a/src/core/file_sys/directory_romfs.h b/src/core/file_sys/directory_romfs.h index 4b71c4b13..e2944099e 100644 --- a/src/core/file_sys/directory_romfs.h +++ b/src/core/file_sys/directory_romfs.h @@ -20,6 +20,12 @@ public: ~Directory_RomFS() override; /** + * Open the directory + * @return true if the directory opened correctly + */ + bool Open() override; + + /** * List files contained in the directory * @param count Number of entries to return at once in entries * @param entries Buffer to read data into diff --git a/src/core/file_sys/directory_sdmc.cpp b/src/core/file_sys/directory_sdmc.cpp index 60a197ce9..0f156a127 100644 --- a/src/core/file_sys/directory_sdmc.cpp +++ b/src/core/file_sys/directory_sdmc.cpp @@ -19,15 +19,22 @@ Directory_SDMC::Directory_SDMC(const Archive_SDMC* archive, const Path& path) { // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass // the root directory we set while opening the archive. // For example, opening /../../usr/bin can give the emulated program your installed programs. - std::string absolute_path = archive->GetMountPoint() + path.AsString(); - FileUtil::ScanDirectoryTree(absolute_path, directory); - children_iterator = directory.children.begin(); + this->path = archive->GetMountPoint() + path.AsString(); + } Directory_SDMC::~Directory_SDMC() { Close(); } +bool Directory_SDMC::Open() { + if (!FileUtil::IsDirectory(path)) + return false; + FileUtil::ScanDirectoryTree(path, directory); + children_iterator = directory.children.begin(); + return true; +} + /** * List files contained in the directory * @param count Number of entries to return at once in entries diff --git a/src/core/file_sys/directory_sdmc.h b/src/core/file_sys/directory_sdmc.h index 4520d0401..4c08b0d61 100644 --- a/src/core/file_sys/directory_sdmc.h +++ b/src/core/file_sys/directory_sdmc.h @@ -23,6 +23,12 @@ public: ~Directory_SDMC() override; /** + * Open the directory + * @return true if the directory opened correctly + */ + bool Open() override; + + /** * List files contained in the directory * @param count Number of entries to return at once in entries * @param entries Buffer to read data into @@ -37,6 +43,7 @@ public: bool Close() const override; private: + std::string path; u32 total_entries_in_directory; FileUtil::FSTEntry directory; diff --git a/src/core/file_sys/file_sdmc.cpp b/src/core/file_sys/file_sdmc.cpp index a4b90670a..b01d96e3d 100644 --- a/src/core/file_sys/file_sdmc.cpp +++ b/src/core/file_sys/file_sdmc.cpp @@ -38,12 +38,15 @@ bool File_SDMC::Open() { } std::string mode_string; - if (mode.read_flag && mode.write_flag) + if (mode.create_flag) mode_string = "w+"; + else if (mode.write_flag) + mode_string = "r+"; // Files opened with Write access can be read from else if (mode.read_flag) mode_string = "r"; - else if (mode.write_flag) - mode_string = "w"; + + // Open the file in binary mode, to avoid problems with CR/LF on Windows systems + mode_string += "b"; file = new FileUtil::IOFile(path, mode_string.c_str()); return true; diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index db571b895..ce4f3c854 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -53,7 +53,7 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 // Wait current thread (acquire the arbiter)... case ArbitrationType::WaitIfLessThan: if ((s32)Memory::Read32(address) <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, handle); + Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); HLE::Reschedule(__func__); } break; diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp index 647f0dea9..a875fa7ff 100644 --- a/src/core/hle/kernel/archive.cpp +++ b/src/core/hle/kernel/archive.cpp @@ -421,6 +421,11 @@ ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys: directory->path = path; directory->backend = archive->backend->OpenDirectory(path); + if (!directory->backend) { + return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Permanent); + } + return MakeResult<Handle>(handle); } diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index d07e9761b..5a173e129 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -27,21 +27,7 @@ public: std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex std::string name; ///< Name of mutex (optional) - ResultVal<bool> SyncRequest() override { - // TODO(bunnei): ImplementMe - locked = true; - return MakeResult<bool>(false); - } - - ResultVal<bool> WaitSynchronization() override { - // TODO(bunnei): ImplementMe - bool wait = locked; - if (locked) { - Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); - } - - return MakeResult<bool>(wait); - } + ResultVal<bool> WaitSynchronization() override; }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -49,21 +35,46 @@ public: typedef std::multimap<Handle, Handle> MutexMap; static MutexMap g_mutex_held_locks; -void MutexAcquireLock(Mutex* mutex, Handle thread) { +/** + * Acquires the specified mutex for the specified thread + * @param mutex Mutex that is to be acquired + * @param thread Thread that will acquired + */ +void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) { g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); mutex->lock_thread = thread; } -void MutexAcquireLock(Mutex* mutex) { - Handle thread = GetCurrentThreadHandle(); +bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { MutexAcquireLock(mutex, thread); + Kernel::ResumeThreadFromWait(thread); + return true; +} + +/** + * Resumes a thread waiting for the specified mutex + * @param mutex The mutex that some thread is waiting on + */ +void ResumeWaitingThread(Mutex* mutex) { + // Find the next waiting thread for the mutex... + if (mutex->waiting_threads.empty()) { + // Reset mutex lock thread handle, nothing is waiting + mutex->locked = false; + mutex->lock_thread = -1; + } + else { + // Resume the next waiting thread and re-lock the mutex + std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); + ReleaseMutexForThread(mutex, *iter); + mutex->waiting_threads.erase(iter); + } } void MutexEraseLock(Mutex* mutex) { Handle handle = mutex->GetHandle(); auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { - if ((*iter).second == handle) { + if (iter->second == handle) { g_mutex_held_locks.erase(iter); break; } @@ -71,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) { mutex->lock_thread = -1; } +void ReleaseThreadMutexes(Handle thread) { + auto locked = g_mutex_held_locks.equal_range(thread); + + // Release every mutex that the thread holds, and resume execution on the waiting threads + for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { + Mutex* mutex = g_object_pool.GetFast<Mutex>(iter->second); + ResumeWaitingThread(mutex); + } + + // Erase all the locks that this thread holds + g_mutex_held_locks.erase(thread); +} + bool LockMutex(Mutex* mutex) { // Mutex alread locked? if (mutex->locked) { @@ -80,26 +104,9 @@ bool LockMutex(Mutex* mutex) { return true; } -bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { - MutexAcquireLock(mutex, thread); - Kernel::ResumeThreadFromWait(thread); - return true; -} - bool ReleaseMutex(Mutex* mutex) { MutexEraseLock(mutex); - - // Find the next waiting thread for the mutex... - while (!mutex->waiting_threads.empty()) { - std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); - ReleaseMutexForThread(mutex, *iter); - mutex->waiting_threads.erase(iter); - } - - // Reset mutex lock thread handle, nothing is waiting - mutex->locked = false; - mutex->lock_thread = -1; - + ResumeWaitingThread(mutex); return true; } @@ -157,4 +164,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { return handle; } +ResultVal<bool> Mutex::WaitSynchronization() { + bool wait = locked; + if (locked) { + Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); + } + else { + // Lock the mutex when the first thread accesses it + locked = true; + MutexAcquireLock(this); + } + + return MakeResult<bool>(wait); +} } // namespace diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 155449f95..7f4909a6e 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -24,4 +24,10 @@ ResultCode ReleaseMutex(Handle handle); */ Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); +/** + * Releases all the mutexes held by the specified thread + * @param thread Thread that is holding the mutexes + */ +void ReleaseThreadMutexes(Handle thread); + } // namespace diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 8d65dc84d..492b917e1 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -14,6 +14,7 @@ #include "core/hle/hle.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/mutex.h" #include "core/hle/result.h" #include "core/mem_map.h" @@ -63,6 +64,7 @@ public: WaitType wait_type; Handle wait_handle; + VAddr wait_address; std::vector<Handle> waiting_threads; @@ -126,6 +128,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { } t->wait_type = WAITTYPE_NONE; t->wait_handle = 0; + t->wait_address = 0; } /// Change a thread to "ready" state @@ -146,16 +149,25 @@ void ChangeReadyState(Thread* t, bool ready) { } /// Verify that a thread has not been released from waiting -inline bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { +static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { _dbg_assert_(KERNEL, thread != nullptr); return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting()); } +/// Verify that a thread has not been released from waiting (with wait address) +static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { + _dbg_assert_(KERNEL, thread != nullptr); + return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address); +} + /// Stops the current thread ResultCode StopThread(Handle handle, const char* reason) { Thread* thread = g_object_pool.Get<Thread>(handle); if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); + // Release all the mutexes that this thread holds + ReleaseThreadMutexes(handle); + ChangeReadyState(thread, false); thread->status = THREADSTATUS_DORMANT; for (Handle waiting_handle : thread->waiting_threads) { @@ -169,6 +181,7 @@ ResultCode StopThread(Handle handle, const char* reason) { // Stopped threads are never waiting. thread->wait_type = WAITTYPE_NONE; thread->wait_handle = 0; + thread->wait_address = 0; return RESULT_SUCCESS; } @@ -197,12 +210,12 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { for (Handle handle : thread_queue) { Thread* thread = g_object_pool.Get<Thread>(handle); - // TODO(bunnei): Verify arbiter address... - if (!VerifyWait(thread, WAITTYPE_ARB, arbiter)) + if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) continue; if (thread == nullptr) continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. + if(thread->current_priority <= priority) { highest_priority_thread = handle; priority = thread->current_priority; @@ -222,8 +235,7 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) { for (Handle handle : thread_queue) { Thread* thread = g_object_pool.Get<Thread>(handle); - // TODO(bunnei): Verify arbiter address... - if (VerifyWait(thread, WAITTYPE_ARB, arbiter)) + if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) ResumeThreadFromWait(handle); } } @@ -277,11 +289,6 @@ Thread* NextThread() { return Kernel::g_object_pool.Get<Thread>(next); } -/** - * Puts the current thread in the wait state for the given type - * @param wait_type Type of wait - * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread - */ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { Thread* thread = GetCurrentThread(); thread->wait_type = wait_type; @@ -289,6 +296,11 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } +void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) { + WaitCurrentThread(wait_type, wait_handle); + GetCurrentThread()->wait_address = wait_address; +} + /// Resumes a thread from waiting by marking it as "ready" void ResumeThreadFromWait(Handle handle) { Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); @@ -339,6 +351,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio thread->processor_id = processor_id; thread->wait_type = WAITTYPE_NONE; thread->wait_handle = 0; + thread->wait_address = 0; thread->name = name; return thread; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 53a19d779..be7adface 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -5,6 +5,9 @@ #pragma once #include "common/common_types.h" + +#include "core/mem_map.h" + #include "core/hle/kernel/kernel.h" #include "core/hle/result.h" @@ -85,6 +88,14 @@ Handle GetCurrentThreadHandle(); */ void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); +/** + * Puts the current thread in the wait state for the given type + * @param wait_type Type of wait + * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread + * @param wait_address Arbitration address used to resume from wait + */ +void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address); + /// Put current thread in a wait state - on WaitSynchronization void WaitThread_Synchronization(); diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg_u.cpp index d6b586ea0..82bab5797 100644 --- a/src/core/hle/service/cfg_u.cpp +++ b/src/core/hle/service/cfg_u.cpp @@ -11,33 +11,38 @@ namespace CFG_U { -static const std::array<const char*, 187> country_codes = { - nullptr, "JP", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 0-7 - "AI", "AG", "AR", "AW", "BS", "BB", "BZ", "BO", // 8-15 - "BR", "VG", "CA", "KY", "CL", "CO", "CR", "DM", // 16-23 - "DO", "EC", "SV", "GF", "GD", "GP", "GT", "GY", // 24-31 - "HT", "HN", "JM", "MQ", "MX", "MS", "AN", "NI", // 32-39 - "PA", "PY", "PE", "KN", "LC", "VC", "SR", "TT", // 40-47 - "TC", "US", "UY", "VI", "VE", nullptr, nullptr, nullptr, // 48-55 - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 56-63 - "AL", "AU", "AT", "BE", "BA", "BW", "BG", "HR", // 64-71 - "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", // 72-79 - "HU", "IS", "IE", "IT", "LV", "LS", "LI", "LT", // 80-87 - "LU", "MK", "MT", "ME", "MZ", "NA", "NL", "NZ", // 88-95 - "NO", "PL", "PT", "RO", "RU", "RS", "SK", "SI", // 96-103 - "ZA", "ES", "SZ", "SE", "CH", "TR", "GB", "ZM", // 104-111 - "ZW", "AZ", "MR", "ML", "NE", "TD", "SD", "ER", // 112-119 - "DJ", "SO", "AD", "GI", "GG", "IM", "JE", "MC", // 120-127 - "TW", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 128-135 - "KR", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 136-143 - "HK", "MO", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 144-151 - "ID", "SG", "TH", "PH", "MY", nullptr, nullptr, nullptr, // 152-159 - "CN", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 160-167 - "AE", "IN", "EG", "OM", "QA", "KW", "SA", "SY", // 168-175 - "BH", "JO", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 176-183 - "SM", "VA", "BM", // 184-186 +// TODO(Link Mauve): use a constexpr once MSVC starts supporting it. +#define C(code) ((code)[0] | ((code)[1] << 8)) + +static const std::array<u16, 187> country_codes = { + 0, C("JP"), 0, 0, 0, 0, 0, 0, // 0-7 + C("AI"), C("AG"), C("AR"), C("AW"), C("BS"), C("BB"), C("BZ"), C("BO"), // 8-15 + C("BR"), C("VG"), C("CA"), C("KY"), C("CL"), C("CO"), C("CR"), C("DM"), // 16-23 + C("DO"), C("EC"), C("SV"), C("GF"), C("GD"), C("GP"), C("GT"), C("GY"), // 24-31 + C("HT"), C("HN"), C("JM"), C("MQ"), C("MX"), C("MS"), C("AN"), C("NI"), // 32-39 + C("PA"), C("PY"), C("PE"), C("KN"), C("LC"), C("VC"), C("SR"), C("TT"), // 40-47 + C("TC"), C("US"), C("UY"), C("VI"), C("VE"), 0, 0, 0, // 48-55 + 0, 0, 0, 0, 0, 0, 0, 0, // 56-63 + C("AL"), C("AU"), C("AT"), C("BE"), C("BA"), C("BW"), C("BG"), C("HR"), // 64-71 + C("CY"), C("CZ"), C("DK"), C("EE"), C("FI"), C("FR"), C("DE"), C("GR"), // 72-79 + C("HU"), C("IS"), C("IE"), C("IT"), C("LV"), C("LS"), C("LI"), C("LT"), // 80-87 + C("LU"), C("MK"), C("MT"), C("ME"), C("MZ"), C("NA"), C("NL"), C("NZ"), // 88-95 + C("NO"), C("PL"), C("PT"), C("RO"), C("RU"), C("RS"), C("SK"), C("SI"), // 96-103 + C("ZA"), C("ES"), C("SZ"), C("SE"), C("CH"), C("TR"), C("GB"), C("ZM"), // 104-111 + C("ZW"), C("AZ"), C("MR"), C("ML"), C("NE"), C("TD"), C("SD"), C("ER"), // 112-119 + C("DJ"), C("SO"), C("AD"), C("GI"), C("GG"), C("IM"), C("JE"), C("MC"), // 120-127 + C("TW"), 0, 0, 0, 0, 0, 0, 0, // 128-135 + C("KR"), 0, 0, 0, 0, 0, 0, 0, // 136-143 + C("HK"), C("MO"), 0, 0, 0, 0, 0, 0, // 144-151 + C("ID"), C("SG"), C("TH"), C("PH"), C("MY"), 0, 0, 0, // 152-159 + C("CN"), 0, 0, 0, 0, 0, 0, 0, // 160-167 + C("AE"), C("IN"), C("EG"), C("OM"), C("QA"), C("KW"), C("SA"), C("SY"), // 168-175 + C("BH"), C("JO"), 0, 0, 0, 0, 0, 0, // 176-183 + C("SM"), C("VA"), C("BM") // 184-186 }; +#undef C + /** * CFG_User::GetCountryCodeString service function * Inputs: @@ -50,20 +55,14 @@ static void GetCountryCodeString(Service::Interface* self) { u32* cmd_buffer = Service::GetCommandBuffer(); u32 country_code_id = cmd_buffer[1]; - if (country_code_id >= country_codes.size()) { + if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) { ERROR_LOG(KERNEL, "requested country code id=%d is invalid", country_code_id); cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; return; } - - const char* code = country_codes[country_code_id]; - if (code != nullptr) { - cmd_buffer[1] = 0; - cmd_buffer[2] = code[0] | (code[1] << 8); - } else { - cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; - DEBUG_LOG(KERNEL, "requested country code id=%d is not set", country_code_id); - } + + cmd_buffer[1] = 0; + cmd_buffer[2] = country_codes[country_code_id]; } /** @@ -77,20 +76,25 @@ static void GetCountryCodeString(Service::Interface* self) { static void GetCountryCodeID(Service::Interface* self) { u32* cmd_buffer = Service::GetCommandBuffer(); u16 country_code = cmd_buffer[1]; - u16 country_code_id = -1; + u16 country_code_id = 0; - for (u32 i = 0; i < country_codes.size(); ++i) { - const char* code_string = country_codes[i]; + // The following algorithm will fail if the first country code isn't 0. + _dbg_assert_(HLE, country_codes[0] == 0); - if (code_string != nullptr) { - u16 code = code_string[0] | (code_string[1] << 8); - if (code == country_code) { - country_code_id = i; - break; - } + for (size_t id = 0; id < country_codes.size(); ++id) { + if (country_codes[id] == country_code) { + country_code_id = id; + break; } } + if (0 == country_code_id) { + ERROR_LOG(KERNEL, "requested country code name=%c%c is invalid", country_code & 0xff, country_code >> 8); + cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; + cmd_buffer[2] = 0xFFFF; + return; + } + cmd_buffer[1] = 0; cmd_buffer[2] = country_code_id; } diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 72be4c817..e89c8aae3 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -12,6 +12,7 @@ namespace DSP_DSP { +static u32 read_pipe_count; static Handle semaphore_event; static Handle interrupt_event; @@ -108,6 +109,48 @@ void WriteReg0x10(Service::Interface* self) { DEBUG_LOG(KERNEL, "(STUBBED) called"); } +/** + * DSP_DSP::ReadPipeIfPossible service function + * Inputs: + * 1 : Unknown + * 2 : Unknown + * 3 : Size in bytes of read (observed only lower half word used) + * 0x41 : Virtual address to read from DSP pipe to in memory + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Number of bytes read from pipe + */ +void ReadPipeIfPossible(Service::Interface* self) { + u32* cmd_buff = Service::GetCommandBuffer(); + + u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size + VAddr addr = cmd_buff[0x41]; + + // Canned DSP responses that games expect. These were taken from HW by 3dmoo team. + // TODO: Remove this hack :) + static const std::array<u16, 16> canned_read_pipe = { + 0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540, + 0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58 + }; + + u32 initial_size = read_pipe_count; + + for (unsigned offset = 0; offset < size; offset += sizeof(u16)) { + if (read_pipe_count < canned_read_pipe.size()) { + Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]); + read_pipe_count++; + } else { + ERROR_LOG(KERNEL, "canned read pipe log exceeded!"); + break; + } + } + + cmd_buff[1] = 0; // No error + cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16); + + DEBUG_LOG(KERNEL, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010040, nullptr, "RecvData"}, {0x00020040, nullptr, "RecvDataIsReady"}, @@ -119,7 +162,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000B0000, nullptr, "CheckSemaphoreRequest"}, {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"}, {0x000D0082, nullptr, "WriteProcessPipe"}, - {0x001000C0, nullptr, "ReadPipeIfPossible"}, + {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"}, {0x001100C2, LoadComponent, "LoadComponent"}, {0x00120000, nullptr, "UnloadComponent"}, {0x00130082, nullptr, "FlushDataCache"}, @@ -142,6 +185,7 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { semaphore_event = Kernel::CreateEvent(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event"); interrupt_event = 0; + read_pipe_count = 0; Register(FunctionTable, ARRAY_SIZE(FunctionTable)); } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index de1bd3f61..34eabac45 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -162,7 +162,8 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!"); - cmd_buff[2] = g_thread_id++; // ThreadID + cmd_buff[1] = 0x2A07; // Value verified by 3dmoo team, purpose unknown, but needed for GSP init + cmd_buff[2] = g_thread_id++; // Thread ID cmd_buff[4] = g_shared_memory; // GSP shared memory Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct? @@ -172,6 +173,7 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) { * Signals that the specified interrupt type has occurred to userland code * @param interrupt_id ID of interrupt that is being signalled * @todo This should probably take a thread_id parameter and only signal this thread? + * @todo This probably does not belong in the GSP module, instead move to video_core */ void SignalInterrupt(InterruptId interrupt_id) { if (0 == g_interrupt_event) { @@ -210,6 +212,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { memcpy(Memory::GetPointer(command.dma_request.dest_address), Memory::GetPointer(command.dma_request.source_address), command.dma_request.size); + SignalInterrupt(InterruptId::DMA); break; // ctrulib homebrew sends all relevant command list data with this command, @@ -218,13 +221,13 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { case CommandId::SET_COMMAND_LIST_LAST: { auto& params = command.set_command_list_last; + WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3); - WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size >> 3); + WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size); // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1); - SignalInterrupt(InterruptId::P3D); break; } @@ -242,6 +245,8 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3); WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2); WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2); + + SignalInterrupt(InterruptId::PSC0); break; } @@ -255,14 +260,9 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); - // TODO(bunnei): Signalling all of these interrupts here is totally wrong, but it seems to - // work well enough for running demos. Need to figure out how these all work and trigger - // them correctly. - SignalInterrupt(InterruptId::PSC0); + // TODO(bunnei): Determine if these interrupts should be signalled here. SignalInterrupt(InterruptId::PSC1); SignalInterrupt(InterruptId::PPF); - SignalInterrupt(InterruptId::P3D); - SignalInterrupt(InterruptId::DMA); // Update framebuffer information if requested for (int screen_id = 0; screen_id < 2; ++screen_id) { @@ -305,6 +305,8 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { /// This triggers handling of the GX command written to the command buffer in shared memory. static void TriggerCmdReqQueue(Service::Interface* self) { + DEBUG_LOG(GSP, "called"); + // Iterate through each thread's command queue... for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id); @@ -320,6 +322,9 @@ static void TriggerCmdReqQueue(Service::Interface* self) { command_buffer->number_commands = command_buffer->number_commands - 1; } } + + u32* cmd_buff = Service::GetCommandBuffer(); + cmd_buff[1] = 0; // No error } const Interface::FunctionInfo FunctionTable[] = { diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 20e7fb4d3..3a7d6c469 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -10,6 +10,7 @@ #include <string> #include "common/common.h" +#include "common/string_util.h" #include "core/mem_map.h" #include "core/hle/kernel/kernel.h" @@ -79,21 +80,20 @@ public: u32* cmd_buff = GetCommandBuffer(); auto itr = m_functions.find(cmd_buff[0]); - if (itr == m_functions.end()) { - ERROR_LOG(OSHLE, "unknown/unimplemented function: port=%s, command=0x%08X", - GetPortName().c_str(), cmd_buff[0]); + if (itr == m_functions.end() || itr->second.func == nullptr) { + // Number of params == bits 0-5 + bits 6-11 + int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); - // TODO(bunnei): Hack - ignore error - u32* cmd_buff = Service::GetCommandBuffer(); - cmd_buff[1] = 0; - return MakeResult<bool>(false); - } - if (itr->second.func == nullptr) { - ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s", - GetPortName().c_str(), itr->second.name.c_str()); + std::string error = "unknown/unimplemented function '%s': port=%s"; + for (int i = 1; i <= num_params; ++i) { + error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); + } + + std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name; + + ERROR_LOG(OSHLE, error.c_str(), name.c_str(), GetPortName().c_str()); // TODO(bunnei): Hack - ignore error - u32* cmd_buff = Service::GetCommandBuffer(); cmd_buff[1] = 0; return MakeResult<bool>(false); } diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index af5e1b39b..77557e582 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -154,8 +154,7 @@ inline void Write(u32 addr, const T data) { if (config.trigger & 1) { u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress())); - u32 size = config.size << 3; - Pica::CommandProcessor::ProcessCommandList(buffer, size); + Pica::CommandProcessor::ProcessCommandList(buffer, config.size); } break; } diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index 3fa7b9ccf..86cd5e680 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -169,7 +169,7 @@ struct Regs { INSERT_PADDING_WORDS(0x331); struct { - // command list size + // command list size (in bytes) u32 size; INSERT_PADDING_WORDS(0x1); diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index ea001673a..73a4f1e53 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp @@ -6,7 +6,6 @@ #include "core/hw/hw.h" #include "core/hw/gpu.h" -#include "core/hw/ndma.h" namespace HW { @@ -40,11 +39,6 @@ template <typename T> inline void Read(T &var, const u32 addr) { switch (addr & 0xFFFFF000) { - // TODO(bunnei): What is the virtual address of NDMA? - // case VADDR_NDMA: - // NDMA::Read(var, addr); - // break; - case VADDR_GPU: GPU::Read(var, addr); break; @@ -58,11 +52,6 @@ template <typename T> inline void Write(u32 addr, const T data) { switch (addr & 0xFFFFF000) { - // TODO(bunnei): What is the virtual address of NDMA? - // case VADDR_NDMA - // NDMA::Write(addr, data); - // break; - case VADDR_GPU: GPU::Write(addr, data); break; @@ -87,13 +76,11 @@ template void Write<u8>(u32 addr, const u8 data); /// Update hardware void Update() { GPU::Update(); - NDMA::Update(); } /// Initialize hardware void Init() { GPU::Init(); - NDMA::Init(); NOTICE_LOG(HW, "initialized OK"); } diff --git a/src/core/hw/ndma.cpp b/src/core/hw/ndma.cpp deleted file mode 100644 index 593e5de30..000000000 --- a/src/core/hw/ndma.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#include "common/common_types.h" - -#include "core/hw/ndma.h" - -namespace NDMA { - -template <typename T> -inline void Read(T &var, const u32 addr) { - ERROR_LOG(NDMA, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); -} - -template <typename T> -inline void Write(u32 addr, const T data) { - ERROR_LOG(NDMA, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr); -} - -// Explicitly instantiate template functions because we aren't defining this in the header: - -template void Read<u64>(u64 &var, const u32 addr); -template void Read<u32>(u32 &var, const u32 addr); -template void Read<u16>(u16 &var, const u32 addr); -template void Read<u8>(u8 &var, const u32 addr); - -template void Write<u64>(u32 addr, const u64 data); -template void Write<u32>(u32 addr, const u32 data); -template void Write<u16>(u32 addr, const u16 data); -template void Write<u8>(u32 addr, const u8 data); - -/// Update hardware -void Update() { -} - -/// Initialize hardware -void Init() { - NOTICE_LOG(GPU, "initialized OK"); -} - -/// Shutdown hardware -void Shutdown() { - NOTICE_LOG(GPU, "shutdown OK"); -} - -} // namespace diff --git a/src/core/hw/ndma.h b/src/core/hw/ndma.h deleted file mode 100644 index d8fa3d40b..000000000 --- a/src/core/hw/ndma.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" - -namespace NDMA { - -template <typename T> -inline void Read(T &var, const u32 addr); - -template <typename T> -inline void Write(u32 addr, const T data); - -/// Update hardware -void Update(); - -/// Initialize hardware -void Init(); - -/// Shutdown hardware -void Shutdown(); - -} // namespace diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp new file mode 100644 index 000000000..7ef146359 --- /dev/null +++ b/src/core/loader/3dsx.cpp @@ -0,0 +1,236 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include <algorithm> +#include <vector> + +#include "core/file_sys/archive_romfs.h" +#include "core/loader/elf.h" +#include "core/loader/ncch.h" +#include "core/hle/kernel/archive.h" +#include "core/mem_map.h" + +#include "3dsx.h" + + +namespace Loader { + + +/** + * File layout: + * - File header + * - Code, rodata and data relocation table headers + * - Code segment + * - Rodata segment + * - Loadable (non-BSS) part of the data segment + * - Code relocation table + * - Rodata relocation table + * - Data relocation table + * + * Memory layout before relocations are applied: + * [0..codeSegSize) -> code segment + * [codeSegSize..rodataSegSize) -> rodata segment + * [rodataSegSize..dataSegSize) -> data segment + * + * Memory layout after relocations are applied: well, however the loader sets it up :) + * The entrypoint is always the start of the code segment. + * The BSS section must be cleared manually by the application. + */ +enum THREEDSX_Error { + ERROR_NONE = 0, + ERROR_READ = 1, + ERROR_FILE = 2, + ERROR_ALLOC = 3 +}; +static const u32 RELOCBUFSIZE = 512; + +// File header +static const u32 THREEDSX_MAGIC = 0x58534433; // '3DSX' +#pragma pack(1) +struct THREEDSX_Header +{ + u32 magic; + u16 header_size, reloc_hdr_size; + u32 format_ver; + u32 flags; + + // Sizes of the code, rodata and data segments + + // size of the BSS section (uninitialized latter half of the data segment) + u32 code_seg_size, rodata_seg_size, data_seg_size, bss_size; +}; + +// Relocation header: all fields (even extra unknown fields) are guaranteed to be relocation counts. +struct THREEDSX_RelocHdr +{ + // # of absolute relocations (that is, fix address to post-relocation memory layout) + u32 cross_segment_absolute; + // # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched) + u32 cross_segment_relative; + // more? + + // Relocations are written in this order: + // - Absolute relocations + // - Relative relocations +}; + +// Relocation entry: from the current pointer, skip X words and patch Y words +struct THREEDSX_Reloc +{ + u16 skip, patch; +}; +#pragma pack() + +struct THREEloadinfo +{ + u8* seg_ptrs[3]; // code, rodata & data + u32 seg_addrs[3]; + u32 seg_sizes[3]; +}; + +class THREEDSXReader { +public: + static int Load3DSXFile(const std::string& filename, u32 base_addr); +}; + +static u32 TranslateAddr(u32 addr, THREEloadinfo *loadinfo, u32* offsets) +{ + if (addr < offsets[0]) + return loadinfo->seg_addrs[0] + addr; + if (addr < offsets[1]) + return loadinfo->seg_addrs[1] + addr - offsets[0]; + return loadinfo->seg_addrs[2] + addr - offsets[1]; +} + +int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) +{ + FileUtil::IOFile file(filename, "rb"); + if (!file.IsOpen()) { + return ERROR_FILE; + } + THREEDSX_Header hdr; + if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr)) + return ERROR_READ; + + THREEloadinfo loadinfo; + //loadinfo segments must be a multiple of 0x1000 + loadinfo.seg_sizes[0] = (hdr.code_seg_size + 0xFFF) &~0xFFF; + loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) &~0xFFF; + loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) &~0xFFF; + u32 offsets[2] = { loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] }; + u32 data_load_size = (hdr.data_seg_size - hdr.bss_size + 0xFFF) &~0xFFF; + u32 bss_load_size = loadinfo.seg_sizes[2] - data_load_size; + u32 n_reloc_tables = hdr.reloc_hdr_size / 4; + std::vector<u8> all_mem(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2] + 3 * n_reloc_tables); + + loadinfo.seg_addrs[0] = base_addr; + loadinfo.seg_addrs[1] = loadinfo.seg_addrs[0] + loadinfo.seg_sizes[0]; + loadinfo.seg_addrs[2] = loadinfo.seg_addrs[1] + loadinfo.seg_sizes[1]; + loadinfo.seg_ptrs[0] = &all_mem[0]; + loadinfo.seg_ptrs[1] = loadinfo.seg_ptrs[0] + loadinfo.seg_sizes[0]; + loadinfo.seg_ptrs[2] = loadinfo.seg_ptrs[1] + loadinfo.seg_sizes[1]; + + // Skip header for future compatibility + file.Seek(hdr.header_size, SEEK_SET); + + // Read the relocation headers + u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size); + + for (u32 current_segment = 0; current_segment < 3; current_segment++) { + if (file.ReadBytes(&relocs[current_segment*n_reloc_tables], n_reloc_tables * 4) != n_reloc_tables * 4) + return ERROR_READ; + } + + // Read the segments + if (file.ReadBytes(loadinfo.seg_ptrs[0], hdr.code_seg_size) != hdr.code_seg_size) + return ERROR_READ; + if (file.ReadBytes(loadinfo.seg_ptrs[1], hdr.rodata_seg_size) != hdr.rodata_seg_size) + return ERROR_READ; + if (file.ReadBytes(loadinfo.seg_ptrs[2], hdr.data_seg_size - hdr.bss_size) != hdr.data_seg_size - hdr.bss_size) + return ERROR_READ; + + // BSS clear + memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size); + + // Relocate the segments + for (u32 current_segment = 0; current_segment < 3; current_segment++) { + for (u32 current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) { + u32 n_relocs = relocs[current_segment*n_reloc_tables + current_segment_reloc_table]; + if (current_segment_reloc_table >= 2) { + // We are not using this table - ignore it because we don't know what it dose + file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR); + continue; + } + static THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; + + u32* pos = (u32*)loadinfo.seg_ptrs[current_segment]; + u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4); + + while (n_relocs) { + u32 remaining = std::min(RELOCBUFSIZE, n_relocs); + n_relocs -= remaining; + + if (file.ReadBytes(reloc_table, remaining*sizeof(THREEDSX_Reloc)) != remaining*sizeof(THREEDSX_Reloc)) + return ERROR_READ; + + for (u32 current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) { + DEBUG_LOG(LOADER, "(t=%d,skip=%u,patch=%u)\n", + current_segment_reloc_table, (u32)reloc_table[current_inprogress].skip, (u32)reloc_table[current_inprogress].patch); + pos += reloc_table[current_inprogress].skip; + s32 num_patches = reloc_table[current_inprogress].patch; + while (0 < num_patches && pos < end_pos) { + u32 in_addr = (char*)pos - (char*)&all_mem[0]; + u32 addr = TranslateAddr(*pos, &loadinfo, offsets); + DEBUG_LOG(LOADER, "Patching %08X <-- rel(%08X,%d) (%08X)\n", + base_addr + in_addr, addr, current_segment_reloc_table, *pos); + switch (current_segment_reloc_table) { + case 0: *pos = (addr); break; + case 1: *pos = (addr - in_addr); break; + default: break; //this should never happen + } + pos++; + num_patches--; + } + } + } + } + } + + // Write the data + memcpy(Memory::GetPointer(base_addr), &all_mem[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]); + + DEBUG_LOG(LOADER, "CODE: %u pages\n", loadinfo.seg_sizes[0] / 0x1000); + DEBUG_LOG(LOADER, "RODATA: %u pages\n", loadinfo.seg_sizes[1] / 0x1000); + DEBUG_LOG(LOADER, "DATA: %u pages\n", data_load_size / 0x1000); + DEBUG_LOG(LOADER, "BSS: %u pages\n", bss_load_size / 0x1000); + + return ERROR_NONE; +} + + /// AppLoader_DSX constructor + AppLoader_THREEDSX::AppLoader_THREEDSX(const std::string& filename) : filename(filename) { + } + + /// AppLoader_DSX destructor + AppLoader_THREEDSX::~AppLoader_THREEDSX() { + } + + /** + * Loads a 3DSX file + * @return Success on success, otherwise Error + */ + ResultStatus AppLoader_THREEDSX::Load() { + INFO_LOG(LOADER, "Loading 3DSX file %s...", filename.c_str()); + FileUtil::IOFile file(filename, "rb"); + if (file.IsOpen()) { + + THREEDSXReader reader; + reader.Load3DSXFile(filename, 0x00100000); + Kernel::LoadExec(0x00100000); + } else { + return ResultStatus::Error; + } + return ResultStatus::Success; + } + +} // namespace Loader diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h new file mode 100644 index 000000000..848d3ef8a --- /dev/null +++ b/src/core/loader/3dsx.h @@ -0,0 +1,32 @@ +// Copyright 2014 Dolphin Emulator Project / Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "core/loader/loader.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Loader namespace + +namespace Loader { + +/// Loads an 3DSX file +class AppLoader_THREEDSX final : public AppLoader { +public: + AppLoader_THREEDSX(const std::string& filename); + ~AppLoader_THREEDSX() override; + + /** + * Load the bootable file + * @return ResultStatus result of function + */ + ResultStatus Load() override; + +private: + std::string filename; + bool is_loaded; +}; + +} // namespace Loader diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index a268e021a..174397b05 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -5,6 +5,7 @@ #include <memory> #include "core/file_sys/archive_romfs.h" +#include "core/loader/3dsx.h" #include "core/loader/elf.h" #include "core/loader/ncch.h" #include "core/hle/kernel/archive.h" @@ -42,6 +43,8 @@ FileType IdentifyFile(const std::string &filename) { return FileType::CCI; } else if (extension == ".bin") { return FileType::BIN; + } else if (extension == ".3dsx") { + return FileType::THREEDSX; } return FileType::Unknown; } @@ -56,6 +59,10 @@ ResultStatus LoadFile(const std::string& filename) { switch (IdentifyFile(filename)) { + //3DSX file format... + case FileType::THREEDSX: + return AppLoader_THREEDSX(filename).Load(); + // Standard ELF file format... case FileType::ELF: return AppLoader_ELF(filename).Load(); diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 68f843005..0f836d285 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -22,6 +22,7 @@ enum class FileType { CIA, ELF, BIN, + THREEDSX, //3DSX }; /// Return type for functions in Loader namespace diff --git a/src/core/mem_map.h b/src/core/mem_map.h index c9529f84c..f17afb60d 100644 --- a/src/core/mem_map.h +++ b/src/core/mem_map.h @@ -26,12 +26,10 @@ enum : u32 { FCRAM_PADDR_END = (FCRAM_PADDR + FCRAM_SIZE), ///< FCRAM end of physical space FCRAM_VADDR = 0x08000000, ///< FCRAM virtual address FCRAM_VADDR_END = (FCRAM_VADDR + FCRAM_SIZE), ///< FCRAM end of virtual space - FCRAM_MASK = (FCRAM_SIZE - 1), ///< FCRAM mask SHARED_MEMORY_SIZE = 0x04000000, ///< Shared memory size SHARED_MEMORY_VADDR = 0x10000000, ///< Shared memory SHARED_MEMORY_VADDR_END = (SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE), - SHARED_MEMORY_MASK = (SHARED_MEMORY_SIZE - 1), DSP_MEMORY_SIZE = 0x00080000, ///< DSP memory size DSP_MEMORY_VADDR = 0x1FF00000, ///< DSP memory virtual address @@ -39,37 +37,31 @@ enum : u32 { CONFIG_MEMORY_SIZE = 0x00001000, ///< Configuration memory size CONFIG_MEMORY_VADDR = 0x1FF80000, ///< Configuration memory virtual address CONFIG_MEMORY_VADDR_END = (CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE), - CONFIG_MEMORY_MASK = (CONFIG_MEMORY_SIZE - 1), KERNEL_MEMORY_SIZE = 0x00001000, ///< Kernel memory size KERNEL_MEMORY_VADDR = 0xFFFF0000, ///< Kernel memory where the kthread objects etc are KERNEL_MEMORY_VADDR_END = (KERNEL_MEMORY_VADDR + KERNEL_MEMORY_SIZE), - KERNEL_MEMORY_MASK = (KERNEL_MEMORY_SIZE - 1), EXEFS_CODE_SIZE = 0x03F00000, EXEFS_CODE_VADDR = 0x00100000, ///< ExeFS:/.code is loaded here EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE), - EXEFS_CODE_MASK = 0x03FFFFFF, // Region of FCRAM used by system SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB SYSTEM_MEMORY_VADDR = 0x04000000, SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE), - SYSTEM_MEMORY_MASK = 0x03FFFFFF, HEAP_SIZE = FCRAM_SIZE, ///< Application heap size //HEAP_PADDR = HEAP_GSP_SIZE, //HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE), HEAP_VADDR = 0x08000000, HEAP_VADDR_END = (HEAP_VADDR + HEAP_SIZE), - HEAP_MASK = (HEAP_SIZE - 1), HEAP_GSP_SIZE = 0x02000000, ///< GSP heap size... TODO: Define correctly? HEAP_GSP_VADDR = 0x14000000, HEAP_GSP_VADDR_END = (HEAP_GSP_VADDR + HEAP_GSP_SIZE), HEAP_GSP_PADDR = 0x00000000, HEAP_GSP_PADDR_END = (HEAP_GSP_PADDR + HEAP_GSP_SIZE), - HEAP_GSP_MASK = (HEAP_GSP_SIZE - 1), HARDWARE_IO_SIZE = 0x01000000, HARDWARE_IO_PADDR = 0x10000000, ///< IO physical address start @@ -82,12 +74,10 @@ enum : u32 { VRAM_VADDR = 0x1F000000, VRAM_PADDR_END = (VRAM_PADDR + VRAM_SIZE), VRAM_VADDR_END = (VRAM_VADDR + VRAM_SIZE), - VRAM_MASK = 0x007FFFFF, SCRATCHPAD_SIZE = 0x00004000, ///< Typical stack size - TODO: Read from exheader SCRATCHPAD_VADDR_END = 0x10000000, SCRATCHPAD_VADDR = (SCRATCHPAD_VADDR_END - SCRATCHPAD_SIZE), ///< Stack space - SCRATCHPAD_MASK = (SCRATCHPAD_SIZE - 1), ///< Scratchpad memory mask }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp index e8747840c..1887bcedb 100644 --- a/src/core/mem_map_funcs.cpp +++ b/src/core/mem_map_funcs.cpp @@ -56,7 +56,7 @@ inline void Read(T &var, const VAddr vaddr) { // Kernel memory command buffer if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { - var = *((const T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK]); + var = *((const T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR]); // Hardware I/O register reads // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space @@ -65,23 +65,23 @@ inline void Read(T &var, const VAddr vaddr) { // ExeFS:/.code is loaded here } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { - var = *((const T*)&g_exefs_code[vaddr & EXEFS_CODE_MASK]); + var = *((const T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR]); // FCRAM - GSP heap } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { - var = *((const T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK]); + var = *((const T*)&g_heap_gsp[vaddr - HEAP_GSP_VADDR]); // FCRAM - application heap } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { - var = *((const T*)&g_heap[vaddr & HEAP_MASK]); + var = *((const T*)&g_heap[vaddr - HEAP_VADDR]); // Shared memory } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { - var = *((const T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK]); + var = *((const T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR]); // System memory } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { - var = *((const T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK]); + var = *((const T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR]); // Config memory } else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) { @@ -89,7 +89,7 @@ inline void Read(T &var, const VAddr vaddr) { // VRAM } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { - var = *((const T*)&g_vram[vaddr & VRAM_MASK]); + var = *((const T*)&g_vram[vaddr - VRAM_VADDR]); } else { ERROR_LOG(MEMMAP, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, vaddr); @@ -101,7 +101,7 @@ inline void Write(const VAddr vaddr, const T data) { // Kernel memory command buffer if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { - *(T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK] = data; + *(T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR] = data; // Hardware I/O register writes // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space @@ -110,27 +110,27 @@ inline void Write(const VAddr vaddr, const T data) { // ExeFS:/.code is loaded here } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { - *(T*)&g_exefs_code[vaddr & EXEFS_CODE_MASK] = data; + *(T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR] = data; // FCRAM - GSP heap } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { - *(T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK] = data; + *(T*)&g_heap_gsp[vaddr - HEAP_GSP_VADDR] = data; // FCRAM - application heap } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { - *(T*)&g_heap[vaddr & HEAP_MASK] = data; + *(T*)&g_heap[vaddr - HEAP_VADDR] = data; // Shared memory } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { - *(T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK] = data; + *(T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR] = data; // System memory } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { - *(T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK] = data; + *(T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR] = data; // VRAM } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { - *(T*)&g_vram[vaddr & VRAM_MASK] = data; + *(T*)&g_vram[vaddr - VRAM_VADDR] = data; //} else if ((vaddr & 0xFFF00000) == 0x1FF00000) { // _assert_msg_(MEMMAP, false, "umimplemented write to DSP memory"); @@ -148,31 +148,31 @@ inline void Write(const VAddr vaddr, const T data) { u8 *GetPointer(const VAddr vaddr) { // Kernel memory command buffer if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { - return g_kernel_mem + (vaddr & KERNEL_MEMORY_MASK); + return g_kernel_mem + (vaddr - KERNEL_MEMORY_VADDR); // ExeFS:/.code is loaded here } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { - return g_exefs_code + (vaddr & EXEFS_CODE_MASK); + return g_exefs_code + (vaddr - EXEFS_CODE_VADDR); // FCRAM - GSP heap } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { - return g_heap_gsp + (vaddr & HEAP_GSP_MASK); + return g_heap_gsp + (vaddr - HEAP_GSP_VADDR); // FCRAM - application heap } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { - return g_heap + (vaddr & HEAP_MASK); + return g_heap + (vaddr - HEAP_VADDR); // Shared memory } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { - return g_shared_mem + (vaddr & SHARED_MEMORY_MASK); + return g_shared_mem + (vaddr - SHARED_MEMORY_VADDR); // System memory } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { - return g_system_mem + (vaddr & SYSTEM_MEMORY_MASK); + return g_system_mem + (vaddr - SYSTEM_MEMORY_VADDR); // VRAM } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { - return g_vram + (vaddr & VRAM_MASK); + return g_vram + (vaddr - VRAM_VADDR); } else { ERROR_LOG(MEMMAP, "unknown GetPointer @ 0x%08x", vaddr); |