Refactor SVC handler (#540)
* Refactor SVC handler * Get rid of KernelErr * Split kernel code files into multiple folders
This commit is contained in:
parent
2534a7f10c
commit
0039bb6394
105 changed files with 1894 additions and 1982 deletions
7
Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs
Normal file
7
Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
interface IKFutureSchedulerObject
|
||||
{
|
||||
void TimeUp();
|
||||
}
|
||||
}
|
42
Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
Normal file
42
Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
class KAutoObject
|
||||
{
|
||||
protected Horizon System;
|
||||
|
||||
public KAutoObject(Horizon system)
|
||||
{
|
||||
System = system;
|
||||
}
|
||||
|
||||
public virtual KernelResult SetName(string name)
|
||||
{
|
||||
if (!System.AutoObjectNames.TryAdd(name, this))
|
||||
{
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
public static KernelResult RemoveName(Horizon system, string name)
|
||||
{
|
||||
if (!system.AutoObjectNames.TryRemove(name, out _))
|
||||
{
|
||||
return KernelResult.NotFound;
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
public static KAutoObject FindNamedObject(Horizon system, string name)
|
||||
{
|
||||
if (system.AutoObjectNames.TryGetValue(name, out KAutoObject obj))
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
147
Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs
Normal file
147
Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs
Normal file
|
@ -0,0 +1,147 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
class KResourceLimit
|
||||
{
|
||||
private const int Time10SecondsMs = 10000;
|
||||
|
||||
private long[] _current;
|
||||
private long[] _limit;
|
||||
private long[] _available;
|
||||
|
||||
private object _lockObj;
|
||||
|
||||
private LinkedList<KThread> _waitingThreads;
|
||||
|
||||
private int _waitingThreadsCount;
|
||||
|
||||
private Horizon _system;
|
||||
|
||||
public KResourceLimit(Horizon system)
|
||||
{
|
||||
_current = new long[(int)LimitableResource.Count];
|
||||
_limit = new long[(int)LimitableResource.Count];
|
||||
_available = new long[(int)LimitableResource.Count];
|
||||
|
||||
_lockObj = new object();
|
||||
|
||||
_waitingThreads = new LinkedList<KThread>();
|
||||
|
||||
_system = system;
|
||||
}
|
||||
|
||||
public bool Reserve(LimitableResource resource, ulong amount)
|
||||
{
|
||||
return Reserve(resource, (long)amount);
|
||||
}
|
||||
|
||||
public bool Reserve(LimitableResource resource, long amount)
|
||||
{
|
||||
return Reserve(resource, amount, KTimeManager.ConvertMillisecondsToNanoseconds(Time10SecondsMs));
|
||||
}
|
||||
|
||||
public bool Reserve(LimitableResource resource, long amount, long timeout)
|
||||
{
|
||||
long endTimePoint = KTimeManager.ConvertNanosecondsToMilliseconds(timeout);
|
||||
|
||||
endTimePoint += PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
bool success = false;
|
||||
|
||||
int index = GetIndex(resource);
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
long newCurrent = _current[index] + amount;
|
||||
|
||||
while (newCurrent > _limit[index] && _available[index] + amount <= _limit[index])
|
||||
{
|
||||
_waitingThreadsCount++;
|
||||
|
||||
KConditionVariable.Wait(_system, _waitingThreads, _lockObj, timeout);
|
||||
|
||||
_waitingThreadsCount--;
|
||||
|
||||
newCurrent = _current[index] + amount;
|
||||
|
||||
if (timeout >= 0 && PerformanceCounter.ElapsedMilliseconds > endTimePoint)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newCurrent <= _limit[index])
|
||||
{
|
||||
_current[index] = newCurrent;
|
||||
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void Release(LimitableResource resource, ulong amount)
|
||||
{
|
||||
Release(resource, (long)amount);
|
||||
}
|
||||
|
||||
public void Release(LimitableResource resource, long amount)
|
||||
{
|
||||
Release(resource, amount, amount);
|
||||
}
|
||||
|
||||
private void Release(LimitableResource resource, long usedAmount, long availableAmount)
|
||||
{
|
||||
int index = GetIndex(resource);
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
_current [index] -= usedAmount;
|
||||
_available[index] -= availableAmount;
|
||||
|
||||
if (_waitingThreadsCount > 0)
|
||||
{
|
||||
KConditionVariable.NotifyAll(_system, _waitingThreads);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long GetRemainingValue(LimitableResource resource)
|
||||
{
|
||||
int index = GetIndex(resource);
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
return _limit[index] - _current[index];
|
||||
}
|
||||
}
|
||||
|
||||
public KernelResult SetLimitValue(LimitableResource resource, long limit)
|
||||
{
|
||||
int index = GetIndex(resource);
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
if (_current[index] <= limit)
|
||||
{
|
||||
_limit[index] = limit;
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetIndex(LimitableResource resource)
|
||||
{
|
||||
return (int)resource;
|
||||
}
|
||||
}
|
||||
}
|
35
Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs
Normal file
35
Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
class KSynchronizationObject : KAutoObject
|
||||
{
|
||||
public LinkedList<KThread> WaitingThreads;
|
||||
|
||||
public KSynchronizationObject(Horizon system) : base(system)
|
||||
{
|
||||
WaitingThreads = new LinkedList<KThread>();
|
||||
}
|
||||
|
||||
public LinkedListNode<KThread> AddWaitingThread(KThread thread)
|
||||
{
|
||||
return WaitingThreads.AddLast(thread);
|
||||
}
|
||||
|
||||
public void RemoveWaitingThread(LinkedListNode<KThread> node)
|
||||
{
|
||||
WaitingThreads.Remove(node);
|
||||
}
|
||||
|
||||
public virtual void Signal()
|
||||
{
|
||||
System.Synchronization.SignalObject(this);
|
||||
}
|
||||
|
||||
public virtual bool IsSignaled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
143
Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
Normal file
143
Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
Normal file
|
@ -0,0 +1,143 @@
|
|||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
class KTimeManager : IDisposable
|
||||
{
|
||||
private class WaitingObject
|
||||
{
|
||||
public IKFutureSchedulerObject Object { get; private set; }
|
||||
|
||||
public long TimePoint { get; private set; }
|
||||
|
||||
public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
|
||||
{
|
||||
Object = schedulerObj;
|
||||
TimePoint = timePoint;
|
||||
}
|
||||
}
|
||||
|
||||
private List<WaitingObject> _waitingObjects;
|
||||
|
||||
private AutoResetEvent _waitEvent;
|
||||
|
||||
private bool _keepRunning;
|
||||
|
||||
public KTimeManager()
|
||||
{
|
||||
_waitingObjects = new List<WaitingObject>();
|
||||
|
||||
_keepRunning = true;
|
||||
|
||||
Thread work = new Thread(WaitAndCheckScheduledObjects);
|
||||
|
||||
work.Start();
|
||||
}
|
||||
|
||||
public void ScheduleFutureInvocation(IKFutureSchedulerObject schedulerObj, long timeout)
|
||||
{
|
||||
long timePoint = PerformanceCounter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(timeout);
|
||||
|
||||
lock (_waitingObjects)
|
||||
{
|
||||
_waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
|
||||
}
|
||||
|
||||
_waitEvent.Set();
|
||||
}
|
||||
|
||||
public static long ConvertNanosecondsToMilliseconds(long time)
|
||||
{
|
||||
time /= 1000000;
|
||||
|
||||
if ((ulong)time > int.MaxValue)
|
||||
{
|
||||
return int.MaxValue;
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
public static long ConvertMillisecondsToNanoseconds(long time)
|
||||
{
|
||||
return time * 1000000;
|
||||
}
|
||||
|
||||
public static long ConvertMillisecondsToTicks(long time)
|
||||
{
|
||||
return time * 19200;
|
||||
}
|
||||
|
||||
public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object)
|
||||
{
|
||||
lock (_waitingObjects)
|
||||
{
|
||||
_waitingObjects.RemoveAll(x => x.Object == Object);
|
||||
}
|
||||
}
|
||||
|
||||
private void WaitAndCheckScheduledObjects()
|
||||
{
|
||||
using (_waitEvent = new AutoResetEvent(false))
|
||||
{
|
||||
while (_keepRunning)
|
||||
{
|
||||
WaitingObject next;
|
||||
|
||||
lock (_waitingObjects)
|
||||
{
|
||||
next = _waitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
|
||||
}
|
||||
|
||||
if (next != null)
|
||||
{
|
||||
long timePoint = PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
if (next.TimePoint > timePoint)
|
||||
{
|
||||
_waitEvent.WaitOne((int)(next.TimePoint - timePoint));
|
||||
}
|
||||
|
||||
bool timeUp = PerformanceCounter.ElapsedMilliseconds >= next.TimePoint;
|
||||
|
||||
if (timeUp)
|
||||
{
|
||||
lock (_waitingObjects)
|
||||
{
|
||||
timeUp = _waitingObjects.Remove(next);
|
||||
}
|
||||
}
|
||||
|
||||
if (timeUp)
|
||||
{
|
||||
next.Object.TimeUp();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_waitEvent.WaitOne();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_keepRunning = false;
|
||||
|
||||
_waitEvent?.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
137
Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
Normal file
137
Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
Normal file
|
@ -0,0 +1,137 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
static class KernelInit
|
||||
{
|
||||
public static void InitializeResourceLimit(KResourceLimit resourceLimit)
|
||||
{
|
||||
void EnsureSuccess(KernelResult result)
|
||||
{
|
||||
if (result != KernelResult.Success)
|
||||
{
|
||||
throw new InvalidOperationException($"Unexpected result \"{result}\".");
|
||||
}
|
||||
}
|
||||
|
||||
int kernelMemoryCfg = 0;
|
||||
|
||||
long ramSize = GetRamSize(kernelMemoryCfg);
|
||||
|
||||
EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Memory, ramSize));
|
||||
EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Thread, 800));
|
||||
EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Event, 700));
|
||||
EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.TransferMemory, 200));
|
||||
EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Session, 900));
|
||||
|
||||
if (!resourceLimit.Reserve(LimitableResource.Memory, 0) ||
|
||||
!resourceLimit.Reserve(LimitableResource.Memory, 0x60000))
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected failure reserving memory on resource limit.");
|
||||
}
|
||||
}
|
||||
|
||||
public static KMemoryRegionManager[] GetMemoryRegions()
|
||||
{
|
||||
KMemoryArrange arrange = GetMemoryArrange();
|
||||
|
||||
return new KMemoryRegionManager[]
|
||||
{
|
||||
GetMemoryRegion(arrange.Application),
|
||||
GetMemoryRegion(arrange.Applet),
|
||||
GetMemoryRegion(arrange.Service),
|
||||
GetMemoryRegion(arrange.NvServices)
|
||||
};
|
||||
}
|
||||
|
||||
private static KMemoryRegionManager GetMemoryRegion(KMemoryArrangeRegion region)
|
||||
{
|
||||
return new KMemoryRegionManager(region.Address, region.Size, region.EndAddr);
|
||||
}
|
||||
|
||||
private static KMemoryArrange GetMemoryArrange()
|
||||
{
|
||||
int mcEmemCfg = 0x1000;
|
||||
|
||||
ulong ememApertureSize = (ulong)(mcEmemCfg & 0x3fff) << 20;
|
||||
|
||||
int kernelMemoryCfg = 0;
|
||||
|
||||
ulong ramSize = (ulong)GetRamSize(kernelMemoryCfg);
|
||||
|
||||
ulong ramPart0;
|
||||
ulong ramPart1;
|
||||
|
||||
if (ramSize * 2 > ememApertureSize)
|
||||
{
|
||||
ramPart0 = ememApertureSize / 2;
|
||||
ramPart1 = ememApertureSize / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
ramPart0 = ememApertureSize;
|
||||
ramPart1 = 0;
|
||||
}
|
||||
|
||||
int memoryArrange = 1;
|
||||
|
||||
ulong applicationRgSize;
|
||||
|
||||
switch (memoryArrange)
|
||||
{
|
||||
case 2: applicationRgSize = 0x80000000; break;
|
||||
case 0x11:
|
||||
case 0x21: applicationRgSize = 0x133400000; break;
|
||||
default: applicationRgSize = 0xcd500000; break;
|
||||
}
|
||||
|
||||
ulong appletRgSize;
|
||||
|
||||
switch (memoryArrange)
|
||||
{
|
||||
case 2: appletRgSize = 0x61200000; break;
|
||||
case 3: appletRgSize = 0x1c000000; break;
|
||||
case 0x11: appletRgSize = 0x23200000; break;
|
||||
case 0x12:
|
||||
case 0x21: appletRgSize = 0x89100000; break;
|
||||
default: appletRgSize = 0x1fb00000; break;
|
||||
}
|
||||
|
||||
KMemoryArrangeRegion serviceRg;
|
||||
KMemoryArrangeRegion nvServicesRg;
|
||||
KMemoryArrangeRegion appletRg;
|
||||
KMemoryArrangeRegion applicationRg;
|
||||
|
||||
const ulong nvServicesRgSize = 0x29ba000;
|
||||
|
||||
ulong applicationRgEnd = DramMemoryMap.DramEnd; //- RamPart0;
|
||||
|
||||
applicationRg = new KMemoryArrangeRegion(applicationRgEnd - applicationRgSize, applicationRgSize);
|
||||
|
||||
ulong nvServicesRgEnd = applicationRg.Address - appletRgSize;
|
||||
|
||||
nvServicesRg = new KMemoryArrangeRegion(nvServicesRgEnd - nvServicesRgSize, nvServicesRgSize);
|
||||
appletRg = new KMemoryArrangeRegion(nvServicesRgEnd, appletRgSize);
|
||||
|
||||
//Note: There is an extra region used by the kernel, however
|
||||
//since we are doing HLE we are not going to use that memory, so give all
|
||||
//the remaining memory space to services.
|
||||
ulong serviceRgSize = nvServicesRg.Address - DramMemoryMap.SlabHeapEnd;
|
||||
|
||||
serviceRg = new KMemoryArrangeRegion(DramMemoryMap.SlabHeapEnd, serviceRgSize);
|
||||
|
||||
return new KMemoryArrange(serviceRg, nvServicesRg, appletRg, applicationRg);
|
||||
}
|
||||
|
||||
private static long GetRamSize(int kernelMemoryCfg)
|
||||
{
|
||||
switch ((kernelMemoryCfg >> 16) & 3)
|
||||
{
|
||||
case 1: return 0x180000000;
|
||||
case 2: return 0x200000000;
|
||||
default: return 0x100000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
32
Ryujinx.HLE/HOS/Kernel/Common/KernelResult.cs
Normal file
32
Ryujinx.HLE/HOS/Kernel/Common/KernelResult.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
enum KernelResult
|
||||
{
|
||||
Success = 0,
|
||||
InvalidCapability = 0x1c01,
|
||||
ThreadNotStarted = 0x7201,
|
||||
ThreadTerminating = 0x7601,
|
||||
InvalidSize = 0xca01,
|
||||
InvalidAddress = 0xcc01,
|
||||
OutOfResource = 0xce01,
|
||||
OutOfMemory = 0xd001,
|
||||
HandleTableFull = 0xd201,
|
||||
InvalidMemState = 0xd401,
|
||||
InvalidPermission = 0xd801,
|
||||
InvalidMemRange = 0xdc01,
|
||||
InvalidPriority = 0xe001,
|
||||
InvalidCpuCore = 0xe201,
|
||||
InvalidHandle = 0xe401,
|
||||
UserCopyFailed = 0xe601,
|
||||
InvalidCombination = 0xe801,
|
||||
TimedOut = 0xea01,
|
||||
Cancelled = 0xec01,
|
||||
MaximumExceeded = 0xee01,
|
||||
InvalidEnumValue = 0xf001,
|
||||
NotFound = 0xf201,
|
||||
InvalidThread = 0xf401,
|
||||
InvalidState = 0xfa01,
|
||||
ReservedValue = 0xfc01,
|
||||
ResLimitExceeded = 0x10801
|
||||
}
|
||||
}
|
72
Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
Normal file
72
Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using ChocolArm64.Memory;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
static class KernelTransfer
|
||||
{
|
||||
public static bool UserToKernelInt32(Horizon system, ulong address, out int value)
|
||||
{
|
||||
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (currentProcess.CpuMemory.IsMapped((long)address) &&
|
||||
currentProcess.CpuMemory.IsMapped((long)address + 3))
|
||||
{
|
||||
value = currentProcess.CpuMemory.ReadInt32((long)address);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
value = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool UserToKernelString(Horizon system, ulong address, int size, out string value)
|
||||
{
|
||||
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (currentProcess.CpuMemory.IsMapped((long)address) &&
|
||||
currentProcess.CpuMemory.IsMapped((long)address + size - 1))
|
||||
{
|
||||
value = MemoryHelper.ReadAsciiString(currentProcess.CpuMemory, (long)address, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
value = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool KernelToUserInt32(Horizon system, ulong address, int value)
|
||||
{
|
||||
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (currentProcess.CpuMemory.IsMapped((long)address) &&
|
||||
currentProcess.CpuMemory.IsMapped((long)address + 3))
|
||||
{
|
||||
currentProcess.CpuMemory.WriteInt32ToSharedAddr((long)address, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool KernelToUserInt64(Horizon system, ulong address, long value)
|
||||
{
|
||||
KProcess currentProcess = system.Scheduler.GetCurrentProcess();
|
||||
|
||||
if (currentProcess.CpuMemory.IsMapped((long)address) &&
|
||||
currentProcess.CpuMemory.IsMapped((long)address + 7))
|
||||
{
|
||||
currentProcess.CpuMemory.WriteInt64((long)address, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
13
Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs
Normal file
13
Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
enum LimitableResource : byte
|
||||
{
|
||||
Memory = 0,
|
||||
Thread = 1,
|
||||
Event = 2,
|
||||
TransferMemory = 3,
|
||||
Session = 4,
|
||||
|
||||
Count = 5
|
||||
}
|
||||
}
|
128
Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs
Normal file
128
Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs
Normal file
|
@ -0,0 +1,128 @@
|
|||
using Ryujinx.Common;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
class MersenneTwister
|
||||
{
|
||||
private int _index;
|
||||
private uint[] _mt;
|
||||
|
||||
public MersenneTwister(uint seed)
|
||||
{
|
||||
_mt = new uint[624];
|
||||
|
||||
_mt[0] = seed;
|
||||
|
||||
for (int mtIdx = 1; mtIdx < _mt.Length; mtIdx++)
|
||||
{
|
||||
uint prev = _mt[mtIdx - 1];
|
||||
|
||||
_mt[mtIdx] = (uint)(0x6c078965 * (prev ^ (prev >> 30)) + mtIdx);
|
||||
}
|
||||
|
||||
_index = _mt.Length;
|
||||
}
|
||||
|
||||
public long GenRandomNumber(long min, long max)
|
||||
{
|
||||
long range = max - min;
|
||||
|
||||
if (min == max)
|
||||
{
|
||||
return min;
|
||||
}
|
||||
|
||||
if (range == -1)
|
||||
{
|
||||
//Increment would cause a overflow, special case.
|
||||
return GenRandomNumber(2, 2, 32, 0xffffffffu, 0xffffffffu);
|
||||
}
|
||||
|
||||
range++;
|
||||
|
||||
//This is log2(Range) plus one.
|
||||
int nextRangeLog2 = 64 - BitUtils.CountLeadingZeros64(range);
|
||||
|
||||
//If Range is already power of 2, subtract one to use log2(Range) directly.
|
||||
int rangeLog2 = nextRangeLog2 - (BitUtils.IsPowerOfTwo64(range) ? 1 : 0);
|
||||
|
||||
int parts = rangeLog2 > 32 ? 2 : 1;
|
||||
int bitsPerPart = rangeLog2 / parts;
|
||||
|
||||
int fullParts = parts - (rangeLog2 - parts * bitsPerPart);
|
||||
|
||||
uint mask = 0xffffffffu >> (32 - bitsPerPart);
|
||||
uint maskPlus1 = 0xffffffffu >> (31 - bitsPerPart);
|
||||
|
||||
long randomNumber;
|
||||
|
||||
do
|
||||
{
|
||||
randomNumber = GenRandomNumber(parts, fullParts, bitsPerPart, mask, maskPlus1);
|
||||
}
|
||||
while ((ulong)randomNumber >= (ulong)range);
|
||||
|
||||
return min + randomNumber;
|
||||
}
|
||||
|
||||
private long GenRandomNumber(
|
||||
int parts,
|
||||
int fullParts,
|
||||
int bitsPerPart,
|
||||
uint mask,
|
||||
uint maskPlus1)
|
||||
{
|
||||
long randomNumber = 0;
|
||||
|
||||
int part = 0;
|
||||
|
||||
for (; part < fullParts; part++)
|
||||
{
|
||||
randomNumber <<= bitsPerPart;
|
||||
randomNumber |= GenRandomNumber() & mask;
|
||||
}
|
||||
|
||||
for (; part < parts; part++)
|
||||
{
|
||||
randomNumber <<= bitsPerPart + 1;
|
||||
randomNumber |= GenRandomNumber() & maskPlus1;
|
||||
}
|
||||
|
||||
return randomNumber;
|
||||
}
|
||||
|
||||
private uint GenRandomNumber()
|
||||
{
|
||||
if (_index >= _mt.Length)
|
||||
{
|
||||
Twist();
|
||||
}
|
||||
|
||||
uint value = _mt[_index++];
|
||||
|
||||
value ^= value >> 11;
|
||||
value ^= (value << 7) & 0x9d2c5680;
|
||||
value ^= (value << 15) & 0xefc60000;
|
||||
value ^= value >> 18;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private void Twist()
|
||||
{
|
||||
for (int mtIdx = 0; mtIdx < _mt.Length; mtIdx++)
|
||||
{
|
||||
uint value = (_mt[mtIdx] & 0x80000000) + (_mt[(mtIdx + 1) % _mt.Length] & 0x7fffffff);
|
||||
|
||||
_mt[mtIdx] = _mt[(mtIdx + 397) % _mt.Length] ^ (value >> 1);
|
||||
|
||||
if ((value & 1) != 0)
|
||||
{
|
||||
_mt[mtIdx] ^= 0x9908b0df;
|
||||
}
|
||||
}
|
||||
|
||||
_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue