Implement time:* 4.0.0 commands (#736)

* Abstract SteadyClockCore to follow Nintendo changes in 4.x

This is the ground work for 4.0.0 support

* Implement TickBasedSteadyClockCore

Preparation for the ephemeral clock.

* Refactor SystemClockCore to follow 4.0.0 changes

* Implement EphemeralNetworkSystemClock

* Implement GetSnapshotClock & GetSnapshotClockFromSystemClockContext

* Implement CalculateStandardUserSystemClockDifferenceByUser & CalculateSpanBetween

* Remove an outdated comment & unused import

* Fix a nit and GetClockSnapshot

* Address comment
This commit is contained in:
Thomas Guillemard 2019-07-25 16:44:51 +02:00 committed by gdkchan
parent d254548548
commit 54b79dffa8
13 changed files with 518 additions and 205 deletions

View file

@ -7,6 +7,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
[StructLayout(LayoutKind.Sequential)]
struct TimeSpanType
{
private const long NanoSecondsPerSecond = 1000000000;
public long NanoSeconds;
public TimeSpanType(long nanoSeconds)
@ -16,12 +18,17 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
public long ToSeconds()
{
return NanoSeconds / 1000000000;
return NanoSeconds / NanoSecondsPerSecond;
}
public static TimeSpanType FromSeconds(long seconds)
{
return new TimeSpanType(seconds * NanoSecondsPerSecond);
}
public static TimeSpanType FromTicks(ulong ticks, ulong frequency)
{
return new TimeSpanType((long)ticks * 1000000000 / (long)frequency);
return FromSeconds((long)ticks / (long)frequency);
}
}
@ -59,4 +66,40 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
public long Offset;
public SteadyClockTimePoint SteadyTimePoint;
}
[StructLayout(LayoutKind.Sequential, Size = 0xD0)]
struct ClockSnapshot
{
public SystemClockContext UserContext;
public SystemClockContext NetworkContext;
public long UserTime;
public long NetworkTime;
public CalendarTime UserCalendarTime;
public CalendarTime NetworkCalendarTime;
public CalendarAdditionalInfo UserCalendarAdditionalTime;
public CalendarAdditionalInfo NetworkCalendarAdditionalTime;
public SteadyClockTimePoint SteadyClockTimePoint;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x24)]
public char[] LocationName;
[MarshalAs(UnmanagedType.I1)]
public bool IsAutomaticCorrectionEnabled;
public byte Type;
public ushort Unknown;
public static ResultCode GetCurrentTime(out long currentTime, SteadyClockTimePoint steadyClockTimePoint, SystemClockContext context)
{
currentTime = 0;
if (steadyClockTimePoint.ClockSourceId == context.SteadyTimePoint.ClockSourceId)
{
currentTime = steadyClockTimePoint.TimePoint + context.Offset;
return ResultCode.Success;
}
return ResultCode.TimeMismatch;
}
}
}

View file

@ -0,0 +1,27 @@
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
class EphemeralNetworkSystemClockCore : SystemClockCore
{
private static EphemeralNetworkSystemClockCore _instance;
public static EphemeralNetworkSystemClockCore Instance
{
get
{
if (_instance == null)
{
_instance = new EphemeralNetworkSystemClockCore(TickBasedSteadyClockCore.Instance);
}
return _instance;
}
}
public EphemeralNetworkSystemClockCore(SteadyClockCore steadyClockCore) : base(steadyClockCore) { }
public override ResultCode Flush(SystemClockContext context)
{
return ResultCode.Success;
}
}
}

View file

