diff options
| author | Ac_K <Acoustik666@gmail.com> | 2018-07-19 06:03:53 +0200 |
|---|---|---|
| committer | gdkchan <gab.dark.100@gmail.com> | 2018-07-19 01:03:53 -0300 |
| commit | 8b685b12f0b7a901139999dff17b24b049b9084b (patch) | |
| tree | aa1aa952bd84786d641bce6162ff37e749339f54 /Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs | |
| parent | 6a69001aa26fe6f3688fa98901ca40e641743d55 (diff) | |
Implement SvcWaitForAddress 0x34 (#289)
* Implement SvcWaitForAddress 0x34
Currently needed by Sonic Mania Plus
* Fix mistake
* read-decrement-write locked
Diffstat (limited to 'Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs')
| -rw-r--r-- | Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs b/Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs new file mode 100644 index 00000000..ce9ef0cd --- /dev/null +++ b/Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs @@ -0,0 +1,112 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using Ryujinx.HLE.OsHle.Handles; + +using static Ryujinx.HLE.OsHle.ErrorCode; + +namespace Ryujinx.HLE.OsHle.Kernel +{ + static class AddressArbiter + { + static ulong WaitForAddress(Process Process, AThreadState ThreadState, long Address, ulong Timeout) + { + KThread CurrentThread = Process.GetThread(ThreadState.Tpidr); + + Process.Scheduler.SetReschedule(CurrentThread.ProcessorId); + + CurrentThread.ArbiterWaitAddress = Address; + CurrentThread.ArbiterSignaled = false; + + Process.Scheduler.EnterWait(CurrentThread, NsTimeConverter.GetTimeMs(Timeout)); + + if (!CurrentThread.ArbiterSignaled) + { + return MakeError(ErrorModule.Kernel, KernelErr.Timeout); + } + + return 0; + } + + public static ulong WaitForAddressIfLessThan(Process Process, + AThreadState ThreadState, + AMemory Memory, + long Address, + int Value, + ulong Timeout, + bool ShouldDecrement) + { + Memory.SetExclusive(ThreadState, Address); + + int CurrentValue = Memory.ReadInt32(Address); + + while (true) + { + if (Memory.TestExclusive(ThreadState, Address)) + { + if (CurrentValue < Value) + { + if (ShouldDecrement) + { + Memory.WriteInt32(Address, CurrentValue - 1); + } + + Memory.ClearExclusiveForStore(ThreadState); + } + else + { + Memory.ClearExclusiveForStore(ThreadState); + + return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + } + + break; + } + + Memory.SetExclusive(ThreadState, Address); + + CurrentValue = Memory.ReadInt32(Address); + } + + if (Timeout == 0) + { + return MakeError(ErrorModule.Kernel, KernelErr.Timeout); + } + + return WaitForAddress(Process, ThreadState, Address, Timeout); + } + + public static ulong WaitForAddressIfEqual(Process Process, + AThreadState ThreadState, + AMemory Memory, + long Address, + int Value, + ulong Timeout) + { + if (Memory.ReadInt32(Address) != Value) + { + return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + } + + if (Timeout == 0) + { + return MakeError(ErrorModule.Kernel, KernelErr.Timeout); + } + + return WaitForAddress(Process, ThreadState, Address, Timeout); + } + } + + enum ArbitrationType : int + { + WaitIfLessThan, + DecrementAndWaitIfLessThan, + WaitIfEqual + } + + enum SignalType : int + { + Signal, + IncrementAndSignalIfEqual, + ModifyByWaitingCountAndSignalIfEqual + } +} |
