diff options
Diffstat (limited to '')
-rw-r--r-- | src/core/arm/dyncom/arm_dyncom.cpp | 6 | ||||
-rw-r--r-- | src/core/arm/dyncom/arm_dyncom.h | 3 | ||||
-rw-r--r-- | src/core/arm/dyncom/arm_dyncom_interpreter.cpp | 25 | ||||
-rw-r--r-- | src/core/core.cpp | 19 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.cpp | 63 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.h | 18 | ||||
-rw-r--r-- | src/core/hle/service/gsp_gpu.cpp | 5 | ||||
-rw-r--r-- | src/core/hle/svc.cpp | 4 | ||||
-rw-r--r-- | src/core/system.cpp | 4 |
10 files changed, 110 insertions, 39 deletions
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index c779e3fd4..31eb879a2 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -15,7 +15,7 @@ const static cpu_config_t s_arm11_cpu_info = { "armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE }; -ARM_DynCom::ARM_DynCom() : ticks(0) { +ARM_DynCom::ARM_DynCom() { state = std::unique_ptr<ARMul_State>(new ARMul_State); ARMul_EmulateInit(); @@ -74,11 +74,11 @@ void ARM_DynCom::SetCPSR(u32 cpsr) { } u64 ARM_DynCom::GetTicks() const { - return ticks; + // TODO(Subv): Remove ARM_DynCom::GetTicks() and use CoreTiming::GetTicks() directly once ARMemu is gone + return CoreTiming::GetTicks(); } void ARM_DynCom::AddTicks(u64 ticks) { - this->ticks += ticks; down_count -= ticks; if (down_count < 0) CoreTiming::Advance(); diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 7284dcd07..9e102a46e 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h @@ -89,8 +89,5 @@ public: void ExecuteInstructions(int num_instructions) override; private: - std::unique_ptr<ARMul_State> state; - u64 ticks; - }; diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 426fc6474..e3ca02e98 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -5891,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)); @@ -6699,10 +6696,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) { { if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { umaal_inst* const inst_cream = (umaal_inst*)inst_base->component; - const u32 rm = RM; - const u32 rn = RN; - const u32 rd_lo = RDLO; - const u32 rd_hi = RDHI; + const u64 rm = RM; + const u64 rn = RN; + const u64 rd_lo = RDLO; + const u64 rd_hi = RDHI; const u64 result = (rm * rn) + rd_lo + rd_hi; RDLO = (result & 0xFFFFFFFF); diff --git a/src/core/core.cpp b/src/core/core.cpp index 8ac4481cc..ff506d67d 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" @@ -16,14 +17,22 @@ namespace Core { -static u64 last_ticks = 0; ///< Last CPU ticks -static ARM_Disasm* disasm = nullptr; ///< ARM disassembler ARM_Interface* g_app_core = nullptr; ///< ARM11 application core 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(); @@ -49,7 +58,6 @@ void Stop() { int Init() { LOG_DEBUG(Core, "initialized OK"); - disasm = new ARM_Disasm(); g_sys_core = new ARM_Interpreter(); switch (Settings::values.cpu_core) { @@ -62,13 +70,10 @@ int Init() { break; } - last_ticks = Core::g_app_core->GetTicks(); - return 0; } void Shutdown() { - delete disasm; delete g_app_core; delete g_sys_core; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 084fd03ae..391e833c0 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -126,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/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 81736a866..58bd85ac6 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -89,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 @@ -105,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/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/svc.cpp b/src/core/hle/svc.cpp index 0cf3de75c..cdcdea36d 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -372,6 +372,10 @@ static void SleepThread(s64 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__); } diff --git a/src/core/system.cpp b/src/core/system.cpp index d6188f055..f4c2df1cd 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -21,11 +21,11 @@ void UpdateState(State state) { void Init(EmuWindow* emu_window) { Core::Init(); + CoreTiming::Init(); Memory::Init(); HW::Init(); Kernel::Init(); HLE::Init(); - CoreTiming::Init(); VideoCore::Init(emu_window); } @@ -38,11 +38,11 @@ void RunLoopUntil(u64 global_cycles) { void Shutdown() { VideoCore::Shutdown(); - CoreTiming::Shutdown(); HLE::Shutdown(); Kernel::Shutdown(); HW::Shutdown(); Memory::Shutdown(); + CoreTiming::Shutdown(); Core::Shutdown(); } |