@ -1,34 +1,23 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
class StandardLocalSystemClockCore : SystemClockCore
{
private SteadyClockCore _steadyClockCore;
private SystemClockContext _context;
private static StandardLocalSystemClockCore instance;
private static StandardLocalSystemClockCore _instance;
public static StandardLocalSystemClockCore Instance
{
get
{
if (instance == null)
if (_instance == null)
{
instance = new StandardLocalSystemClockCore(SteadyClockCore.Instance);
_instance = new StandardLocalSystemClockCore(StandardSteadyClockCore.Instance);
}
return instance;
return _instance;
}
}
public StandardLocalSystemClockCore(SteadyClockCore steadyClockCore)
{
_steadyClockCore = steadyClockCore;
_context = new SystemClockContext();
_context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId();
}
public StandardLocalSystemClockCore(StandardSteadyClockCore steadyClockCore) : base(steadyClockCore) {}
public override ResultCode Flush(SystemClockContext context)
{
@ -36,24 +25,5 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
return ResultCode.Success;
}
public override SteadyClockCore GetSteadyClockCore()
{
return _steadyClockCore;
}
public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
{
context = _context;
return ResultCode.Success;
}
public override ResultCode SetSystemClockContext(SystemClockContext context)
{
_context = context;
return ResultCode.Success;
}
}
}

View file

@ -1,35 +1,28 @@
using System;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
class StandardNetworkSystemClockCore : SystemClockCore
{
private SteadyClockCore _steadyClockCore;
private SystemClockContext _context;
private TimeSpanType _standardNetworkClockSufficientAccuracy;
private static StandardNetworkSystemClockCore instance;
private static StandardNetworkSystemClockCore _instance;
public static StandardNetworkSystemClockCore Instance
{
get
{
if (instance == null)
if (_instance == null)
{
instance = new StandardNetworkSystemClockCore(SteadyClockCore.Instance);
_instance = new StandardNetworkSystemClockCore(StandardSteadyClockCore.Instance);
}
return instance;
return _instance;
}
}
public StandardNetworkSystemClockCore(SteadyClockCore steadyClockCore)
public StandardNetworkSystemClockCore(StandardSteadyClockCore steadyClockCore) : base(steadyClockCore)
{
_steadyClockCore = steadyClockCore;
_context = new SystemClockContext();
_context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId();
_standardNetworkClockSufficientAccuracy = new TimeSpanType(0);
}
@ -40,25 +33,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
return ResultCode.Success;
}
public override SteadyClockCore GetSteadyClockCore()
{
return _steadyClockCore;
}
public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
{
context = _context;
return ResultCode.Success;
}
public override ResultCode SetSystemClockContext(SystemClockContext context)
{
_context = context;
return ResultCode.Success;
}
public bool IsStandardNetworkSystemClockAccuracySufficient(KThread thread)
{
SteadyClockCore steadyClockCore = GetSteadyClockCore();
@ -66,7 +40,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
bool isStandardNetworkClockSufficientAccuracy = false;
if (_context.SteadyTimePoint.GetSpanBetween(currentTimePoint, out long outSpan) == ResultCode.Success)
ResultCode result = GetSystemClockContext(thread, out SystemClockContext context);
if (result == ResultCode.Success && context.SteadyTimePoint.GetSpanBetween(currentTimePoint, out long outSpan) == ResultCode.Success)
{
isStandardNetworkClockSufficientAccuracy = outSpan * 1000000000 < _standardNetworkClockSufficientAccuracy.NanoSeconds;
}

View file

@ -0,0 +1,107 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Bpc;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
class StandardSteadyClockCore : SteadyClockCore
{
private long _setupValue;
private ResultCode _setupResultCode;
private bool _isRtcResetDetected;
private TimeSpanType _testOffset;
private TimeSpanType _internalOffset;
private static StandardSteadyClockCore _instance;
public static StandardSteadyClockCore Instance
{
get
{
if (_instance == null)
{
_instance = new StandardSteadyClockCore();
}
return _instance;
}
}
private StandardSteadyClockCore()
{
_testOffset = new TimeSpanType(0);
_internalOffset = new TimeSpanType(0);
}
public override SteadyClockTimePoint GetTimePoint(KThread thread)
{
SteadyClockTimePoint result = new SteadyClockTimePoint
{
TimePoint = 0,
ClockSourceId = GetClockSourceId()
};
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.ThreadState.CntpctEl0, thread.Context.ThreadState.CntfrqEl0);
result.TimePoint = _setupValue + ticksTimeSpan.ToSeconds();
return result;
}
public override TimeSpanType GetTestOffset()
{
return _testOffset;
}
public override void SetTestOffset(TimeSpanType testOffset)
{
_testOffset = testOffset;
}
public override ResultCode GetRtcValue(out ulong rtcValue)
{
return (ResultCode)IRtcManager.GetExternalRtcValue(out rtcValue);
}
public bool IsRtcResetDetected()
{
return _isRtcResetDetected;
}
public override TimeSpanType GetInternalOffset()
{
return _internalOffset;
}
public override void SetInternalOffset(TimeSpanType internalOffset)
{
_internalOffset = internalOffset;
}
public override ResultCode GetSetupResultValue()
{
return _setupResultCode;
}
public void ConfigureSetupValue()
{
int retry = 0;
ResultCode result = ResultCode.Success;
while (retry < 20)
{
result = (ResultCode)IRtcManager.GetExternalRtcValue(out ulong rtcValue);
if (result == ResultCode.Success)
{
_setupValue = (long)rtcValue;
break;
}
retry++;
}
_setupResultCode = result;
}
}
}

