diff options
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/session.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 57 |
2 files changed, 59 insertions, 2 deletions
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 6ddaf970e..26b086f87 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -32,6 +32,10 @@ constexpr u32 CallingPidDesc() { return 0x20; } +constexpr u32 TransferHandleDesc() { + return 0x20; +} + constexpr u32 StaticBufferDesc(u32 size, unsigned int buffer_id) { return 0x2 | (size << 14) | ((buffer_id & 0xF) << 10); } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 3f6bec5fa..9dea995f4 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -182,6 +182,48 @@ static void PriorityBoostStarvedThreads() { } /** + * Gets the registers for timeout parameter of the next WaitSynchronization call. + * @param thread a pointer to the thread that is ready to call WaitSynchronization + * @returns a tuple of two register pointers to low and high part of the timeout parameter + */ +static std::tuple<u32*, u32*> GetWaitSynchTimeoutParameterRegister(Thread* thread) { + bool thumb_mode = (thread->context.cpsr & TBIT) != 0; + u16 thumb_inst = Memory::Read16(thread->context.pc & 0xFFFFFFFE); + u32 inst = Memory::Read32(thread->context.pc & 0xFFFFFFFC) & 0x0FFFFFFF; + + if ((thumb_mode && thumb_inst == 0xDF24) || (!thumb_mode && inst == 0x0F000024)) { + // svc #0x24 (WaitSynchronization1) + return std::make_tuple(&thread->context.cpu_registers[2], &thread->context.cpu_registers[3]); + } else if ((thumb_mode && thumb_inst == 0xDF25) || (!thumb_mode && inst == 0x0F000025)) { + // svc #0x25 (WaitSynchronizationN) + return std::make_tuple(&thread->context.cpu_registers[0], &thread->context.cpu_registers[4]); + } + + UNREACHABLE(); +} + +/** + * Updates the WaitSynchronization timeout paramter according to the difference + * between ticks of the last WaitSynchronization call and the incoming one. + * @param timeout_low a pointer to the register for the low part of the timeout parameter + * @param timeout_high a pointer to the register for the high part of the timeout parameter + * @param last_tick tick of the last WaitSynchronization call + */ +static void UpdateTimeoutParameter(u32* timeout_low, u32* timeout_high, u64 last_tick) { + s64 timeout = ((s64)*timeout_high << 32) | *timeout_low; + + if (timeout != -1) { + timeout -= cyclesToUs(CoreTiming::GetTicks() - last_tick) * 1000; // in nanoseconds + + if (timeout < 0) + timeout = 0; + + *timeout_low = timeout & 0xFFFFFFFF; + *timeout_high = timeout >> 32; + } +} + +/** * Switches the CPU's active thread context to that of the specified thread * @param new_thread The thread to switch to */ @@ -219,6 +261,13 @@ static void SwitchContext(Thread* new_thread) { // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM new_thread->context.pc -= thumb_mode ? 2 : 4; + + // Get the register for timeout parameter + u32* timeout_low, *timeout_high; + std::tie(timeout_low, timeout_high) = GetWaitSynchTimeoutParameterRegister(new_thread); + + // Update the timeout parameter + UpdateTimeoutParameter(timeout_low, timeout_high, new_thread->last_running_ticks); } // Clean up the thread's wait_objects, they'll be restored if needed during @@ -542,8 +591,12 @@ void Reschedule() { HLE::DoneRescheduling(); - // Don't bother switching to the same thread - if (next == cur) + // Don't bother switching to the same thread. + // But if the thread was waiting on objects, we still need to switch it + // to perform PC modification, change state to RUNNING, etc. + // This occurs in the case when an object the thread is waiting on immediately wakes up + // the current thread before Reschedule() is called. + if (next == cur && (next == nullptr || next->waitsynch_waited == false)) return; if (cur && next) { |
