Ryujinx/Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs
Ac_K 8b685b12f0 Implement SvcWaitForAddress 0x34 (#289)
* Implement SvcWaitForAddress 0x34

Currently needed by Sonic Mania Plus

* Fix mistake

* read-decrement-write locked
2018-07-19 01:03:53 -03:00

113 lines
3.6 KiB
C#

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
}
}