View file

@ -8,22 +8,22 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
private StandardNetworkSystemClockCore _networkSystemClockCore;
private bool _autoCorrectionEnabled;
private static StandardUserSystemClockCore instance;
private static StandardUserSystemClockCore _instance;
public static StandardUserSystemClockCore Instance
{
get
{
if (instance == null)
if (_instance == null)
{
instance = new StandardUserSystemClockCore(StandardLocalSystemClockCore.Instance, StandardNetworkSystemClockCore.Instance);
_instance = new StandardUserSystemClockCore(StandardLocalSystemClockCore.Instance, StandardNetworkSystemClockCore.Instance);
}
return instance;
return _instance;
}
}
public StandardUserSystemClockCore(StandardLocalSystemClockCore localSystemClockCore, StandardNetworkSystemClockCore networkSystemClockCore)
public StandardUserSystemClockCore(StandardLocalSystemClockCore localSystemClockCore, StandardNetworkSystemClockCore networkSystemClockCore) : base(localSystemClockCore.GetSteadyClockCore())
{
_localSystemClockCore = localSystemClockCore;
_networkSystemClockCore = networkSystemClockCore;
@ -35,11 +35,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
return ResultCode.NotImplemented;
}
public override SteadyClockCore GetSteadyClockCore()
{
return _localSystemClockCore.GetSteadyClockCore();
}
public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
{
ResultCode result = ApplyAutomaticCorrection(thread, false);

View file

@ -1,54 +1,16 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Bpc;
using Ryujinx.HLE.Utilities;
using System;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
class SteadyClockCore
abstract class SteadyClockCore
{
private long _setupValue;
private ResultCode _setupResultCode;
private bool _isRtcResetDetected;
private TimeSpanType _testOffset;
private TimeSpanType _internalOffset;
private UInt128 _clockSourceId;
private UInt128 _clockSourceId;
private static SteadyClockCore instance;
public static SteadyClockCore Instance
public SteadyClockCore()
{
get
{
if (instance == null)
{
instance = new SteadyClockCore();
}
return instance;
}
}
private SteadyClockCore()
{
_testOffset = new TimeSpanType(0);
_internalOffset = new TimeSpanType(0);
_clockSourceId = new UInt128(Guid.NewGuid().ToByteArray());
}
private SteadyClockTimePoint GetTimePoint(KThread thread)
{
SteadyClockTimePoint result = new SteadyClockTimePoint
{
TimePoint = 0,
ClockSourceId = _clockSourceId
};
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.ThreadState.CntpctEl0, thread.Context.ThreadState.CntfrqEl0);
result.TimePoint = _setupValue + ticksTimeSpan.ToSeconds();
return result;
_clockSourceId = new UInt128(Guid.NewGuid().ToByteArray());
}
public UInt128 GetClockSourceId()
@ -56,76 +18,45 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
return _clockSourceId;
}
public virtual TimeSpanType GetTestOffset()
{
return new TimeSpanType(0);
}
public virtual void SetTestOffset(TimeSpanType testOffset) {}
public virtual ResultCode GetRtcValue(out ulong rtcValue)
{
rtcValue = 0;
return ResultCode.NotImplemented;
}
public virtual ResultCode GetSetupResultValue()
{
return ResultCode.NotImplemented;
}
public virtual TimeSpanType GetInternalOffset()
{
return new TimeSpanType(0);
}
public virtual void SetInternalOffset(TimeSpanType internalOffset) {}
public virtual SteadyClockTimePoint GetTimePoint(KThread thread)
{
throw new NotImplementedException();
}
public SteadyClockTimePoint GetCurrentTimePoint(KThread thread)
{
SteadyClockTimePoint result = GetTimePoint(thread);
result.TimePoint += _testOffset.ToSeconds();
result.TimePoint += _internalOffset.ToSeconds();
result.TimePoint += GetTestOffset().ToSeconds();
result.TimePoint += GetInternalOffset().ToSeconds();
return result;
}
public TimeSpanType GetTestOffset()
{
return _testOffset;
}
public void SetTestOffset(TimeSpanType testOffset)
{
_testOffset = testOffset;
}
public ResultCode GetRtcValue(out ulong rtcValue)
{
return (ResultCode)IRtcManager.GetExternalRtcValue(out rtcValue);
}
public bool IsRtcResetDetected()
{
return _isRtcResetDetected;
}
public ResultCode GetSetupResultCode()
{
return _setupResultCode;
}
public TimeSpanType GetInternalOffset()
{
return _internalOffset;
}
public void SetInternalOffset(TimeSpanType internalOffset)
{
_internalOffset = internalOffset;
}
public ResultCode GetSetupResultValue()
{
return _setupResultCode;
}
public void ConfigureSetupValue()
{
int retry = 0;
ResultCode result = ResultCode.Success;
while (retry < 20)
{
result = (ResultCode)IRtcManager.GetExternalRtcValue(out ulong rtcValue);
if (result == ResultCode.Success)
{
_setupValue = (long)rtcValue;
break;
}
retry++;
}
_setupResultCode = result;
}
}
}

