aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs
diff options
context:
space:
mode:
authorAc_K <Acoustik666@gmail.com>2018-07-19 06:03:53 +0200
committergdkchan <gab.dark.100@gmail.com>2018-07-19 01:03:53 -0300
commit8b685b12f0b7a901139999dff17b24b049b9084b (patch)
treeaa1aa952bd84786d641bce6162ff37e749339f54 /Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs
parent6a69001aa26fe6f3688fa98901ca40e641743d55 (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.cs112
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
+ }
+}