summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/hle/kernel/thread.cpp49
1 files changed, 49 insertions, 0 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 3f6bec5fa..492c821e3 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -182,6 +182,48 @@ static void PriorityBoostStarvedThreads() {
182} 182}
183 183
184/** 184/**
185 * Gets the registers for timeout parameter of the next WaitSynchronization call.
186 * @param thread a pointer to the thread that is ready to call WaitSynchronization
187 * @returns a tuple of two register pointers to low and high part of the timeout parameter
188 */
189static std::tuple<u32*, u32*> GetWaitSynchTimeoutParameterRegister(Thread* thread) {
190 bool thumb_mode = (thread->context.cpsr & TBIT) != 0;
191 u16 thumb_inst = Memory::Read16(thread->context.pc & 0xFFFFFFFE);
192 u32 inst = Memory::Read32(thread->context.pc & 0xFFFFFFFC) & 0x0FFFFFFF;
193
194 if ((thumb_mode && thumb_inst == 0xDF24) || (!thumb_mode && inst == 0x0F000024)) {
195 // svc #0x24 (WaitSynchronization1)
196 return std::make_tuple(&thread->context.cpu_registers[2], &thread->context.cpu_registers[3]);
197 } else if ((thumb_mode && thumb_inst == 0xDF25) || (!thumb_mode && inst == 0x0F000025)) {
198 // svc #0x25 (WaitSynchronizationN)
199 return std::make_tuple(&thread->context.cpu_registers[0], &thread->context.cpu_registers[4]);
200 }
201
202 UNREACHABLE();
203}
204
205/**
206 * Updates the WaitSynchronization timeout paramter according to the difference
207 * between ticks of the last WaitSynchronization call and the incoming one.
208 * @param timeout_low a pointer to the register for the low part of the timeout parameter
209 * @param timeout_high a pointer to the register for the high part of the timeout parameter
210 * @param last_tick tick of the last WaitSynchronization call
211 */
212static void UpdateTimeoutParameter(u32* timeout_low, u32* timeout_high, u64 last_tick) {
213 s64 timeout = ((s64)*timeout_high << 32) | *timeout_low;
214
215 if (timeout != -1) {
216 timeout -= cyclesToUs(CoreTiming::GetTicks() - last_tick) * 1000; // in nanoseconds
217
218 if (timeout < 0)
219 timeout = 0;
220
221 *timeout_low = timeout & 0xFFFFFFFF;
222 *timeout_high = timeout >> 32;
223 }
224}
225
226/**
185 * Switches the CPU's active thread context to that of the specified thread 227 * Switches the CPU's active thread context to that of the specified thread
186 * @param new_thread The thread to switch to 228 * @param new_thread The thread to switch to
187 */ 229 */
@@ -219,6 +261,13 @@ static void SwitchContext(Thread* new_thread) {
219 261
220 // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM 262 // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM
221 new_thread->context.pc -= thumb_mode ? 2 : 4; 263 new_thread->context.pc -= thumb_mode ? 2 : 4;
264
265 // Get the register for timeout parameter
266 u32* timeout_low, *timeout_high;
267 std::tie(timeout_low, timeout_high) = GetWaitSynchTimeoutParameterRegister(new_thread);
268
269 // Update the timeout parameter
270 UpdateTimeoutParameter(timeout_low, timeout_high, new_thread->last_running_ticks);
222 } 271 }
223 272
224 // Clean up the thread's wait_objects, they'll be restored if needed during 273 // Clean up the thread's wait_objects, they'll be restored if needed during