View file

@ -4,11 +4,35 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
abstract class SystemClockCore
{
public abstract SteadyClockCore GetSteadyClockCore();
private SteadyClockCore _steadyClockCore;
private SystemClockContext _context;
public abstract ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context);
public SystemClockCore(SteadyClockCore steadyClockCore)
{
_steadyClockCore = steadyClockCore;
_context = new SystemClockContext();
public abstract ResultCode SetSystemClockContext(SystemClockContext context);
_context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId();
}
public virtual SteadyClockCore GetSteadyClockCore()
{
return _steadyClockCore;
}
public virtual ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
{
context = _context;
return ResultCode.Success;
}
public virtual ResultCode SetSystemClockContext(SystemClockContext context)
{
_context = context;
return ResultCode.Success;
}
public abstract ResultCode Flush(SystemClockContext context);

View file

@ -0,0 +1,40 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Services.Time.Clock
{
class TickBasedSteadyClockCore : SteadyClockCore
{
private static TickBasedSteadyClockCore _instance;
public static TickBasedSteadyClockCore Instance
{
get
{
if (_instance == null)
{
_instance = new TickBasedSteadyClockCore();
}
return _instance;
}
}
private TickBasedSteadyClockCore() {}
public override SteadyClockTimePoint GetTimePoint(KThread thread)
{
SteadyClockTimePoint result = new SteadyClockTimePoint
{
TimePoint = 0,
ClockSourceId = GetClockSourceId()
};
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.ThreadState.CntpctEl0, thread.Context.ThreadState.CntfrqEl0);
result.TimePoint = ticksTimeSpan.ToSeconds();
return result;
}
}
}