diff options
Diffstat (limited to 'src/core/hle')
-rw-r--r-- | src/core/hle/function_wrappers.h | 6 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.cpp | 6 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.h | 1 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.cpp | 63 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.h | 19 | ||||
-rw-r--r-- | src/core/hle/kernel/timer.cpp | 142 | ||||
-rw-r--r-- | src/core/hle/kernel/timer.h | 47 | ||||
-rw-r--r-- | src/core/hle/service/gsp_gpu.cpp | 5 | ||||
-rw-r--r-- | src/core/hle/service/service.cpp | 14 | ||||
-rw-r--r-- | src/core/hle/service/service.h | 15 | ||||
-rw-r--r-- | src/core/hle/svc.cpp | 40 |
11 files changed, 318 insertions, 40 deletions
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 0f822f84b..8eb4f252b 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -135,6 +135,12 @@ template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() { FuncReturn(retval); } +template<s32 func(u32, s64, s64)> void Wrap() { + s64 param1 = ((u64)PARAM(3) << 32) | PARAM(2); + s64 param2 = ((u64)PARAM(4) << 32) | PARAM(1); + FuncReturn(func(PARAM(0), param1, param2)); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Function wrappers that return type u32 diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e59ed1b57..391e833c0 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -9,6 +9,7 @@ #include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/timer.h" namespace Kernel { @@ -105,12 +106,13 @@ void HandleTable::Clear() { /// Initialize the kernel void Init() { Kernel::ThreadingInit(); + Kernel::TimersInit(); } /// Shutdown the kernel void Shutdown() { Kernel::ThreadingShutdown(); - + Kernel::TimersShutdown(); g_handle_table.Clear(); // Free all kernel objects } @@ -124,6 +126,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/kernel.h b/src/core/hle/kernel/kernel.h index 7f86fd07d..3e381d776 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -39,6 +39,7 @@ enum class HandleType : u32 { Process = 8, AddressArbiter = 9, Semaphore = 10, + Timer = 11 }; enum { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 872df2d14..954bd09a0 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; @@ -303,6 +308,37 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_addres GetCurrentThread()->wait_address = wait_address; } +/// Event type for the thread wake up event +static int ThreadWakeupEventType = -1; + +/// Callback that will wake up the thread it was scheduled for +static void ThreadWakeupCallback(u64 parameter, int cycles_late) { + Handle handle = static_cast<Handle>(parameter); + Thread* thread = Kernel::g_handle_table.Get<Thread>(handle); + if (thread == nullptr) { + LOG_ERROR(Kernel, "Thread doesn't exist %u", handle); + return; + } + + Kernel::ResumeThreadFromWait(handle); +} + + +void WakeThreadAfterDelay(Handle handle, s64 nanoseconds) { + // Don't schedule a wakeup if the thread wants to wait forever + if (nanoseconds == -1) + return; + + Thread* thread = Kernel::g_handle_table.Get<Thread>(handle); + if (thread == nullptr) { + LOG_ERROR(Kernel, "Thread doesn't exist %u", handle); + return; + } + + u64 microseconds = nanoseconds / 1000; + CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, handle); +} + /// Resumes a thread from waiting by marking it as "ready" void ResumeThreadFromWait(Handle handle) { Thread* thread = Kernel::g_handle_table.Get<Thread>(handle); @@ -444,7 +480,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; @@ -487,14 +530,15 @@ void Reschedule() { thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle); } } +} - // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put - // to sleep. So, we'll just immediately set it to "ready" again after an attempted context - // switch has occurred. This results in the current thread yielding on a sleep once, and then it - // will immediately be placed back in the queue for execution. - - if (CheckWaitType(prev, WAITTYPE_SLEEP)) - 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) { @@ -512,6 +556,7 @@ ResultCode GetThreadId(u32* thread_id, Handle handle) { void ThreadingInit() { next_thread_id = INITIAL_THREAD_ID; + ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); } void ThreadingShutdown() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 0e1397cd9..58bd85ac6 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -43,6 +43,7 @@ enum WaitType { WAITTYPE_MUTEX, WAITTYPE_SYNCH, WAITTYPE_ARB, + WAITTYPE_TIMER, }; namespace Kernel { @@ -88,6 +89,13 @@ Handle GetCurrentThreadHandle(); void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); /** + * Schedules an event to wake up the specified thread after the specified delay. + * @param handle The thread handle. + * @param nanoseconds The time this thread will be allowed to sleep for. + */ +void WakeThreadAfterDelay(Handle handle, s64 nanoseconds); + +/** * 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 @@ -104,6 +112,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/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp new file mode 100644 index 000000000..7ac669e31 --- /dev/null +++ b/src/core/hle/kernel/timer.cpp @@ -0,0 +1,142 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <set> + +#include "common/common.h" + +#include "core/core_timing.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/timer.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +class Timer : public Object { +public: + std::string GetTypeName() const override { return "Timer"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::Timer; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + ResetType reset_type; ///< The ResetType of this timer + + bool signaled; ///< Whether the timer has been signaled or not + std::set<Handle> waiting_threads; ///< Threads that are waiting for the timer + std::string name; ///< Name of timer (optional) + + u64 initial_delay; ///< The delay until the timer fires for the first time + u64 interval_delay; ///< The delay until the timer fires after the first time + + ResultVal<bool> WaitSynchronization() override { + bool wait = !signaled; + if (wait) { + waiting_threads.insert(GetCurrentThreadHandle()); + Kernel::WaitCurrentThread(WAITTYPE_TIMER, GetHandle()); + } + return MakeResult<bool>(wait); + } +}; + +/** + * Creates a timer. + * @param handle Reference to handle for the newly created timer + * @param reset_type ResetType describing how to create timer + * @param name Optional name of timer + * @return Newly created Timer object + */ +Timer* CreateTimer(Handle& handle, const ResetType reset_type, const std::string& name) { + Timer* timer = new Timer; + + handle = Kernel::g_handle_table.Create(timer).ValueOr(INVALID_HANDLE); + + timer->reset_type = reset_type; + timer->signaled = false; + timer->name = name; + timer->initial_delay = 0; + timer->interval_delay = 0; + return timer; +} + +ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name) { + CreateTimer(*handle, reset_type, name); + return RESULT_SUCCESS; +} + +ResultCode ClearTimer(Handle handle) { + Timer* timer = Kernel::g_handle_table.Get<Timer>(handle); + + if (timer == nullptr) + return InvalidHandle(ErrorModule::Kernel); + + timer->signaled = false; + return RESULT_SUCCESS; +} + +/// The event type of the generic timer callback event +static int TimerCallbackEventType = -1; + +/// The timer callback event, called when a timer is fired +static void TimerCallback(u64 timer_handle, int cycles_late) { + Timer* timer = Kernel::g_handle_table.Get<Timer>(timer_handle); + + if (timer == nullptr) { + LOG_CRITICAL(Kernel, "Callback fired for invalid timer %u", timer_handle); + return; + } + + LOG_TRACE(Kernel, "Timer %u fired", timer_handle); + + timer->signaled = true; + + // Resume all waiting threads + for (Handle thread : timer->waiting_threads) + ResumeThreadFromWait(thread); + + timer->waiting_threads.clear(); + + if (timer->reset_type == RESETTYPE_ONESHOT) + timer->signaled = false; + + if (timer->interval_delay != 0) { + // Reschedule the timer with the interval delay + u64 interval_microseconds = timer->interval_delay / 1000; + CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late, + TimerCallbackEventType, timer_handle); + } +} + +ResultCode SetTimer(Handle handle, s64 initial, s64 interval) { + Timer* timer = Kernel::g_handle_table.Get<Timer>(handle); + + if (timer == nullptr) + return InvalidHandle(ErrorModule::Kernel); + + timer->initial_delay = initial; + timer->interval_delay = interval; + + u64 initial_microseconds = initial / 1000; + CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), TimerCallbackEventType, handle); + return RESULT_SUCCESS; +} + +ResultCode CancelTimer(Handle handle) { + Timer* timer = Kernel::g_handle_table.Get<Timer>(handle); + + if (timer == nullptr) + return InvalidHandle(ErrorModule::Kernel); + + CoreTiming::UnscheduleEvent(TimerCallbackEventType, handle); + return RESULT_SUCCESS; +} + +void TimersInit() { + TimerCallbackEventType = CoreTiming::RegisterEvent("TimerCallback", TimerCallback); +} + +void TimersShutdown() { +} + +} // namespace diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h new file mode 100644 index 000000000..f8aa66b60 --- /dev/null +++ b/src/core/hle/kernel/timer.h @@ -0,0 +1,47 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +#include "core/hle/kernel/kernel.h" +#include "core/hle/svc.h" + +namespace Kernel { + +/** + * Cancels a timer + * @param handle Handle of the timer to cancel + */ +ResultCode CancelTimer(Handle handle); + +/** + * Starts a timer with the specified initial delay and interval + * @param handle Handle of the timer to start + * @param initial Delay until the timer is first fired + * @param interval Delay until the timer is fired after the first time + */ +ResultCode SetTimer(Handle handle, s64 initial, s64 interval); + +/** + * Clears a timer + * @param handle Handle of the timer to clear + */ +ResultCode ClearTimer(Handle handle); + +/** + * Creates a timer + * @param Handle to newly created Timer object + * @param reset_type ResetType describing how to create the timer + * @param name Optional name of timer + * @return ResultCode of the error + */ +ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name="Unknown"); + +/// Initializes the required variables for timers +void TimersInit(); +/// Tears down the timer variables +void TimersShutdown(); +} // namespace 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/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/core/hle/svc.cpp b/src/core/hle/svc.cpp index 2b948d016..051f2d2c6 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -15,6 +15,7 @@ #include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/timer.h" #include "core/hle/function_wrappers.h" #include "core/hle/result.h" @@ -139,6 +140,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { /// Wait for the given handles to synchronize, timeout after the specified nanoseconds static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { + // TODO(bunnei): Do something with nano_seconds, currently ignoring this bool unlock_all = true; bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated @@ -343,12 +345,42 @@ static Result ClearEvent(Handle evt) { return Kernel::ClearEvent(evt).raw; } +/// Creates a timer +static Result CreateTimer(Handle* handle, u32 reset_type) { + ResultCode res = Kernel::CreateTimer(handle, static_cast<ResetType>(reset_type)); + LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", + reset_type, *handle); + return res.raw; +} + +/// Clears a timer +static Result ClearTimer(Handle handle) { + LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); + return Kernel::ClearTimer(handle).raw; +} + +/// Starts a timer +static Result SetTimer(Handle handle, s64 initial, s64 interval) { + LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); + return Kernel::SetTimer(handle, initial, interval).raw; +} + +/// Cancels a timer +static Result CancelTimer(Handle handle) { + LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); + return Kernel::CancelTimer(handle).raw; +} + /// Sleep the current thread static void SleepThread(s64 nanoseconds) { LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); // Sleep current thread and check for next thread to schedule Kernel::WaitCurrentThread(WAITTYPE_SLEEP); + + // Create an event to wake the thread up after the specified nanosecond delay has passed + Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThreadHandle(), nanoseconds); + HLE::Reschedule(__func__); } @@ -396,10 +428,10 @@ const HLE::FunctionDef SVC_Table[] = { {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, - {0x1A, nullptr, "CreateTimer"}, - {0x1B, nullptr, "SetTimer"}, - {0x1C, nullptr, "CancelTimer"}, - {0x1D, nullptr, "ClearTimer"}, + {0x1A, HLE::Wrap<CreateTimer>, "CreateTimer"}, + {0x1B, HLE::Wrap<SetTimer>, "SetTimer"}, + {0x1C, HLE::Wrap<CancelTimer>, "CancelTimer"}, + {0x1D, HLE::Wrap<ClearTimer>, "ClearTimer"}, {0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"}, {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"}, {0x20, nullptr, "UnmapMemoryBlock"}, |