diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs index 9e871c553b..48eed6610b 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IEmulator.cs @@ -8,7 +8,8 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII public ControllerDefinition ControllerDefinition => AppleIIController; - public int Frame { get; private set; } + private int _frame; + public int Frame { get => _frame; set => _frame = value; } public string SystemId => "AppleII"; diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IInputPollable.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IInputPollable.cs index 11f71559f8..34ad51858d 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IInputPollable.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IInputPollable.cs @@ -4,7 +4,12 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII { public partial class AppleII : IInputPollable { - public int LagCount { get; set; } + private int _lagcount; + public int LagCount + { + get => _lagcount; + set => _lagcount = value; + } public bool IsLagFrame { diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs index e6efc269d3..d02f1fccd1 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IStatable.cs @@ -1,181 +1,97 @@ -using System; -using System.IO; - +using System.IO; +using BizHawk.Common; using BizHawk.Emulation.Common; using Jellyfish.Virtu; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; namespace BizHawk.Emulation.Cores.Computers.AppleII { public partial class AppleII : ITextStatable { - private class CoreConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return objectType == typeof(Components); - } - - public override bool CanRead => true; - - public override bool CanWrite => false; - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - return CreateSerializer().Deserialize(reader); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - throw new NotImplementedException(); - } - } - - private void SerializeEverything(JsonWriter w) - { - // this is much faster than other possibilities for serialization - w.WriteStartObject(); - w.WritePropertyName(nameof(Frame)); - w.WriteValue(Frame); - w.WritePropertyName(nameof(LagCount)); - w.WriteValue(LagCount); - w.WritePropertyName(nameof(IsLagFrame)); - w.WriteValue(IsLagFrame); - w.WritePropertyName(nameof(CurrentDisk)); - w.WriteValue(CurrentDisk); - w.WritePropertyName("PreviousDiskPressed"); - w.WriteValue(_prevPressed); - w.WritePropertyName("NextDiskPressed"); - w.WriteValue(_nextPressed); - w.WritePropertyName("Core"); - CreateSerializer().Serialize(w, _machine); - w.WriteEndObject(); - } - - private void DeserializeEverything(JsonReader r) - { - var o = (OtherData)_ser.Deserialize(r, typeof(OtherData)); - Frame = o.Frame; - LagCount = o.LagCount; - IsLagFrame = o.IsLagFrame; - CurrentDisk = o.CurrentDisk; - _machine = o.Core; - _prevPressed = o.PreviousDiskPressed; - _nextPressed = o.NextDiskPressed; - - // since _machine was replaced, we need to reload settings from frontend - PutSettings(_settings); - } - - public class OtherData - { - public int Frame; - public int LagCount; - public bool IsLagFrame; - public int CurrentDisk; - public bool PreviousDiskPressed; - public bool NextDiskPressed; - public Components Core; - } - - private void InitSaveStates() - { - _ser.Converters.Add(new CoreConverter()); - } - - private readonly JsonSerializer _ser = new JsonSerializer(); - public void SaveStateText(TextWriter writer) { - SerializeEverything(new JsonTextWriter(writer) { Formatting = Formatting.None }); + SyncState(new AppleSerializer(writer)); } public void LoadStateText(TextReader reader) { - DeserializeEverything(new JsonTextReader(reader)); + SyncState(new AppleSerializer(reader)); } - /* - * These are horrible; the LoadStateBinary() takes over 10x as long as LoadStateText() - * Until we figure out why JSON.NET's BSONwriter sucks and how to fix it, stick with text-as-binary public void SaveStateBinary(BinaryWriter writer) { - SerializeEverything(new BsonWriter(writer)); + SyncState(new AppleSerializer(writer)); } public void LoadStateBinary(BinaryReader reader) { - DeserializeEverything(new BsonReader(reader)); - } - */ - /* - public void SaveStateBinary(BinaryWriter writer) - { - var tw = new StreamWriter(writer.BaseStream, new System.Text.UTF8Encoding(false)); - SaveStateText(tw); - tw.Flush(); - } - - public void LoadStateBinary(BinaryReader reader) - { - var tr = new StreamReader(reader.BaseStream, System.Text.Encoding.UTF8); - LoadStateText(tr); - }*/ - - // these homemade classes edge out the stock ones slightly, but need BufferedStream to not be bad - public void SaveStateBinary(BinaryWriter writer) - { - var buffer = new BufferedStream(writer.BaseStream, 16384); - var bw2 = new BinaryWriter(buffer); - SerializeEverything(new LBW(bw2)); - bw2.Flush(); - buffer.Flush(); - } - - public void LoadStateBinary(BinaryReader reader) - { - var buffer = new BufferedStream(reader.BaseStream, 16384); - var br2 = new BinaryReader(buffer); - DeserializeEverything(new LBR(br2)); + SyncState(new AppleSerializer(reader)); } public byte[] SaveStateBinary() { // our savestate array can be of varying sizes, so this can't be too clever - var stream = new MemoryStream(); - var writer = new BinaryWriter(stream); + using var stream = new MemoryStream(); + using var writer = new BinaryWriter(stream); SaveStateBinary(writer); writer.Flush(); return stream.ToArray(); } - private static JsonSerializer CreateSerializer() + private void SyncState(AppleSerializer ser) { - // TODO: converters could be cached for speedup + int version = 2; + ser.BeginSection(nameof(AppleII)); + ser.Sync(nameof(version), ref version); + ser.Sync("Frame", ref _frame); + ser.Sync("Lag", ref _lagcount); + ser.Sync("PrevDiskPressed", ref _prevPressed); + ser.Sync("NextDiskPressed", ref _nextPressed); + ser.Sync("CurrentDisk", ref _currentDisk); - var ser = new JsonSerializer + ser.BeginSection("Events"); + _machine.Events.Sync(ser); + ser.EndSection(); + + ser.BeginSection("Cpu"); + _machine.Cpu.Sync(ser); + ser.EndSection(); + + ser.BeginSection("Video"); + _machine.Video.Sync(ser); + ser.EndSection(); + + ser.BeginSection("Memory"); + _machine.Memory.Sync(ser); + ser.EndSection(); + + ser.BeginSection("NoSlotClock"); + _machine.NoSlotClock.Sync(ser); + ser.EndSection(); + + ser.BeginSection("DiskIIController"); + _machine.DiskIIController.Sync(ser); + ser.EndSection(); + + ser.EndSection(); + } + + public class AppleSerializer : Serializer, IComponentSerializer + { + public AppleSerializer(BinaryReader br) : base(br) { - TypeNameHandling = TypeNameHandling.Auto, - PreserveReferencesHandling = PreserveReferencesHandling.All, // leaving out Array is a very important problem, and means that we can't rely on a directly shared array to work. - ReferenceLoopHandling = ReferenceLoopHandling.Serialize, - }; + } - ser.Converters.Add(new TypeTypeConverter(new[] + public AppleSerializer(BinaryWriter bw) : base(bw) { - // all expected Types to convert are either in this assembly or mscorlib - typeof(Memory).Assembly, - typeof(object).Assembly - })); + } - ser.Converters.Add(new DelegateConverter()); - ser.Converters.Add(new ArrayConverter()); + public AppleSerializer(TextReader tr) : base(tr) + { + } - var cr = new DefaultContractResolver(); - cr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic; - ser.ContractResolver = cr; - - return ser; + public AppleSerializer(TextWriter tw) : base(tw) + { + } } } } diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs index b5ed1872a8..6ddf214cb7 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.cs @@ -57,7 +57,6 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII SetCallbacks(); - InitSaveStates(); SetupMemoryDomains(); PutSettings(settings ?? new Settings()); } @@ -72,7 +71,14 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII private readonly byte[] _appleIIRom; private readonly byte[] _diskIIRom; - public int CurrentDisk { get; private set; } + private int _currentDisk; + + public int CurrentDisk + { + get => _currentDisk; + set => _currentDisk = value; + } + public int DiskCount => _romSet.Count; public void SetDisk(int discNum) diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/Components.cs b/BizHawk.Emulation.Cores/Computers/AppleII/Components.cs index fec8a94e2d..6a7ae54a3c 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/Components.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/Components.cs @@ -7,11 +7,6 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII /// public sealed class Components { - /// - /// for deserialization only!! - /// - public Components() { } - public Components(byte[] appleIIe, byte[] diskIIRom) { Events = new MachineEvents(); @@ -19,6 +14,9 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII Cpu = new Cpu(Memory); Video = new Video(Events, Memory); + NoSlotClock = new NoSlotClock(Video); + DiskIIController = new DiskIIController(Video, diskIIRom); + var emptySlot = new EmptyPeripheralCard(Video); // Necessary because of tangling dependencies between memory and video classes @@ -28,7 +26,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII new EmptyCassetteComponent(), new Speaker(Events, Cpu), Video, - new NoSlotClock(Video), + NoSlotClock, emptySlot, emptySlot, emptySlot, @@ -42,9 +40,13 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII Video.Reset(); } - public MachineEvents Events { get; set; } - public Memory Memory { get; private set; } - public Cpu Cpu { get; private set; } - public Video Video { get; private set; } + public MachineEvents Events { get; } + public Memory Memory { get; } + public Cpu Cpu { get; } + public Video Video { get; } + + // Only needed for convenience of savestate syncing, else the memory component needs to do it + public NoSlotClock NoSlotClock { get; } + public DiskIIController DiskIIController { get; } } } diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/JsonConvert.cs b/BizHawk.Emulation.Cores/Computers/AppleII/JsonConvert.cs deleted file mode 100644 index 40c9b0e4a5..0000000000 --- a/BizHawk.Emulation.Cores/Computers/AppleII/JsonConvert.cs +++ /dev/null @@ -1,334 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Newtonsoft.Json; - -namespace BizHawk.Emulation.Cores.Computers.AppleII -{ - public class ArrayConverter : JsonConverter - { - // JSON.NET cannot, when reading, use PreserveReferencesHandling on arrays, although it fully supports it on writing. - // Doing so while being able to fully preserve circular references would require storing the length of the array, - // or reading ahead in the JSON to compute the length. For arrays that could contain reference types, we choose the latter. - // For arrays of primitive types, there is no issue. - - // TODO: on serialization, the type of the object is available, but is the expected type (ie, the one that we'll be fed during deserialization) available? - // need this to at least detect covariance cases... - - public override bool CanConvert(Type objectType) - { - if (!typeof(Array).IsAssignableFrom(objectType)) - { - return false; - } - - if (objectType.GetArrayRank() > 1) - { - throw new NotImplementedException(); - } - - return true; - } - - public override bool CanRead => true; - public override bool CanWrite => true; - - private readonly JsonSerializer _bareSerializer = new JsonSerializer(); // full default settings, separate context - - // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local - private static void ReadExpectType(JsonReader reader, JsonToken expected) - { - if (!reader.Read()) - { - throw new InvalidOperationException(); - } - - if (reader.TokenType != expected) - { - throw new InvalidOperationException(); - } - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - if (reader.TokenType == JsonToken.Null) - { - return null; - } - - if (reader.TokenType != JsonToken.StartObject) - { - throw new InvalidOperationException(); - } - - ReadExpectType(reader, JsonToken.PropertyName); - string prop = reader.Value.ToString(); - ReadExpectType(reader, JsonToken.String); - string id = reader.Value.ToString(); - if (prop == "$ref") - { - object ret = serializer.ReferenceResolver.ResolveReference(serializer, id); - ReadExpectType(reader, JsonToken.EndObject); - return ret; - } - - if (prop == "$id") - { - ReadExpectType(reader, JsonToken.PropertyName); - prop = reader.Value.ToString(); - if (prop == "$length") // complex array - { - ReadExpectType(reader, JsonToken.Integer); - int length = Convert.ToInt32(reader.Value); - ReadExpectType(reader, JsonToken.PropertyName); - if (reader.Value.ToString() != "$values") - throw new InvalidOperationException(); - - Type elementType = objectType.GetElementType(); - - // ReSharper disable once AssignNullToNotNullAttribute - Array ret = Array.CreateInstance(elementType, length); - - // must register reference before deserializing elements to handle possible circular references - serializer.ReferenceResolver.AddReference(serializer, id, ret); - int index = 0; - - ReadExpectType(reader, JsonToken.StartArray); - while (true) - { - if (!reader.Read()) - throw new InvalidOperationException(); - if (reader.TokenType == JsonToken.EndArray) - break; - ret.SetValue(serializer.Deserialize(reader, elementType), index++); - } - ReadExpectType(reader, JsonToken.EndObject); - return ret; - } - - if (prop == "$values") // simple array - { - if (!reader.Read()) - { - throw new InvalidOperationException(); - } - - object ret = _bareSerializer.Deserialize(reader, objectType); - - // OK to add this after deserializing, as arrays of primitive types can't contain backrefs - serializer.ReferenceResolver.AddReference(serializer, id, ret); - ReadExpectType(reader, JsonToken.EndObject); - return ret; - } - - throw new InvalidOperationException(); - } - - throw new InvalidOperationException(); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - if (serializer.ReferenceResolver.IsReferenced(serializer, value)) - { - writer.WriteStartObject(); - - writer.WritePropertyName("$ref"); - writer.WriteValue(serializer.ReferenceResolver.GetReference(serializer, value)); - - writer.WriteEndObject(); - } - else - { - writer.WriteStartObject(); - - writer.WritePropertyName("$id"); - writer.WriteValue(serializer.ReferenceResolver.GetReference(serializer, value)); - - var elementType = value.GetType().GetElementType(); - if (elementType?.IsPrimitive ?? false) - { - writer.WritePropertyName("$values"); - _bareSerializer.Serialize(writer, value); - } - else - { - var array = (Array)value; - writer.WritePropertyName("$length"); - writer.WriteValue(array.Length); - - writer.WritePropertyName("$values"); - writer.WriteStartArray(); - foreach (object o in array) - { - serializer.Serialize(writer, o, elementType); - } - - writer.WriteEndArray(); - } - - writer.WriteEndObject(); - } - } - } - - public class TypeTypeConverter : JsonConverter - { - // serialize and deserialize types, ignoring assembly entirely and only using namespace+typename - // all types, including generic type arguments to supplied types, must be in one of the declared assemblies (only checked on read!) - // the main goal here is to have something with a slight chance of working across versions - public TypeTypeConverter(IEnumerable ass) - { - _assemblies = ass.ToList(); - } - - private readonly List _assemblies; - private readonly Dictionary _readLookup = new Dictionary(); - - public override bool CanConvert(Type objectType) - { - return typeof(Type).IsAssignableFrom(objectType); - } - - public override bool CanRead => true; - public override bool CanWrite => true; - - private Type GetType(string name) - { - if (!_readLookup.TryGetValue(name, out var ret)) - { - ret = _assemblies.Select(ass => ass.GetType(name, false)).Single(t => t != null); - _readLookup.Add(name, ret); - } - - return ret; - } - - private static string GetName(Type type) - { - return $"{type.Namespace}.{type.Name}"; - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - if (reader.TokenType == JsonToken.Null) - { - return null; - } - - if (reader.TokenType == JsonToken.String) - { - return GetType(reader.Value.ToString()); - } - - if (reader.TokenType == JsonToken.StartArray) // full generic - { - List values = serializer.Deserialize>(reader); - return GetType(values[0]).MakeGenericType(values.Skip(1).Select(GetType).ToArray()); - } - - throw new InvalidOperationException(); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var type = (Type)value; - if (type.IsGenericType && !type.IsGenericTypeDefinition) - { - writer.WriteStartArray(); - writer.WriteValue(GetName(type)); - foreach (var t in type.GetGenericArguments()) - { - writer.WriteValue(GetName(t)); - } - writer.WriteEndArray(); - } - else - { - writer.WriteValue(GetName(type)); - } - } - } - - public class DelegateConverter : JsonConverter - { - // caveats: if used on anonymous delegates and/or closures, brittle to name changes in the generated classes and methods - // brittle to type name changes in general - // must be serialized in tree with any real classes referred to by closures - - // CAN NOT preserve reference equality of the delegates themselves, because the delegate must be created with - // target in one shot, with no possibility to change the target later. We preserve references to targets, - // and lose the ability to preserve references to delegates. - - - // TODO: much of this could be made somewhat smarter and more resilient - - public override bool CanConvert(Type objectType) - { - return typeof(Delegate).IsAssignableFrom(objectType); - } - - public override bool CanRead => true; - public override bool CanWrite => true; - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var slug = serializer.Deserialize(reader); - return slug?.GetDelegate(); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var slug = new Slug((Delegate)value); - serializer.Serialize(writer, slug); - } - - private class Slug - { - // ReSharper disable once MemberCanBePrivate.Local - // ReSharper disable once FieldCanBeMadeReadOnly.Local - public Type DelegateType; - - // ReSharper disable once MemberCanBePrivate.Local - // ReSharper disable once FieldCanBeMadeReadOnly.Local - public Type MethodDeclaringType; - - // ReSharper disable once MemberCanBePrivate.Local - // ReSharper disable once FieldCanBeMadeReadOnly.Local - public string MethodName; - - // ReSharper disable once MemberCanBePrivate.Local - // ReSharper disable once FieldCanBeMadeReadOnly.Local - public List MethodParameters; - - // ReSharper disable once MemberCanBePrivate.Local - // ReSharper disable once FieldCanBeMadeReadOnly.Local - public object Target; - - public Delegate GetDelegate() - { - var mi = MethodDeclaringType.GetMethod( - MethodName, - BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, - null, - MethodParameters.ToArray(), - null); - - // ReSharper disable once AssignNullToNotNullAttribute - return Delegate.CreateDelegate(DelegateType, Target, mi); - } - - public Slug() { } - - public Slug(Delegate d) - { - DelegateType = d.GetType(); - MethodDeclaringType = d.Method.DeclaringType; - MethodName = d.Method.Name; - MethodParameters = d.Method.GetParameters().Select(p => p.ParameterType).ToList(); - Target = d.Target; - } - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/LBSON.cs b/BizHawk.Emulation.Cores/Computers/AppleII/LBSON.cs deleted file mode 100644 index d2895b8802..0000000000 --- a/BizHawk.Emulation.Cores/Computers/AppleII/LBSON.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using System.IO; - -using Newtonsoft.Json; - -namespace BizHawk.Emulation.Cores.Computers.AppleII -{ - // barebones classes for writing and reading a simple bson-like format, used to gain a bit of speed in Apple II savestates - internal enum LBTOK : byte - { - Null, - Undefined, - StartArray, - EndArray, - StartObject, - EndObject, - Property, - S8, - U8, - S16, - U16, - S32, - U32, - S64, - U64, - False, - True, - String, - F32, - F64, - ByteArray, - } - - public class LBR : JsonReader - { - private readonly BinaryReader _r; - private object _v; - private JsonToken _t; - - public LBR(BinaryReader reader) - { - _r = reader; - } - - public override void Close() - { - } - - // as best as I can tell, the serializers refer to depth, but don't actually need to work except when doing certain error recovery - public override int Depth => 0; - - public override string Path => throw new NotImplementedException(); - - public override Type ValueType => _v?.GetType(); - - public override JsonToken TokenType => _t; - - public override object Value => _v; - - public override bool Read() - { - LBTOK l = (LBTOK)_r.ReadByte(); - switch (l) - { - case LBTOK.StartArray: _t = JsonToken.StartArray; _v = null; break; - case LBTOK.EndArray: _t = JsonToken.EndArray; _v = null; break; - case LBTOK.StartObject: _t = JsonToken.StartObject; _v = null; break; - case LBTOK.EndObject: _t = JsonToken.EndObject; _v = null; break; - case LBTOK.Null: _t = JsonToken.Null; _v = null; break; - case LBTOK.False: _t = JsonToken.Boolean; _v = false; break; - case LBTOK.True: _t = JsonToken.Boolean; _v = true; break; - case LBTOK.Property: _t = JsonToken.PropertyName; _v = _r.ReadString(); break; - case LBTOK.Undefined: _t = JsonToken.Undefined; _v = null; break; - case LBTOK.S8: _t = JsonToken.Integer; _v = _r.ReadSByte(); break; - case LBTOK.U8: _t = JsonToken.Integer; _v = _r.ReadByte(); break; - case LBTOK.S16: _t = JsonToken.Integer; _v = _r.ReadInt16(); break; - case LBTOK.U16: _t = JsonToken.Integer; _v = _r.ReadUInt16(); break; - case LBTOK.S32: _t = JsonToken.Integer; _v = _r.ReadInt32(); break; - case LBTOK.U32: _t = JsonToken.Integer; _v = _r.ReadUInt32(); break; - case LBTOK.S64: _t = JsonToken.Integer; _v = _r.ReadInt64(); break; - case LBTOK.U64: _t = JsonToken.Integer; _v = _r.ReadUInt64(); break; - case LBTOK.String: _t = JsonToken.String; _v = _r.ReadString(); break; - case LBTOK.F32: _t = JsonToken.Float; _v = _r.ReadSingle(); break; - case LBTOK.F64: _t = JsonToken.Float; _v = _r.ReadDouble(); break; - case LBTOK.ByteArray: _t = JsonToken.Bytes; _v = _r.ReadBytes(_r.ReadInt32()); break; - - default: - throw new InvalidOperationException(); - } - - return true; - } - - public override byte[] ReadAsBytes() - { - if (!Read() || _t != JsonToken.Bytes) - { - return null; - } - - return (byte[])_v; - } - - public override DateTime? ReadAsDateTime() - { - throw new NotImplementedException(); - } - - public override DateTimeOffset? ReadAsDateTimeOffset() - { - throw new NotImplementedException(); - } - - public override decimal? ReadAsDecimal() - { - throw new NotImplementedException(); - } - - public override int? ReadAsInt32() - { - // TODO: speed this up if needed - if (!Read()) - { - return null; - } - - switch (_t) - { - case JsonToken.Null: - return null; - case JsonToken.Integer: - case JsonToken.Float: - return Convert.ToInt32(_v); - case JsonToken.String: - if (int.TryParse(_v.ToString(), out var i)) - { - return i; - } - - return null; - default: - return null; - } - } - - public override string ReadAsString() - { - if (!Read()) - { - return null; - } - - switch (_t) - { - case JsonToken.Null: - return null; - case JsonToken.Float: - case JsonToken.Integer: - case JsonToken.Boolean: - case JsonToken.String: - return _v.ToString(); - default: - return null; - } - } - } - - public class LBW : JsonWriter - { - private readonly BinaryWriter w; - - private void WT(LBTOK t) - { - w.Write((byte)t); - } - - public LBW(BinaryWriter w) - { - this.w = w; - } - - public override void Flush() - { - w.Flush(); - } - - public override void Close() - { - } - - public override void WriteValue(bool value) { WT(value ? LBTOK.True : LBTOK.False); } - - public override void WriteValue(sbyte value) { WT(LBTOK.S8); w.Write(value); } - public override void WriteValue(byte value) { WT(LBTOK.U8); w.Write(value); } - public override void WriteValue(short value) { WT(LBTOK.S16); w.Write(value); } - public override void WriteValue(ushort value) { WT(LBTOK.U16); w.Write(value); } - public override void WriteValue(int value) { WT(LBTOK.S32); w.Write(value); } - public override void WriteValue(uint value) { WT(LBTOK.U32); w.Write(value); } - public override void WriteValue(long value) { WT(LBTOK.S64); w.Write(value); } - public override void WriteValue(ulong value) { WT(LBTOK.U64); w.Write(value); } - - public override void WriteStartArray() { WT(LBTOK.StartArray); } - public override void WriteEndArray() { WT(LBTOK.EndArray); } - public override void WriteStartObject() { WT(LBTOK.StartObject); } - public override void WriteEndObject() { WT(LBTOK.EndObject); } - public override void WriteNull() { WT(LBTOK.Null); } - public override void WriteUndefined() { WT(LBTOK.Undefined); } - - public override void WriteValue(float value) { WT(LBTOK.F32); w.Write(value); } - public override void WriteValue(double value) { WT(LBTOK.F64); w.Write(value); } - - public override void WriteValue(byte[] value) { WT(LBTOK.ByteArray); w.Write(value.Length); w.Write(value); } - - public override void WriteComment(string text) { throw new NotImplementedException(); } - public override void WriteWhitespace(string ws) { throw new NotImplementedException(); } - protected override void WriteIndent() { throw new NotImplementedException(); } - protected override void WriteIndentSpace() { throw new NotImplementedException(); } - public override void WriteEnd() { throw new NotImplementedException(); } - protected override void WriteEnd(JsonToken token) { throw new NotImplementedException(); } - public override void WriteRaw(string json) { throw new NotImplementedException(); } - public override void WriteRawValue(string json) { throw new NotImplementedException(); } - public override void WriteStartConstructor(string name) { throw new NotImplementedException(); } - public override void WriteEndConstructor() { throw new NotImplementedException(); } - protected override void WriteValueDelimiter() { throw new NotImplementedException(); } - - public override void WritePropertyName(string name) { WT(LBTOK.Property); w.Write(name); } - public override void WriteValue(string value) { WT(LBTOK.String); w.Write(value); } - public override void WritePropertyName(string name, bool escape) { WT(LBTOK.Property); w.Write(name); } // no escaping required - - public override void WriteValue(char value) { throw new NotImplementedException(); } - public override void WriteValue(DateTime value) { throw new NotImplementedException(); } - public override void WriteValue(DateTimeOffset value) { throw new NotImplementedException(); } - public override void WriteValue(decimal value) { throw new NotImplementedException(); } - public override void WriteValue(Guid value) { throw new NotImplementedException(); } - public override void WriteValue(TimeSpan value) { throw new NotImplementedException(); } - public override void WriteValue(Uri value) { throw new NotImplementedException(); } - } -} diff --git a/ExternalCoreProjects/Virtu/Cpu.Data.cs b/ExternalCoreProjects/Virtu/Cpu.Data.cs index 30bc7deda7..07ac1567eb 100644 --- a/ExternalCoreProjects/Virtu/Cpu.Data.cs +++ b/ExternalCoreProjects/Virtu/Cpu.Data.cs @@ -1,15 +1,50 @@ -using Newtonsoft.Json; -using System; +using System; namespace Jellyfish.Virtu { // ReSharper disable once UnusedMember.Global public partial class Cpu { - [JsonIgnore] + public void Sync(IComponentSerializer ser) + { + ser.Sync(nameof(_is65C02), ref _is65C02); + ser.Sync(nameof(_multiplier), ref _multiplier); + + ser.Sync(nameof(_ra), ref _ra); + ser.Sync(nameof(_rx), ref _rx); + ser.Sync(nameof(_ry), ref _ry); + ser.Sync(nameof(_rs), ref _rs); + ser.Sync(nameof(_rp), ref _rp); + ser.Sync(nameof(_rpc), ref _rpc); + ser.Sync(nameof(_ea), ref _ea); + ser.Sync(nameof(_cc), ref _cc); + ser.Sync(nameof(_opCode), ref _opCode); + ser.Sync(nameof(_cycles), ref _cycles); + + if (!ser.IsReader) + { + // A way to set the action callback + Is65C02 = _is65C02; + } + } + private Action[] _executeOpCode65N02; - [JsonIgnore] private Action[] _executeOpCode65C02; + private Action[] _executeOpCode; + + private bool _is65C02; + private int _multiplier; + + private int _ra; + private int _rx; + private int _ry; + private int _rs; + private int _rp; + private int _rpc; + private int _ea; + private int _cc; + private int _opCode; + private long _cycles; private const int Pc = 0x01; private const int Pz = 0x02; diff --git a/ExternalCoreProjects/Virtu/Cpu.cs b/ExternalCoreProjects/Virtu/Cpu.cs index e824ac6b2e..84d1d00454 100644 --- a/ExternalCoreProjects/Virtu/Cpu.cs +++ b/ExternalCoreProjects/Virtu/Cpu.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using Newtonsoft.Json; namespace Jellyfish.Virtu { @@ -13,19 +12,15 @@ namespace Jellyfish.Virtu int Execute(); long Cycles { get; } int Multiplier { get; } + + // ReSharper disable once UnusedMember.Global + void Sync(IComponentSerializer ser); } // ReSharper disable once UnusedMember.Global public sealed partial class Cpu : ICpu { - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private IMemoryBus _memory; - - // ReSharper disable once UnusedMember.Global - public Cpu() - { - InitializeOpCodeDelegates(); - } + private readonly IMemoryBus _memory; public Cpu(IMemoryBus memory) { @@ -3432,29 +3427,22 @@ namespace Jellyfish.Virtu set { _is65C02 = value; _executeOpCode = _is65C02 ? _executeOpCode65C02 : _executeOpCode65N02; } } - public int Multiplier { get; set; } + public int Multiplier { get => _multiplier; private set => _multiplier = value; } - public int RA { get; set; } - public int RX { get; set; } - public int RY { get; set; } - public int RS { get; set; } - public int RP { get; set; } - public int RPC { get; set; } - public int EA { get; private set; } - public int CC { get; private set; } - public int OpCode { get; private set; } - public long Cycles { get; private set; } + public int RA { get => _ra; set => _ra = value; } + public int RX { get => _rx; set => _rx = value; } + public int RY { get => _ry; set => _ry = value; } + public int RS { get => _rs; set => _rs = value; } + public int RP { get => _rp; set => _rp = value; } + public int RPC { get => _rpc; set => _rpc = value; } + public int EA { get => _ea; private set => _ea = value; } + public int CC { get => _cc; private set => _cc = value; } + public int OpCode { get => _opCode; private set => _opCode = value; } + public long Cycles { get => _cycles; private set => _cycles = value; } - [JsonIgnore] - private bool _is65C02; - [JsonIgnore] - private Action[] _executeOpCode; - - [JsonIgnore] public Action TraceCallback; /// Carry Flag - [JsonIgnore] public bool FlagC { get => (RP & 0x01) != 0; @@ -3462,7 +3450,6 @@ namespace Jellyfish.Virtu } /// Zero Flag - [JsonIgnore] public bool FlagZ { get => (RP & 0x02) != 0; @@ -3470,7 +3457,6 @@ namespace Jellyfish.Virtu } /// Interrupt Disable Flag - [JsonIgnore] public bool FlagI { get => (RP & 0x04) != 0; @@ -3478,7 +3464,6 @@ namespace Jellyfish.Virtu } /// Decimal Mode Flag - [JsonIgnore] public bool FlagD { get => (RP & 0x08) != 0; @@ -3486,7 +3471,6 @@ namespace Jellyfish.Virtu } /// Break Flag - [JsonIgnore] public bool FlagB { get => (RP & 0x10) != 0; @@ -3494,7 +3478,6 @@ namespace Jellyfish.Virtu } /// T... Flag - [JsonIgnore] public bool FlagT { get => (RP & 0x20) != 0; @@ -3502,7 +3485,6 @@ namespace Jellyfish.Virtu } /// Overflow Flag - [JsonIgnore] public bool FlagV { get => (RP & 0x40) != 0; @@ -3510,7 +3492,6 @@ namespace Jellyfish.Virtu } /// Negative Flag - [JsonIgnore] public bool FlagN { get => (RP & 0x80) != 0; diff --git a/ExternalCoreProjects/Virtu/Disk525.cs b/ExternalCoreProjects/Virtu/Disk525.cs index 03012e3383..65f22147d3 100644 --- a/ExternalCoreProjects/Virtu/Disk525.cs +++ b/ExternalCoreProjects/Virtu/Disk525.cs @@ -4,9 +4,8 @@ namespace Jellyfish.Virtu { internal abstract class Disk525 { - // ReSharper disable once UnusedMember.Global - // ReSharper disable once PublicConstructorInAbstractClass - public Disk525() { } + protected byte[] Data; + public bool IsWriteProtected; protected Disk525(byte[] data, bool isWriteProtected) { @@ -14,6 +13,12 @@ namespace Jellyfish.Virtu IsWriteProtected = isWriteProtected; } + public virtual void Sync(IComponentSerializer ser) + { + ser.Sync(nameof(Data), ref Data, false); + ser.Sync(nameof(IsWriteProtected), ref IsWriteProtected); + } + public static Disk525 CreateDisk(string name, byte[] data, bool isWriteProtected) { if (name == null) @@ -43,11 +48,6 @@ namespace Jellyfish.Virtu public abstract void ReadTrack(int number, int fraction, byte[] buffer); public abstract void WriteTrack(int number, int fraction, byte[] buffer); - public byte[] Data { get; protected set; } - - // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local - public bool IsWriteProtected { get; private set; } - public const int SectorCount = 16; public const int SectorSize = 0x100; public const int TrackSize = 0x1A00; diff --git a/ExternalCoreProjects/Virtu/DiskDsk.cs b/ExternalCoreProjects/Virtu/DiskDsk.cs index 95b014fc85..ff60fc1771 100644 --- a/ExternalCoreProjects/Virtu/DiskDsk.cs +++ b/ExternalCoreProjects/Virtu/DiskDsk.cs @@ -2,12 +2,19 @@ namespace Jellyfish.Virtu { + // ReSharper disable once UnusedMember.Global internal enum SectorSkew { None = 0, Dos, ProDos }; internal sealed class DiskDsk : Disk525 { - // ReSharper disable once UnusedMember.Global - public DiskDsk() { } + private const int SecondaryBufferLength = 0x56; + private const int Volume = 0xFE; + + private byte[] _trackBuffer; + private int _trackOffset; + private byte[] _primaryBuffer = new byte[0x100]; + private byte[] _secondaryBuffer = new byte[SecondaryBufferLength + 1]; + private int[] _sectorSkew; public DiskDsk(byte[] data, bool isWriteProtected, SectorSkew sectorSkew) : base(data, isWriteProtected) @@ -15,6 +22,16 @@ namespace Jellyfish.Virtu _sectorSkew = SectorSkewMode[(int)sectorSkew]; } + public override void Sync(IComponentSerializer ser) + { + ser.Sync(nameof(_trackBuffer), ref _trackBuffer, false); + ser.Sync(nameof(_trackOffset), ref _trackOffset); + ser.Sync(nameof(_primaryBuffer), ref _primaryBuffer, false); + ser.Sync(nameof(_secondaryBuffer), ref _secondaryBuffer, false); + ser.Sync(nameof(_sectorSkew), ref _sectorSkew, false); + base.Sync(ser); + } + public override void ReadTrack(int number, int fraction, byte[] buffer) { int track = number / 2; @@ -165,13 +182,12 @@ namespace Jellyfish.Virtu private int ReadNibble44() { - return (((ReadNibble() << 1) | 0x1) & ReadNibble()); + return ((ReadNibble() << 1) | 0x1) & ReadNibble(); } private byte ReadTranslatedNibble() { - byte data = NibbleToByte[ReadNibble()]; - return data; + return NibbleToByte[ReadNibble()]; } private bool ReadDataNibbles(int sectorOffset) @@ -268,14 +284,6 @@ namespace Jellyfish.Virtu WriteNibble(ByteToNibble[a]); // data checksum } - private byte[] _trackBuffer; - private int _trackOffset; - private byte[] _primaryBuffer = new byte[0x100]; - private const int SecondaryBufferLength = 0x56; - private byte[] _secondaryBuffer = new byte[SecondaryBufferLength + 1]; - private int[] _sectorSkew; - private const int Volume = 0xFE; - private static readonly byte[] SwapBits = { 0, 2, 1, 3 }; private static readonly int[] SectorSkewNone = diff --git a/ExternalCoreProjects/Virtu/DiskIIController.cs b/ExternalCoreProjects/Virtu/DiskIIController.cs index 138d4bad01..0275bdc836 100644 --- a/ExternalCoreProjects/Virtu/DiskIIController.cs +++ b/ExternalCoreProjects/Virtu/DiskIIController.cs @@ -8,16 +8,26 @@ namespace Jellyfish.Virtu // ReSharper disable once UnusedMemberInSuper.Global DiskIIDrive Drive1 { get; } + + // ReSharper disable once UnusedMember.Global + void Sync(IComponentSerializer ser); } // ReSharper disable once UnusedMember.Global public sealed class DiskIIController : IDiskIIController { - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private IVideo _video; + private const int Phase1On = 1 << 1; + private readonly IVideo _video; + private readonly byte[] _romRegionC1C7; - // ReSharper disable once UnusedMember.Global - public DiskIIController() { } + private bool _driveLight; + private int _latch; + private int _phaseStates; + private bool _motorOn; + private int _driveNumber; + private bool _loadMode; + private bool _writeMode; + private bool _driveSpin; public DiskIIController(IVideo video, byte[] diskIIRom) { @@ -32,8 +42,30 @@ namespace Jellyfish.Virtu _writeMode = false; } - public bool DriveLight { get; set; } + public DiskIIDrive Drive1 { get; } + public DiskIIDrive Drive2 { get; } + public bool DriveLight + { + get => _driveLight; + set => _driveLight = value; + } + + public void Sync(IComponentSerializer ser) + { + ser.Sync(nameof(_driveLight), ref _driveLight); + + ser.Sync(nameof(_latch), ref _latch); + ser.Sync(nameof(_phaseStates), ref _phaseStates); + ser.Sync(nameof(_motorOn), ref _motorOn); + ser.Sync(nameof(_driveNumber), ref _driveNumber); + ser.Sync(nameof(_loadMode), ref _loadMode); + ser.Sync(nameof(_writeMode), ref _writeMode); + ser.Sync(nameof(_driveSpin), ref _driveSpin); + + Drive1.Sync(ser); + Drive2.Sync(ser); + } public IList Drives => new List { Drive1, Drive2 }; public void WriteIoRegionC8CF(int address, int data) => _video.ReadFloatingBus(); @@ -236,24 +268,5 @@ namespace Jellyfish.Virtu Drives[_driveNumber].ApplyPhaseChange(_phaseStates); } } - - // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local - public DiskIIDrive Drive1 { get; private set; } - - - // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local - public DiskIIDrive Drive2 { get; private set; } - - private const int Phase1On = 1 << 1; - - private int _latch; - private int _phaseStates; - private bool _motorOn; - private int _driveNumber; - private bool _loadMode; - private bool _writeMode; - private bool _driveSpin; - - private byte[] _romRegionC1C7 = new byte[0x0100]; } } diff --git a/ExternalCoreProjects/Virtu/DiskIIDrive.cs b/ExternalCoreProjects/Virtu/DiskIIDrive.cs index ab96889a38..8ad31f65d0 100644 --- a/ExternalCoreProjects/Virtu/DiskIIDrive.cs +++ b/ExternalCoreProjects/Virtu/DiskIIDrive.cs @@ -1,14 +1,17 @@ -using Newtonsoft.Json; - -namespace Jellyfish.Virtu +namespace Jellyfish.Virtu { public sealed class DiskIIDrive { - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private IDiskIIController _diskController; + private readonly IDiskIIController _diskController; + private readonly int[][] _driveArmStepDelta = new int[PhaseCount][]; - // ReSharper disable once UnusedMember.Global - public DiskIIDrive() { } + private bool _trackLoaded; + private bool _trackChanged; + private int _trackNumber; + private int _trackOffset; + + private byte[] _trackData = new byte[Disk525.TrackSize]; + private Disk525 _disk; public DiskIIDrive(IDiskIIController diskController) { @@ -19,6 +22,18 @@ namespace Jellyfish.Virtu _driveArmStepDelta[3] = new[] { 0, 1, 0, 1, -1, 0, -1, 0, 0, 1, 0, 1, -1, 0, -1, 0 }; // phase 3 } + public void Sync(IComponentSerializer ser) + { + ser.Sync(nameof(_trackLoaded), ref _trackLoaded); + ser.Sync(nameof(_trackChanged), ref _trackChanged); + ser.Sync(nameof(_trackNumber), ref _trackNumber); + ser.Sync(nameof(_trackOffset), ref _trackOffset); + ser.Sync(nameof(_trackData), ref _trackData, false); + + // TODO: save the delta, this is saving the rom into save states + _disk?.Sync(ser); + } + // ReSharper disable once UnusedMember.Global public void InsertDisk(string name, byte[] data, bool isWriteProtected) { @@ -101,24 +116,9 @@ namespace Jellyfish.Virtu } } - [JsonIgnore] - public bool IsWriteProtected => _disk.IsWriteProtected; + public bool IsWriteProtected => _disk?.IsWriteProtected ?? false; private const int TrackNumberMax = 0x44; - private const int PhaseCount = 4; - - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private int[][] _driveArmStepDelta = new int[PhaseCount][]; - - private bool _trackLoaded; - private bool _trackChanged; - private int _trackNumber; - private int _trackOffset; - - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private byte[] _trackData = new byte[Disk525.TrackSize]; - - private Disk525 _disk; } } diff --git a/ExternalCoreProjects/Virtu/DiskNib.cs b/ExternalCoreProjects/Virtu/DiskNib.cs index 301513e692..6b2b615c71 100644 --- a/ExternalCoreProjects/Virtu/DiskNib.cs +++ b/ExternalCoreProjects/Virtu/DiskNib.cs @@ -4,9 +4,6 @@ namespace Jellyfish.Virtu { internal sealed class DiskNib : Disk525 { - // ReSharper disable once UnusedMember.Global - public DiskNib() { } - public DiskNib(byte[] data, bool isWriteProtected) : base(data, isWriteProtected) { diff --git a/ExternalCoreProjects/Virtu/IComponentSerializer.cs b/ExternalCoreProjects/Virtu/IComponentSerializer.cs new file mode 100644 index 0000000000..5a60c95a66 --- /dev/null +++ b/ExternalCoreProjects/Virtu/IComponentSerializer.cs @@ -0,0 +1,17 @@ +namespace Jellyfish.Virtu +{ + // Serves as a generalized interface to the BizHawk serializer + public interface IComponentSerializer + { + bool IsReader { get; } + void Sync(string name, ref bool val); + void Sync(string name, ref int val); + void Sync(string name, ref long val); + void Sync(string name, ref ulong val); + + void Sync(string name, ref bool[] val, bool useNull); + void Sync(string name, ref byte[] val, bool useNull); + void Sync(string name, ref ushort[] val, bool useNull); + void Sync(string name, ref int[] val, bool useNull); + } +} diff --git a/ExternalCoreProjects/Virtu/Keyboard.cs b/ExternalCoreProjects/Virtu/Keyboard.cs index 7802087208..b4246da83c 100644 --- a/ExternalCoreProjects/Virtu/Keyboard.cs +++ b/ExternalCoreProjects/Virtu/Keyboard.cs @@ -143,9 +143,6 @@ namespace Jellyfish.Virtu public sealed class Keyboard { - // ReSharper disable once UnusedMember.Global - public Keyboard() { } - static Keyboard() { for (int i = 0; i < 62; i++) @@ -226,15 +223,15 @@ namespace Jellyfish.Virtu 0x7a1a5a1a, // z }; - /// 0 - 55 + // key: 0 - 55 private static int KeyToAscii(int key, bool control, bool shift) { - int s = control ? (shift ? 0 : 16) : (shift ? 8 : 24); + int s = control ? shift ? 0 : 16 : shift ? 8 : 24; return (int)(KeyAsciiData[key] >> s & 0x7f); } // ReSharper disable once InconsistentNaming - private static Dictionary DescriptionsToKeys = new Dictionary(); + private static readonly Dictionary DescriptionsToKeys = new Dictionary(); private static Keys FromStrings(IEnumerable keynames) { diff --git a/ExternalCoreProjects/Virtu/MachineEvents.cs b/ExternalCoreProjects/Virtu/MachineEvents.cs index 588b71c50b..0f7d97414d 100644 --- a/ExternalCoreProjects/Virtu/MachineEvents.cs +++ b/ExternalCoreProjects/Virtu/MachineEvents.cs @@ -1,29 +1,87 @@ using System; using System.Collections.Generic; -using System.Globalization; +using System.Linq; namespace Jellyfish.Virtu { + public enum EventCallbacks + { + FlushOutput, + FlushRow, + LeaveVBlank, + ResetVsync, + InverseText + } + internal sealed class MachineEvent { - public MachineEvent(int delta, Action action) + public MachineEvent(int delta, EventCallbacks type) { Delta = delta; - Action = action; - } - - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "Delta = {0} Action = {{{1}.{2}}}", Delta, Action.Method.DeclaringType?.Name, Action.Method.Name); + Type = type; } public int Delta { get; set; } - public Action Action { get; set; } + public EventCallbacks Type { get; set; } } public sealed class MachineEvents { - public void AddEvent(int delta, Action action) + private readonly Dictionary _eventDelegates = new Dictionary(); + + private readonly LinkedList _used = new LinkedList(); + private readonly LinkedList _free = new LinkedList(); + + // ReSharper disable once UnusedMember.Global + public void Sync(IComponentSerializer ser) + { + if (ser.IsReader) + { + int[] usedDelta = new int[0]; + int[] usedType = new int[0]; + int[] freeDelta = new int[0]; + int[] freeType = new int[0]; + + ser.Sync("UsedDelta", ref usedDelta, false); + ser.Sync("UsedType", ref usedType, false); + ser.Sync("FreeDelta", ref freeDelta, false); + ser.Sync("FreeType", ref freeType, false); + + _used.Clear(); + for (int i = 0; i < usedDelta.Length; i++) + { + var e = new MachineEvent(usedDelta[i], (EventCallbacks)usedType[i]); + _used.AddLast(new LinkedListNode(e)); + } + + _free.Clear(); + for (int i = 0; i < freeDelta.Length; i++) + { + var e = new MachineEvent(freeDelta[i], (EventCallbacks)freeType[i]); + _free.AddLast(new LinkedListNode(e)); + } + } + else + { + var usedDelta = _used.Select(u => u.Delta).ToArray(); + var usedType = _used.Select(u => (int)u.Type).ToArray(); + var freeDelta = _free.Select(f => f.Delta).ToArray(); + var freeType = _free.Select(f => (int)f.Type).ToArray(); + + ser.Sync("UsedDelta", ref usedDelta, false); + ser.Sync("UsedType", ref usedType, false); + ser.Sync("FreeDelta", ref freeDelta, false); + ser.Sync("FreeType", ref freeType, false); + } + } + + public void AddEventDelegate(EventCallbacks type, Action action) + + { + _eventDelegates[type] = action; + } + + public void AddEvent(int delta, EventCallbacks type) { var node = _used.First; for (; node != null; node = node.Next) @@ -44,11 +102,11 @@ namespace Jellyfish.Virtu { _free.RemoveFirst(); newNode.Value.Delta = delta; - newNode.Value.Action = action; + newNode.Value.Type = type; } else { - newNode = new LinkedListNode(new MachineEvent(delta, action)); + newNode = new LinkedListNode(new MachineEvent(delta, type)); } if (node != null) @@ -61,7 +119,7 @@ namespace Jellyfish.Virtu } } - public int FindEvent(Action action) + public int FindEvent(EventCallbacks type) { int delta = 0; @@ -69,9 +127,9 @@ namespace Jellyfish.Virtu { delta += node.Value.Delta; - var other = node.Value.Action; + var other = node.Value.Type; - if (other.Method == action.Method && other.Target == action.Target) + if (other == type) { return delta; } @@ -88,7 +146,7 @@ namespace Jellyfish.Virtu while (node.Value.Delta <= 0) { - node.Value.Action(); + _eventDelegates[node.Value.Type](); RemoveEvent(node); node = _used.First; } @@ -104,11 +162,5 @@ namespace Jellyfish.Virtu _used.Remove(node); _free.AddFirst(node); // cache node; avoids garbage } - - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private LinkedList _used = new LinkedList(); - - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private LinkedList _free = new LinkedList(); } } diff --git a/ExternalCoreProjects/Virtu/Memory.Data.cs b/ExternalCoreProjects/Virtu/Memory.Data.cs index b41b6af390..ba658d204c 100644 --- a/ExternalCoreProjects/Virtu/Memory.Data.cs +++ b/ExternalCoreProjects/Virtu/Memory.Data.cs @@ -1,5 +1,4 @@ -using Newtonsoft.Json; -using System; +using System; namespace Jellyfish.Virtu { @@ -96,7 +95,6 @@ namespace Jellyfish.Virtu Video.ModeC, Video.ModeD, Video.Mode1, Video.Mode2, Video.ModeE, Video.ModeF, Video.Mode1, Video.Mode2 }; - [JsonIgnore] private Action[][][] WriteRamModeBankRegion; } } diff --git a/ExternalCoreProjects/Virtu/Memory.cs b/ExternalCoreProjects/Virtu/Memory.cs index 94fda5d1fa..48a1ebda66 100644 --- a/ExternalCoreProjects/Virtu/Memory.cs +++ b/ExternalCoreProjects/Virtu/Memory.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; namespace Jellyfish.Virtu { + // ReSharper disable once UnusedMember.Global public enum MonitorType { Unknown, Standard, Enhanced } public interface IMemoryBus @@ -31,8 +31,12 @@ namespace Jellyfish.Virtu int VideoMode { get; } MonitorType Monitor { get; } + + // ReSharper disable once UnusedMember.Global + void Sync(IComponentSerializer ser); } + // ReSharper disable once UnusedMember.Global public sealed partial class Memory : IMemoryBus { private IGamePort _gamePort; @@ -47,15 +51,30 @@ namespace Jellyfish.Virtu private IPeripheralCard _slot5; private IPeripheralCard _slot7; - // TODO: this shouldn't be in savestates! - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private byte[] _appleIIe; + private readonly byte[] _appleIIe; + private readonly byte[][] _regionRead = new byte[RegionCount][]; + private readonly byte[][] _regionWrite = new byte[RegionCount][]; + private readonly Action[] _writeRegion = new Action[RegionCount]; - // ReSharper disable once UnusedMember.Global - public Memory() - { - InitializeWriteDelegates(); - } + private bool _lagged; + private int _state; + private int _slotRegionC8CF; + private byte[] _zeroPage; + private byte[] _ramMainRegion0001 = new byte[0x0200]; + private byte[] _ramMainRegion02BF = new byte[0xBE00]; + private byte[] _ramMainBank1RegionD0DF = new byte[0x1000]; + private byte[] _ramMainBank2RegionD0DF = new byte[0x1000]; + private byte[] _ramMainRegionE0FF = new byte[0x2000]; + private byte[] _ramAuxRegion0001 = new byte[0x0200]; + private byte[] _ramAuxRegion02BF = new byte[0xBE00]; + private byte[] _ramAuxBank1RegionD0DF = new byte[0x1000]; + private byte[] _ramAuxBank2RegionD0DF = new byte[0x1000]; + private byte[] _ramAuxRegionE0FF = new byte[0x2000]; + + private byte[] _romExternalRegionC1CF = new byte[0x0F00]; + private byte[] _romInternalRegionC1CF = new byte[0x0F00]; + private byte[] _romRegionD0DF = new byte[0x1000]; + private byte[] _romRegionE0FF = new byte[0x2000]; // ReSharper disable once UnusedMember.Global public Memory(byte[] appleIIe) @@ -64,6 +83,37 @@ namespace Jellyfish.Virtu InitializeWriteDelegates(); } + public void Sync(IComponentSerializer ser) + { + ser.Sync(nameof(_lagged), ref _lagged); + ser.Sync(nameof(_state), ref _state); + ser.Sync(nameof(_slotRegionC8CF), ref _slotRegionC8CF); + ser.Sync(nameof(_zeroPage), ref _zeroPage, false); + + ser.Sync(nameof(_ramMainRegion0001), ref _ramMainRegion0001, false); + ser.Sync(nameof(_ramMainRegion02BF), ref _ramMainRegion02BF, false); + ser.Sync(nameof(_ramMainBank1RegionD0DF), ref _ramMainBank1RegionD0DF, false); + ser.Sync(nameof(_ramMainBank2RegionD0DF), ref _ramMainBank2RegionD0DF, false); + ser.Sync(nameof(_ramMainRegionE0FF), ref _ramMainRegionE0FF, false); + ser.Sync(nameof(_ramAuxRegion0001), ref _ramAuxRegion0001, false); + ser.Sync(nameof(_ramAuxRegion02BF), ref _ramAuxRegion02BF, false); + ser.Sync(nameof(_ramAuxBank1RegionD0DF), ref _ramAuxBank1RegionD0DF, false); + ser.Sync(nameof(_ramAuxBank2RegionD0DF), ref _ramAuxBank2RegionD0DF, false); + ser.Sync(nameof(_ramAuxRegionE0FF), ref _ramAuxRegionE0FF, false); + ser.Sync(nameof(_romExternalRegionC1CF), ref _romExternalRegionC1CF, false); + ser.Sync(nameof(_romInternalRegionC1CF), ref _romInternalRegionC1CF, false); + ser.Sync(nameof(_romRegionD0DF), ref _romRegionD0DF, false); + ser.Sync(nameof(_romRegionE0FF), ref _romRegionE0FF, false); + + if (ser.IsReader) + { + MapRegion0001(); + MapRegion02BF(); + MapRegionC0CF(); + MapRegionD0FF(); + } + } + // ReSharper disable once UnusedMember.Global public void Initialize( Keyboard keyboard, @@ -123,7 +173,7 @@ namespace Jellyfish.Virtu } } - public bool Lagged { get; set; } + public bool Lagged { get => _lagged; set => _lagged = value; } private IList Slots => new List { @@ -2100,51 +2150,15 @@ namespace Jellyfish.Virtu public MonitorType Monitor { get; private set; } public int VideoMode => StateVideoMode[_state & StateVideo]; - [JsonIgnore] private Action _writeIoRegionC0C0; - [JsonIgnore] private Action _writeIoRegionC1C7; - [JsonIgnore] private Action _writeIoRegionC3C3; - [JsonIgnore] private Action _writeIoRegionC8CF; - [JsonIgnore] private Action _writeRomRegionD0FF; - - [JsonIgnore] + public Action ReadCallback; - - [JsonIgnore] public Action WriteCallback; - - [JsonIgnore] public Action ExecuteCallback; - - [JsonIgnore] public Action InputCallback; - - private int _state; - private int _slotRegionC8CF; - - private byte[] _zeroPage; - private byte[][] _regionRead = new byte[RegionCount][]; - private byte[][] _regionWrite = new byte[RegionCount][]; - private Action[] _writeRegion = new Action[RegionCount]; - - private byte[] _ramMainRegion0001 = new byte[0x0200]; - private byte[] _ramMainRegion02BF = new byte[0xBE00]; - private byte[] _ramMainBank1RegionD0DF = new byte[0x1000]; - private byte[] _ramMainBank2RegionD0DF = new byte[0x1000]; - private byte[] _ramMainRegionE0FF = new byte[0x2000]; - private byte[] _ramAuxRegion0001 = new byte[0x0200]; - private byte[] _ramAuxRegion02BF = new byte[0xBE00]; - private byte[] _ramAuxBank1RegionD0DF = new byte[0x1000]; - private byte[] _ramAuxBank2RegionD0DF = new byte[0x1000]; - private byte[] _ramAuxRegionE0FF = new byte[0x2000]; - - private byte[] _romExternalRegionC1CF = new byte[0x0F00]; - private byte[] _romInternalRegionC1CF = new byte[0x0F00]; - private byte[] _romRegionD0DF = new byte[0x1000]; - private byte[] _romRegionE0FF = new byte[0x2000]; } } diff --git a/ExternalCoreProjects/Virtu/NoSlotClock.cs b/ExternalCoreProjects/Virtu/NoSlotClock.cs index 2e793d0d4e..79d8e91b45 100644 --- a/ExternalCoreProjects/Virtu/NoSlotClock.cs +++ b/ExternalCoreProjects/Virtu/NoSlotClock.cs @@ -6,16 +6,20 @@ namespace Jellyfish.Virtu { int Read(int address, int value); void Write(int address); + + // ReSharper disable once UnusedMember.Global + void Sync(IComponentSerializer ser); } // ReSharper disable once UnusedMember.Global public sealed class NoSlotClock : ISlotClock { - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private Video _video; + private readonly Video _video; - // ReSharper disable once UnusedMember.Global - public NoSlotClock() { } + private bool _clockEnabled; + private bool _writeEnabled; + private RingRegister _clockRegister; + private RingRegister _comparisonRegister; public NoSlotClock(Video video) { @@ -27,6 +31,14 @@ namespace Jellyfish.Virtu _comparisonRegister = new RingRegister(ClockInitSequence, 0x1); } + public void Sync(IComponentSerializer ser) + { + ser.Sync(nameof(_clockEnabled), ref _clockEnabled); + ser.Sync(nameof(_writeEnabled), ref _writeEnabled); + _clockRegister.Sync(ser); + _comparisonRegister.Sync(ser); + } + public int Read(int address, int value) { // this may read or write the clock @@ -142,11 +154,6 @@ namespace Jellyfish.Virtu private const ulong ClockInitSequence = 0x5CA33AC55CA33AC5; - private bool _clockEnabled; - private bool _writeEnabled; - private RingRegister _clockRegister; - private RingRegister _comparisonRegister; - private struct RingRegister { public RingRegister(ulong data, ulong mask) @@ -155,6 +162,12 @@ namespace Jellyfish.Virtu _mask = mask; } + public void Sync(IComponentSerializer ser) + { + ser.Sync(nameof(_data), ref _data); + ser.Sync(nameof(_mask), ref _mask); + } + public void Reset() { _mask = 0x1; diff --git a/ExternalCoreProjects/Virtu/Speaker.cs b/ExternalCoreProjects/Virtu/Speaker.cs index 0e3c371a14..09e1a34b63 100644 --- a/ExternalCoreProjects/Virtu/Speaker.cs +++ b/ExternalCoreProjects/Virtu/Speaker.cs @@ -1,8 +1,4 @@ -using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; - -namespace Jellyfish.Virtu +namespace Jellyfish.Virtu { public interface ISpeaker { @@ -13,46 +9,46 @@ namespace Jellyfish.Virtu // ReSharper disable once UnusedMember.Global void GetSamples(out short[] samples, out int nSamp); + + // ReSharper disable once UnusedMember.Global + void Sync(IComponentSerializer ser); } + // ReSharper disable once UnusedMember.Global public sealed class Speaker : ISpeaker { - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private MachineEvents _events; - - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private ICpu _cpu; - - public Speaker() { } - public Speaker(MachineEvents events, ICpu cpu) - { - _events = events; - _cpu = cpu; - _flushOutputEvent = FlushOutputEvent; // cache delegates; avoids garbage - - _events.AddEvent(CyclesPerFlush * _cpu.Multiplier, _flushOutputEvent); - - _isHigh = false; - _highCycles = _totalCycles = 0; - } - private const int CyclesPerFlush = 23; - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private Action _flushOutputEvent; + private readonly MachineEvents _events; + private readonly ICpu _cpu; private bool _isHigh; private int _highCycles; private int _totalCycles; private long _lastCycles; - [JsonIgnore] // only relevant if trying to savestate mid-frame + // only relevant if trying to savestate mid-frame private readonly short[] _buffer = new short[4096]; - - [JsonIgnore] // only relevant if trying to savestate mid-frame private int _position; - #region Api + public Speaker(MachineEvents events, ICpu cpu) + { + _events = events; + _cpu = cpu; + _events.AddEventDelegate(EventCallbacks.FlushOutput, FlushOutputEvent); + _events.AddEvent(CyclesPerFlush * _cpu.Multiplier, EventCallbacks.FlushOutput); + + _isHigh = false; + _highCycles = _totalCycles = 0; + } + + public void Sync(IComponentSerializer ser) + { + ser.Sync(nameof(_isHigh), ref _isHigh); + ser.Sync(nameof(_highCycles), ref _highCycles); + ser.Sync(nameof(_totalCycles), ref _totalCycles); + ser.Sync(nameof(_lastCycles), ref _lastCycles); + } public void Clear() { @@ -66,8 +62,6 @@ namespace Jellyfish.Virtu _position = 0; } - #endregion - public void ToggleOutput() { UpdateCycles(); @@ -81,7 +75,7 @@ namespace Jellyfish.Virtu Output(_highCycles * short.MaxValue / _totalCycles); _highCycles = _totalCycles = 0; - _events.AddEvent(CyclesPerFlush * _cpu.Multiplier, _flushOutputEvent); + _events.AddEvent(CyclesPerFlush * _cpu.Multiplier, EventCallbacks.FlushOutput); } private void UpdateCycles() @@ -104,12 +98,5 @@ namespace Jellyfish.Virtu _buffer[_position++] = (short)data; } } - - - [OnDeserialized] - private void OnDeserialized(StreamingContext context) - { - _position = 0; - } } } diff --git a/ExternalCoreProjects/Virtu/Video.Data.cs b/ExternalCoreProjects/Virtu/Video.Data.cs index 0ecda3e45f..92b74fb921 100644 --- a/ExternalCoreProjects/Virtu/Video.Data.cs +++ b/ExternalCoreProjects/Virtu/Video.Data.cs @@ -1615,8 +1615,7 @@ namespace Jellyfish.Virtu public const int ModeE = 0xE; public const int ModeF = 0xF; - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private Action[] _flushRowMode; + private readonly Action[] _flushRowMode; private const int Width = 560; private const int Height = VLineEnterVBlank; diff --git a/ExternalCoreProjects/Virtu/Video.cs b/ExternalCoreProjects/Virtu/Video.cs index 967b51e69e..4d12a1607b 100644 --- a/ExternalCoreProjects/Virtu/Video.cs +++ b/ExternalCoreProjects/Virtu/Video.cs @@ -1,6 +1,4 @@ using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; namespace Jellyfish.Virtu { @@ -9,7 +7,9 @@ namespace Jellyfish.Virtu public interface IVideo { + // ReSharper disable once UnusedMember.Global int[] GetVideoBuffer(); + // ReSharper disable once UnusedMember.Global void Reset(); void DirtyCell(int addressOffset); @@ -20,26 +20,42 @@ namespace Jellyfish.Virtu void SetCharSet(); bool IsVBlank { get; } + + // ReSharper disable once UnusedMember.Global + void Sync(IComponentSerializer ser); } public sealed partial class Video : IVideo { - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private MachineEvents _events; + private readonly MachineEvents _events; + private readonly IMemoryBus _memory; - // ReSharper disable once FieldCanBeMadeReadOnly.Local - private IMemoryBus _memory; + private readonly int[] _colorPalette = new int[ColorPaletteCount]; + + private bool _isVBlank; + private int[] _framebuffer = new int[560 * 384]; + private bool _isTextInversed; + private ScannerOptions _scannerOptions; + private int _cyclesPerVBlank; + private int _cyclesPerVBlankPreset; + private int _cyclesPerVSync; + private int _cyclesPerFlash; + private int _vCountPreset; + private int _vLineLeaveVBlank; + private ushort[] _charSet; + private bool _isMonochrome; + + private bool[] _isCellDirty = new bool[Height * CellColumns + 1]; // includes sentinel - public Video() { } public Video(MachineEvents events, IMemoryBus memory) { _events = events; _memory = memory; - _flushRowEvent = FlushRowEvent; // cache delegates; avoids garbage - _inverseTextEvent = InverseTextEvent; - _leaveVBlankEvent = LeaveVBlankEvent; - _resetVSyncEvent = ResetVSyncEvent; + _events.AddEventDelegate(EventCallbacks.FlushRow, FlushRowEvent); + _events.AddEventDelegate(EventCallbacks.InverseText, InverseTextEvent); + _events.AddEventDelegate(EventCallbacks.LeaveVBlank, LeaveVBlankEvent); + _events.AddEventDelegate(EventCallbacks.ResetVsync, ResetVSyncEvent); _flushRowMode = new Action[] { @@ -73,22 +89,43 @@ namespace Jellyfish.Virtu IsMonochrome = false; ScannerOptions = ScannerOptions.None; - IsVBlank = true; + _isVBlank = true; - _events.AddEvent(_cyclesPerVBlankPreset, _leaveVBlankEvent); // align flush events with scanner; assumes vcount preset at start of frame [3-15, 3-16] - _events.AddEvent(_cyclesPerVSync, _resetVSyncEvent); - _events.AddEvent(_cyclesPerFlash, _inverseTextEvent); + _events.AddEvent(_cyclesPerVBlankPreset, EventCallbacks.LeaveVBlank); // align flush events with scanner; assumes vcount preset at start of frame [3-15, 3-16] + _events.AddEvent(_cyclesPerVSync, EventCallbacks.ResetVsync); + _events.AddEvent(_cyclesPerFlash, EventCallbacks.InverseText); + } + + public void Sync(IComponentSerializer ser) + { + if (ser.IsReader) + { + int option = 0; + ser.Sync(nameof(_scannerOptions), ref option); + _scannerOptions = (ScannerOptions)option; + } + else + { + int option = (int)_scannerOptions; + ser.Sync(nameof(_scannerOptions), ref option); + } + + ser.Sync(nameof(_isVBlank), ref _isVBlank); + ser.Sync(nameof(_framebuffer), ref _framebuffer, false); + ser.Sync(nameof(_isTextInversed), ref _isTextInversed); + ser.Sync(nameof(_cyclesPerVBlank), ref _cyclesPerVBlank); + ser.Sync(nameof(_cyclesPerVBlankPreset), ref _cyclesPerVBlankPreset); + ser.Sync(nameof(_cyclesPerVSync), ref _cyclesPerVSync); + ser.Sync(nameof(_cyclesPerFlash), ref _cyclesPerFlash); + ser.Sync(nameof(_vCountPreset), ref _vCountPreset); + 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); } // ReSharper disable once UnusedMember.Global - public int[] GetVideoBuffer() => Framebuffer; - - [OnDeserialized] - private void OnDeserialized(StreamingContext context) - { - // the VideoService forgets all of its information on LoadState - DirtyScreen(); - } + public int[] GetVideoBuffer() => _framebuffer; public void Reset() { @@ -149,7 +186,7 @@ namespace Jellyfish.Virtu public int ReadFloatingBus() // [5-40] { // derive scanner counters from current cycles into frame; assumes hcount and vcount preset at start of frame [3-13, 3-15, 3-16] - int cycles = _cyclesPerVSync - _events.FindEvent(_resetVSyncEvent); + int cycles = _cyclesPerVSync - _events.FindEvent(EventCallbacks.ResetVsync); int hClock = cycles % CyclesPerHSync; int hCount = (hClock != 0) ? HCountPreset + hClock - 1 : 0; int vLine = cycles / CyclesPerHSync; @@ -918,18 +955,18 @@ namespace Jellyfish.Virtu private void FlushRowEvent() { - int y = (_cyclesPerVSync - _cyclesPerVBlankPreset - _events.FindEvent(_resetVSyncEvent)) / CyclesPerHSync; + int y = (_cyclesPerVSync - _cyclesPerVBlankPreset - _events.FindEvent(EventCallbacks.ResetVsync)) / CyclesPerHSync; _flushRowMode[_memory.VideoMode](y - CellHeight); // in arrears if (y < Height) { - _events.AddEvent(CyclesPerFlush, _flushRowEvent); + _events.AddEvent(CyclesPerFlush, EventCallbacks.FlushRow); } else { - IsVBlank = true; - _events.AddEvent(_cyclesPerVBlank, _leaveVBlankEvent); + _isVBlank = true; + _events.AddEvent(_cyclesPerVBlank, EventCallbacks.LeaveVBlank); } } @@ -946,18 +983,18 @@ namespace Jellyfish.Virtu { _isTextInversed = !_isTextInversed; DirtyScreenText(); - _events.AddEvent(_cyclesPerFlash, _inverseTextEvent); + _events.AddEvent(_cyclesPerFlash, EventCallbacks.InverseText); } private void LeaveVBlankEvent() { - IsVBlank = false; - _events.AddEvent(CyclesPerFlush, _flushRowEvent); + _isVBlank = false; + _events.AddEvent(CyclesPerFlush, EventCallbacks.FlushRow); } private void ResetVSyncEvent() { - _events.AddEvent(_cyclesPerVSync, _resetVSyncEvent); + _events.AddEvent(_cyclesPerVSync, EventCallbacks.ResetVsync); } private void SetPalette() @@ -1002,13 +1039,10 @@ namespace Jellyfish.Virtu DirtyScreen(); } - // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local - private int[] Framebuffer { get; set; } = new int[560 * 384]; - private void SetPixel(int x, int y, int color) { int i = 560 * (2 * y) + x; - Framebuffer[i] = Framebuffer[i + 560] = _colorPalette[color]; + _framebuffer[i] = _framebuffer[i + 560] = _colorPalette[color]; } private void SetScanner() @@ -1030,53 +1064,36 @@ namespace Jellyfish.Virtu _cyclesPerFlash = VSyncsPerFlash * _cyclesPerVSync; } - [JsonIgnore] - public bool IsMonochrome { get => _isMonochrome; set { _isMonochrome = value; DirtyScreen(); } } + public bool IsMonochrome + { + get => _isMonochrome; + set { _isMonochrome = value; DirtyScreen(); } + } - [JsonIgnore] - internal ScannerOptions ScannerOptions { get => _scannerOptions; set { _scannerOptions = value; SetScanner(); } } + internal ScannerOptions ScannerOptions + { + get => _scannerOptions; + set { _scannerOptions = value; SetScanner(); } + } - public bool IsVBlank { get; private set; } + public bool IsVBlank => _isVBlank; - private Action _flushRowEvent; - private Action _inverseTextEvent; - private Action _leaveVBlankEvent; - private Action _resetVSyncEvent; - - private int _colorBlack; - private int _colorDarkBlue; - private int _colorDarkGreen; - private int _colorMediumBlue; - private int _colorBrown; - private int _colorLightGrey; - private int _colorGreen; - private int _colorAquamarine; - private int _colorDeepRed; - private int _colorPurple; - private int _colorDarkGrey; - private int _colorLightBlue; - private int _colorOrange; - private int _colorPink; - private int _colorYellow; - private int _colorWhite; - private int _colorMonochrome; - - [JsonIgnore] // not sync relevant - private bool _isMonochrome; - - private bool _isTextInversed; - private ScannerOptions _scannerOptions; - private int _cyclesPerVBlank; - private int _cyclesPerVBlankPreset; - private int _cyclesPerVSync; - private int _cyclesPerFlash; - private int _vCountPreset; - private int _vLineLeaveVBlank; - - private ushort[] _charSet; - private int[] _colorPalette = new int[ColorPaletteCount]; - - [JsonIgnore] // everything is automatically dirtied on load, so no need to save - private bool[] _isCellDirty = new bool[Height * CellColumns + 1]; // includes sentinel + private readonly int _colorBlack; + private readonly int _colorDarkBlue; + private readonly int _colorDarkGreen; + private readonly int _colorMediumBlue; + private readonly int _colorBrown; + private readonly int _colorLightGrey; + private readonly int _colorGreen; + private readonly int _colorAquamarine; + private readonly int _colorDeepRed; + private readonly int _colorPurple; + private readonly int _colorDarkGrey; + private readonly int _colorLightBlue; + private readonly int _colorOrange; + private readonly int _colorPink; + private readonly int _colorYellow; + private readonly int _colorWhite; + private readonly int _colorMonochrome; } } diff --git a/ExternalCoreProjects/Virtu/Virtu.csproj b/ExternalCoreProjects/Virtu/Virtu.csproj index 1dded9d752..0c5a5d2c32 100644 --- a/ExternalCoreProjects/Virtu/Virtu.csproj +++ b/ExternalCoreProjects/Virtu/Virtu.csproj @@ -33,9 +33,6 @@ false - - ..\..\References\Newtonsoft.Json.dll - @@ -53,6 +50,7 @@ + diff --git a/ExternalCoreProjects/Virtu/Virtu.sln.DotSettings b/ExternalCoreProjects/Virtu/Virtu.sln.DotSettings index 3211634121..26b84f0adc 100644 --- a/ExternalCoreProjects/Virtu/Virtu.sln.DotSettings +++ b/ExternalCoreProjects/Virtu/Virtu.sln.DotSettings @@ -1,5 +1,6 @@  DO_NOT_SHOW + 0DF AA AB AC diff --git a/References/Virtu.dll b/References/Virtu.dll index a9e41c53f1..a54efa1546 100644 Binary files a/References/Virtu.dll and b/References/Virtu.dll differ