diff options
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Kernel/Common')
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs | 7 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs | 73 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs | 188 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs | 35 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs | 78 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs | 218 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs | 89 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs | 73 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs | 13 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs | 12 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs | 9 | ||||
| -rw-r--r-- | src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs | 128 |
12 files changed, 923 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs new file mode 100644 index 00000000..473683ff --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/IKFutureSchedulerObject.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Kernel.Common +{ + interface IKFutureSchedulerObject + { + void TimeUp(); + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs new file mode 100644 index 00000000..424bf788 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KAutoObject.cs @@ -0,0 +1,73 @@ +using Ryujinx.Horizon.Common; +using System.Diagnostics; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Kernel.Common +{ + class KAutoObject + { + protected KernelContext KernelContext; + + private int _referenceCount; + + public KAutoObject(KernelContext context) + { + KernelContext = context; + + _referenceCount = 1; + } + + public virtual Result SetName(string name) + { + if (!KernelContext.AutoObjectNames.TryAdd(name, this)) + { + return KernelResult.InvalidState; + } + + return Result.Success; + } + + public static Result RemoveName(KernelContext context, string name) + { + if (!context.AutoObjectNames.TryRemove(name, out _)) + { + return KernelResult.NotFound; + } + + return Result.Success; + } + + public static KAutoObject FindNamedObject(KernelContext context, string name) + { + if (context.AutoObjectNames.TryGetValue(name, out KAutoObject obj)) + { + return obj; + } + + return null; + } + + public void IncrementReferenceCount() + { + int newRefCount = Interlocked.Increment(ref _referenceCount); + + Debug.Assert(newRefCount >= 2); + } + + public void DecrementReferenceCount() + { + int newRefCount = Interlocked.Decrement(ref _referenceCount); + + Debug.Assert(newRefCount >= 0); + + if (newRefCount == 0) + { + Destroy(); + } + } + + protected virtual void Destroy() + { + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs new file mode 100644 index 00000000..b1a602f1 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs @@ -0,0 +1,188 @@ +using Ryujinx.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Kernel.Common +{ + class KResourceLimit : KAutoObject + { + private const int DefaultTimeoutMs = 10000; // 10s + + private readonly long[] _current; + private readonly long[] _limit; + private readonly long[] _current2; + private readonly long[] _peak; + + private readonly object _lock; + + private readonly LinkedList<KThread> _waitingThreads; + + private int _waitingThreadsCount; + + public KResourceLimit(KernelContext context) : base(context) + { + _current = new long[(int)LimitableResource.Count]; + _limit = new long[(int)LimitableResource.Count]; + _current2 = new long[(int)LimitableResource.Count]; + _peak = new long[(int)LimitableResource.Count]; + + _lock = new object(); + + _waitingThreads = new LinkedList<KThread>(); + } + + 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(DefaultTimeoutMs)); + } + + 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 (_lock) + { + if (_current2[index] >= _limit[index]) + { + return false; + } + + long newCurrent = _current[index] + amount; + + while (newCurrent > _limit[index] && _current2[index] + amount <= _limit[index]) + { + _waitingThreadsCount++; + + KConditionVariable.Wait(KernelContext, _waitingThreads, _lock, timeout); + + _waitingThreadsCount--; + + newCurrent = _current[index] + amount; + + if (timeout >= 0 && PerformanceCounter.ElapsedMilliseconds > endTimePoint) + { + break; + } + } + + if (newCurrent <= _limit[index]) + { + _current[index] = newCurrent; + _current2[index] += amount; + + if (_current[index] > _peak[index]) + { + _peak[index] = _current[index]; + } + + 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); + } + + public void Release(LimitableResource resource, long amount, long amount2) + { + int index = GetIndex(resource); + + lock (_lock) + { + _current[index] -= amount; + _current2[index] -= amount2; + + if (_waitingThreadsCount > 0) + { + KConditionVariable.NotifyAll(KernelContext, _waitingThreads); + } + } + } + + public long GetRemainingValue(LimitableResource resource) + { + int index = GetIndex(resource); + + lock (_lock) + { + return _limit[index] - _current[index]; + } + } + + public long GetCurrentValue(LimitableResource resource) + { + int index = GetIndex(resource); + + lock (_lock) + { + return _current[index]; + } + } + + public long GetLimitValue(LimitableResource resource) + { + int index = GetIndex(resource); + + lock (_lock) + { + return _limit[index]; + } + } + + public long GetPeakValue(LimitableResource resource) + { + int index = GetIndex(resource); + + lock (_lock) + { + return _peak[index]; + } + } + + public Result SetLimitValue(LimitableResource resource, long limit) + { + int index = GetIndex(resource); + + lock (_lock) + { + if (_current[index] <= limit) + { + _limit[index] = limit; + _peak[index] = _current[index]; + + return Result.Success; + } + else + { + return KernelResult.InvalidState; + } + } + } + + private static int GetIndex(LimitableResource resource) + { + return (int)resource; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs new file mode 100644 index 00000000..ddc0069d --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KSynchronizationObject.cs @@ -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 { get; } + + public KSynchronizationObject(KernelContext context) : base(context) + { + 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() + { + KernelContext.Synchronization.SignalObject(this); + } + + public virtual bool IsSignaled() + { + return false; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs new file mode 100644 index 00000000..8a727c30 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs @@ -0,0 +1,78 @@ +using Ryujinx.HLE.HOS.Kernel.Memory; +using System; + +namespace Ryujinx.HLE.HOS.Kernel.Common +{ + static class KSystemControl + { + private const ulong KiB = 1024; + private const ulong MiB = 1024 * KiB; + private const ulong GiB = 1024 * MiB; + + private const ulong PageSize = 4 * KiB; + + private const ulong RequiredNonSecureSystemPoolSizeVi = 0x2238 * PageSize; + private const ulong RequiredNonSecureSystemPoolSizeNvservices = 0x710 * PageSize; + private const ulong RequiredNonSecureSystemPoolSizeOther = 0x80 * PageSize; + + private const ulong RequiredNonSecureSystemPoolSize = + RequiredNonSecureSystemPoolSizeVi + + RequiredNonSecureSystemPoolSizeNvservices + + RequiredNonSecureSystemPoolSizeOther; + + public static ulong GetApplicationPoolSize(MemoryArrange arrange) + { + return arrange switch + { + MemoryArrange.MemoryArrange4GiB or + MemoryArrange.MemoryArrange4GiBSystemDev or + MemoryArrange.MemoryArrange6GiBAppletDev => 3285 * MiB, + MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB, + MemoryArrange.MemoryArrange6GiB or + MemoryArrange.MemoryArrange8GiB => 4916 * MiB, + _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\".") + }; + } + + public static ulong GetAppletPoolSize(MemoryArrange arrange) + { + return arrange switch + { + MemoryArrange.MemoryArrange4GiB => 507 * MiB, + MemoryArrange.MemoryArrange4GiBAppletDev => 1554 * MiB, + MemoryArrange.MemoryArrange4GiBSystemDev => 448 * MiB, + MemoryArrange.MemoryArrange6GiB => 562 * MiB, + MemoryArrange.MemoryArrange6GiBAppletDev or + MemoryArrange.MemoryArrange8GiB => 2193 * MiB, + _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\".") + }; + } + + public static ulong GetMinimumNonSecureSystemPoolSize() + { + return RequiredNonSecureSystemPoolSize; + } + + public static ulong GetDramEndAddress(MemorySize size) + { + return DramMemoryMap.DramBase + GetDramSize(size); + } + + public static ulong GenerateRandom() + { + // TODO + return 0; + } + + public static ulong GetDramSize(MemorySize size) + { + return size switch + { + MemorySize.MemorySize4GiB => 4 * GiB, + MemorySize.MemorySize6GiB => 6 * GiB, + MemorySize.MemorySize8GiB => 8 * GiB, + _ => throw new ArgumentException($"Invalid memory size \"{size}\".") + }; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs new file mode 100644 index 00000000..c0cd9ce9 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs @@ -0,0 +1,218 @@ +using Ryujinx.Common; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Kernel.Common +{ + class KTimeManager : IDisposable + { + public static readonly long DefaultTimeIncrementNanoseconds = ConvertGuestTicksToNanoseconds(2); + + private class WaitingObject + { + public IKFutureSchedulerObject Object { get; } + public long TimePoint { get; } + + public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint) + { + Object = schedulerObj; + TimePoint = timePoint; + } + } + + private readonly KernelContext _context; + private readonly List<WaitingObject> _waitingObjects; + private AutoResetEvent _waitEvent; + private bool _keepRunning; + private long _enforceWakeupFromSpinWait; + + private const long NanosecondsPerSecond = 1000000000L; + private const long NanosecondsPerMillisecond = 1000000L; + + public KTimeManager(KernelContext context) + { + _context = context; + _waitingObjects = new List<WaitingObject>(); + _keepRunning = true; + + Thread work = new Thread(WaitAndCheckScheduledObjects) + { + Name = "HLE.TimeManager" + }; + + work.Start(); + } + + public void ScheduleFutureInvocation(IKFutureSchedulerObject schedulerObj, long timeout) + { + long startTime = PerformanceCounter.ElapsedTicks; + long timePoint = startTime + ConvertNanosecondsToHostTicks(timeout); + + if (timePoint < startTime) + { + timePoint = long.MaxValue; + } + + lock (_context.CriticalSection.Lock) + { + _waitingObjects.Add(new WaitingObject(schedulerObj, timePoint)); + + if (timeout < NanosecondsPerMillisecond) + { + Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1); + } + } + + _waitEvent.Set(); + } + + public void UnscheduleFutureInvocation(IKFutureSchedulerObject schedulerObj) + { + lock (_context.CriticalSection.Lock) + { + for (int index = _waitingObjects.Count - 1; index >= 0; index--) + { + if (_waitingObjects[index].Object == schedulerObj) + { + _waitingObjects.RemoveAt(index); + } + } + } + } + + private void WaitAndCheckScheduledObjects() + { + SpinWait spinWait = new SpinWait(); + WaitingObject next; + + using (_waitEvent = new AutoResetEvent(false)) + { + while (_keepRunning) + { + lock (_context.CriticalSection.Lock) + { + Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 0); + + next = GetNextWaitingObject(); + } + + if (next != null) + { + long timePoint = PerformanceCounter.ElapsedTicks; + + if (next.TimePoint > timePoint) + { + long ms = Math.Min((next.TimePoint - timePoint) / PerformanceCounter.TicksPerMillisecond, int.MaxValue); + + if (ms > 0) + { + _waitEvent.WaitOne((int)ms); + } + else + { + while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks < next.TimePoint) + { + // Our time is close - don't let SpinWait go off and potentially Thread.Sleep(). + if (spinWait.NextSpinWillYield) + { + Thread.Yield(); + + spinWait.Reset(); + } + else + { + spinWait.SpinOnce(); + } + } + + spinWait.Reset(); + } + } + + bool timeUp = PerformanceCounter.ElapsedTicks >= next.TimePoint; + + if (timeUp) + { + lock (_context.CriticalSection.Lock) + { + if (_waitingObjects.Remove(next)) + { + next.Object.TimeUp(); + } + } + } + } + else + { + _waitEvent.WaitOne(); + } + } + } + } + + private WaitingObject GetNextWaitingObject() + { + WaitingObject selected = null; + + long lowestTimePoint = long.MaxValue; + + for (int index = _waitingObjects.Count - 1; index >= 0; index--) + { + WaitingObject current = _waitingObjects[index]; + + if (current.TimePoint <= lowestTimePoint) + { + selected = current; + lowestTimePoint = current.TimePoint; + } + } + + return selected; + } + + public static long ConvertNanosecondsToMilliseconds(long time) + { + time /= NanosecondsPerMillisecond; + + if ((ulong)time > int.MaxValue) + { + return int.MaxValue; + } + + return time; + } + + public static long ConvertMillisecondsToNanoseconds(long time) + { + return time * NanosecondsPerMillisecond; + } + + public static long ConvertNanosecondsToHostTicks(long ns) + { + long nsDiv = ns / NanosecondsPerSecond; + long nsMod = ns % NanosecondsPerSecond; + long tickDiv = PerformanceCounter.TicksPerSecond / NanosecondsPerSecond; + long tickMod = PerformanceCounter.TicksPerSecond % NanosecondsPerSecond; + + long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / NanosecondsPerSecond; + return (nsDiv * tickDiv) * NanosecondsPerSecond + nsDiv * tickMod + nsMod * tickDiv + baseTicks; + } + + public static long ConvertGuestTicksToNanoseconds(long ticks) + { + return (long)Math.Ceiling(ticks * (1000000000.0 / 19200000.0)); + } + + public static long ConvertHostTicksToTicks(long time) + { + return (long)((time / (double)PerformanceCounter.TicksPerSecond) * 19200000.0); + } + + public void Dispose() + { + _keepRunning = false; + _waitEvent?.Set(); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs new file mode 100644 index 00000000..efa2a480 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs @@ -0,0 +1,89 @@ +using Ryujinx.HLE.HOS.Kernel.Memory; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.HLE.HOS.Kernel.Common +{ + static class KernelInit + { + private readonly struct MemoryRegion + { + public ulong Address { get; } + public ulong Size { get; } + + public ulong EndAddress => Address + Size; + + public MemoryRegion(ulong address, ulong size) + { + Address = address; + Size = size; + } + } + + public static void InitializeResourceLimit(KResourceLimit resourceLimit, MemorySize size) + { + void EnsureSuccess(Result result) + { + if (result != Result.Success) + { + throw new InvalidOperationException($"Unexpected result \"{result}\"."); + } + } + + ulong ramSize = KSystemControl.GetDramSize(size); + + EnsureSuccess(resourceLimit.SetLimitValue(LimitableResource.Memory, (long)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(MemorySize size, MemoryArrange arrange) + { + ulong poolEnd = KSystemControl.GetDramEndAddress(size); + ulong applicationPoolSize = KSystemControl.GetApplicationPoolSize(arrange); + ulong appletPoolSize = KSystemControl.GetAppletPoolSize(arrange); + + MemoryRegion servicePool; + MemoryRegion nvServicesPool; + MemoryRegion appletPool; + MemoryRegion applicationPool; + + ulong nvServicesPoolSize = KSystemControl.GetMinimumNonSecureSystemPoolSize(); + + applicationPool = new MemoryRegion(poolEnd - applicationPoolSize, applicationPoolSize); + + ulong nvServicesPoolEnd = applicationPool.Address - appletPoolSize; + + nvServicesPool = new MemoryRegion(nvServicesPoolEnd - nvServicesPoolSize, nvServicesPoolSize); + appletPool = new MemoryRegion(nvServicesPoolEnd, appletPoolSize); + + // 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 servicePoolSize = nvServicesPool.Address - DramMemoryMap.SlabHeapEnd; + + servicePool = new MemoryRegion(DramMemoryMap.SlabHeapEnd, servicePoolSize); + + return new KMemoryRegionManager[] + { + GetMemoryRegion(applicationPool), + GetMemoryRegion(appletPool), + GetMemoryRegion(servicePool), + GetMemoryRegion(nvServicesPool) + }; + } + + private static KMemoryRegionManager GetMemoryRegion(MemoryRegion region) + { + return new KMemoryRegionManager(region.Address, region.Size, region.EndAddress); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs new file mode 100644 index 00000000..cbc276c5 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs @@ -0,0 +1,73 @@ +using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.Kernel.Process; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Kernel.Common +{ + static class KernelTransfer + { + public static bool UserToKernel<T>(out T value, ulong address) where T : unmanaged + { + KProcess currentProcess = KernelStatic.GetCurrentProcess(); + + if (currentProcess.CpuMemory.IsRangeMapped(address, (ulong)Unsafe.SizeOf<T>())) + { + value = currentProcess.CpuMemory.Read<T>(address); + + return true; + } + + value = default; + + return false; + } + + public static bool UserToKernelArray<T>(ulong address, Span<T> values) where T : unmanaged + { + KProcess currentProcess = KernelStatic.GetCurrentProcess(); + + Span<byte> data = MemoryMarshal.Cast<T, byte>(values); + + if (currentProcess.CpuMemory.IsRangeMapped(address, (ulong)data.Length)) + { + currentProcess.CpuMemory.Read(address, data); + + return true; + } + + return false; + } + + public static bool UserToKernelString(out string value, ulong address, uint size) + { + KProcess currentProcess = KernelStatic.GetCurrentProcess(); + + if (currentProcess.CpuMemory.IsRangeMapped(address, size)) + { + value = MemoryHelper.ReadAsciiString(currentProcess.CpuMemory, address, size); + + return true; + } + + value = null; + + return false; + } + + public static bool KernelToUser<T>(ulong address, T value) where T: unmanaged + { + KProcess currentProcess = KernelStatic.GetCurrentProcess(); + + if (currentProcess.CpuMemory.IsRangeMapped(address, (ulong)Unsafe.SizeOf<T>())) + { + currentProcess.CpuMemory.Write(address, value); + + return true; + } + + return false; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs new file mode 100644 index 00000000..2e6a3e45 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/LimitableResource.cs @@ -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 + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs new file mode 100644 index 00000000..d2bcfd62 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.HLE.HOS.Kernel.Common +{ + enum MemoryArrange : byte + { + MemoryArrange4GiB, + MemoryArrange4GiBAppletDev, + MemoryArrange4GiBSystemDev, + MemoryArrange6GiB, + MemoryArrange6GiBAppletDev, + MemoryArrange8GiB + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs new file mode 100644 index 00000000..159385b6 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/MemroySize.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Kernel.Common +{ + enum MemorySize : byte + { + MemorySize4GiB = 0, + MemorySize6GiB = 1, + MemorySize8GiB = 2 + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs new file mode 100644 index 00000000..4c99f425 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/MersenneTwister.cs @@ -0,0 +1,128 @@ +using System.Numerics; + +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 - BitOperations.LeadingZeroCount((ulong)range); + + // If Range is already power of 2, subtract one to use log2(Range) directly. + int rangeLog2 = nextRangeLog2 - (BitOperations.IsPow2(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; + } + } +} |
