diff --git a/ExternalCoreProjects/Virtu/NoSlotClock.cs b/ExternalCoreProjects/Virtu/NoSlotClock.cs index 79d8e91b45..a11b72e1ef 100644 --- a/ExternalCoreProjects/Virtu/NoSlotClock.cs +++ b/ExternalCoreProjects/Virtu/NoSlotClock.cs @@ -21,6 +21,8 @@ namespace Jellyfish.Virtu private RingRegister _clockRegister; private RingRegister _comparisonRegister; + public Func FrontendTimeCallback; + public NoSlotClock(Video video) { _video = video; @@ -117,7 +119,7 @@ namespace Jellyfish.Virtu private void PopulateClockRegister() { // all values are in packed BCD format (4 bits per decimal digit) - var now = DateTime.Now; + var now = FrontendTimeCallback(); int centisecond = now.Millisecond / 10; // 00-99 _clockRegister.WriteNibble(centisecond % 10); diff --git a/ExternalCoreProjects/Virtu/Video.cs b/ExternalCoreProjects/Virtu/Video.cs index 0350f153c9..56af11e0ab 100644 --- a/ExternalCoreProjects/Virtu/Video.cs +++ b/ExternalCoreProjects/Virtu/Video.cs @@ -121,7 +121,8 @@ namespace Jellyfish.Virtu ser.Sync(nameof(_vLineLeaveVBlank), ref _vLineLeaveVBlank); ser.Sync(nameof(_charSet), ref _charSet, false); ser.Sync(nameof(_isCellDirty), ref _isCellDirty, false); - ser.Sync(nameof(_isMonochrome), ref _isMonochrome); + //ser.Sync(nameof(_isMonochrome), ref _isMonochrome); + //TODO: does this affect sync? } // ReSharper disable once UnusedMember.Global diff --git a/References/Virtu.dll b/References/Virtu.dll index 3a4d7c8897..88f17b8aa3 100644 Binary files a/References/Virtu.dll and b/References/Virtu.dll differ diff --git a/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs b/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs index ab28873df4..f6b6d38e51 100644 --- a/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs +++ b/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs @@ -13,7 +13,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII public string SystemId => VSystemID.Raw.AppleII; - public bool DeterministicEmulation => true; + public bool DeterministicEmulation { get; } public bool FrameAdvance(IController controller, bool render, bool renderSound) { diff --git a/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISettable.cs b/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISettable.cs index f51a4a1ff7..ffa5a4a42a 100644 --- a/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISettable.cs +++ b/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.ISettable.cs @@ -1,24 +1,58 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; + +using BizHawk.Common; using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.AppleII { - public partial class AppleII : ISettable + public partial class AppleII : ISettable { private Settings _settings; + private SyncSettings _syncSettings; public class Settings { + [DisplayName("Monochrome")] [DefaultValue(false)] [Description("Choose a monochrome monitor.")] public bool Monochrome { get; set; } - public Settings Clone() => (Settings)MemberwiseClone(); + public Settings() + => SettingsUtil.SetDefaultValues(this); + + public Settings Clone() + => (Settings)MemberwiseClone(); } - public Settings GetSettings() => _settings.Clone(); + public class SyncSettings + { + [DisplayName("Initial Time")] + [Description("Initial time of emulation.")] + [DefaultValue(typeof(DateTime), "2010-01-01")] + [TypeConverter(typeof(BizDateTimeConverter))] + public DateTime InitialTime { get; set; } - public object GetSyncSettings() => null; + [DisplayName("Use Real Time")] + [Description("If true, RTC clock will be based off of real time instead of emulated time. Ignored (set to false) when recording a movie.")] + [DefaultValue(false)] + public bool UseRealTime { get; set; } + + public SyncSettings() + => SettingsUtil.SetDefaultValues(this); + + public SyncSettings Clone() + => (SyncSettings)MemberwiseClone(); + + public static bool NeedsReboot(SyncSettings x, SyncSettings y) + => !DeepEquality.DeepEquals(x, y); + } + + public Settings GetSettings() + => _settings.Clone(); + + public SyncSettings GetSyncSettings() + => _syncSettings.Clone(); public PutSettingsDirtyBits PutSettings(Settings o) { @@ -30,6 +64,11 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII return PutSettingsDirtyBits.None; } - public PutSettingsDirtyBits PutSyncSettings(object o) => PutSettingsDirtyBits.None; + public PutSettingsDirtyBits PutSyncSettings(SyncSettings o) + { + var ret = SyncSettings.NeedsReboot(_syncSettings, o); + _syncSettings = o; + return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None; + } } } diff --git a/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs b/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs index 8e3a5c5699..ec94cd1e2b 100644 --- a/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs +++ b/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs @@ -39,6 +39,8 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII ser.Sync("CurrentDisk", ref _currentDisk); ser.Sync("WhiteAppleDown", ref Keyboard.WhiteAppleDown); ser.Sync("BlackAppleDown", ref Keyboard.BlackAppleDown); + ser.Sync("ClockTime", ref _clockTime); + ser.Sync("ClockRemainder", ref _clockRemainder); ser.BeginSection("Events"); _machine.Events.Sync(ser); diff --git a/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs b/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs index 0cbb846bd3..ca442905a6 100644 --- a/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs +++ b/src/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using BizHawk.Common.CollectionExtensions; @@ -20,7 +21,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII } [CoreConstructor(VSystemID.Raw.AppleII)] - public AppleII(CoreLoadParameters lp) + public AppleII(CoreLoadParameters lp) { _romSet = lp.Roms.Select(r => r.RomData).ToList(); var ser = new BasicServiceProvider(this); @@ -35,7 +36,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII _diskIIRom = lp.Comm.CoreFileProvider.GetFirmwareOrThrow(new(SystemId, "DiskII"), "The DiskII firmware is required"); _machine = new Components(_appleIIRom, _diskIIRom); - + // make a writable memory stream cloned from the rom. // for junk.dsk the .dsk is important because it determines the format from that InitDisk(); @@ -46,6 +47,10 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII SetupMemoryDomains(); PutSettings(lp.Settings ?? new Settings()); + + _syncSettings = lp.SyncSettings ?? new SyncSettings(); + DeterministicEmulation = lp.DeterministicEmulationRequested || !_syncSettings.UseRealTime; + InitializeRtc(DeterministicEmulation); } private static readonly ControllerDefinition AppleIIController; @@ -155,6 +160,8 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII _prevPressed = false; } + AdvanceRtc(); + MachineAdvance(RealButtons.Where(controller.IsPressed)); if (IsLagFrame) { @@ -211,5 +218,38 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII }; _machine.Memory.InputCallback = InputCallbacks.Call; } + + private bool _useRealTime; + private long _clockTime; + private int _clockRemainder; + private const int TicksInSecond = 10000000; // DateTime.Ticks uses 100-nanosecond intervals + + private DateTime GetFrontendTime() + { + if (_useRealTime && DeterministicEmulation) + throw new InvalidOperationException(); + + return _useRealTime + ? DateTime.Now + : new DateTime(_clockTime * TicksInSecond + (_clockRemainder * TicksInSecond / VsyncNumerator)); + } + + private void InitializeRtc(bool useRealTime) + { + _useRealTime = useRealTime; + _clockTime = _syncSettings.InitialTime.Ticks / TicksInSecond; + _clockRemainder = (int)(_syncSettings.InitialTime.Ticks % TicksInSecond) * VsyncNumerator / TicksInSecond; + _machine.NoSlotClock.FrontendTimeCallback = GetFrontendTime; + } + + private void AdvanceRtc() + { + _clockRemainder += VsyncDenominator; + if (_clockRemainder >= VsyncNumerator) + { + _clockRemainder -= VsyncNumerator; + _clockTime++; + } + } } }