Apple II - refactor states to use Serializer (#1859)
* apple II - poc to redo binary states to use the Serializer and pass it into virtu * virtu - savestate logic for Video * AppleII - serialize more components with AppleSerializer * cleanup * refactor MachineEvents to store delegates to call and have the calling code interact with an enum, prep work for serializing this class * apple II - first attempt at serializing Machine events * cleanup * add isvblank to savestates * put more things in savestates, cleanup * cleanup * cleanup, save more things * cleanup, fixes * cleanup, save more things * virtu - savestate fixes * cleanup * save CurrentDisk in savestates
This commit is contained in:
parent
9831ca59bd
commit
33ad336b6a
|
@ -8,7 +8,8 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
|
||||||
|
|
||||||
public ControllerDefinition ControllerDefinition => AppleIIController;
|
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";
|
public string SystemId => "AppleII";
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,12 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
|
||||||
{
|
{
|
||||||
public partial class AppleII : IInputPollable
|
public partial class AppleII : IInputPollable
|
||||||
{
|
{
|
||||||
public int LagCount { get; set; }
|
private int _lagcount;
|
||||||
|
public int LagCount
|
||||||
|
{
|
||||||
|
get => _lagcount;
|
||||||
|
set => _lagcount = value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsLagFrame
|
public bool IsLagFrame
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,181 +1,97 @@
|
||||||
using System;
|
using System.IO;
|
||||||
using System.IO;
|
using BizHawk.Common;
|
||||||
|
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
using Jellyfish.Virtu;
|
using Jellyfish.Virtu;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Computers.AppleII
|
namespace BizHawk.Emulation.Cores.Computers.AppleII
|
||||||
{
|
{
|
||||||
public partial class AppleII : ITextStatable
|
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<Components>(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)
|
public void SaveStateText(TextWriter writer)
|
||||||
{
|
{
|
||||||
SerializeEverything(new JsonTextWriter(writer) { Formatting = Formatting.None });
|
SyncState(new AppleSerializer(writer));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadStateText(TextReader reader)
|
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)
|
public void SaveStateBinary(BinaryWriter writer)
|
||||||
{
|
{
|
||||||
SerializeEverything(new BsonWriter(writer));
|
SyncState(new AppleSerializer(writer));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadStateBinary(BinaryReader reader)
|
public void LoadStateBinary(BinaryReader reader)
|
||||||
{
|
{
|
||||||
DeserializeEverything(new BsonReader(reader));
|
SyncState(new AppleSerializer(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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] SaveStateBinary()
|
public byte[] SaveStateBinary()
|
||||||
{
|
{
|
||||||
// our savestate array can be of varying sizes, so this can't be too clever
|
// our savestate array can be of varying sizes, so this can't be too clever
|
||||||
var stream = new MemoryStream();
|
using var stream = new MemoryStream();
|
||||||
var writer = new BinaryWriter(stream);
|
using var writer = new BinaryWriter(stream);
|
||||||
SaveStateBinary(writer);
|
SaveStateBinary(writer);
|
||||||
writer.Flush();
|
writer.Flush();
|
||||||
return stream.ToArray();
|
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());
|
public AppleSerializer(TextReader tr) : base(tr)
|
||||||
ser.Converters.Add(new ArrayConverter());
|
{
|
||||||
|
}
|
||||||
|
|
||||||
var cr = new DefaultContractResolver();
|
public AppleSerializer(TextWriter tw) : base(tw)
|
||||||
cr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic;
|
{
|
||||||
ser.ContractResolver = cr;
|
}
|
||||||
|
|
||||||
return ser;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,6 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
|
||||||
|
|
||||||
SetCallbacks();
|
SetCallbacks();
|
||||||
|
|
||||||
InitSaveStates();
|
|
||||||
SetupMemoryDomains();
|
SetupMemoryDomains();
|
||||||
PutSettings(settings ?? new Settings());
|
PutSettings(settings ?? new Settings());
|
||||||
}
|
}
|
||||||
|
@ -72,7 +71,14 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
|
||||||
private readonly byte[] _appleIIRom;
|
private readonly byte[] _appleIIRom;
|
||||||
private readonly byte[] _diskIIRom;
|
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 int DiskCount => _romSet.Count;
|
||||||
|
|
||||||
public void SetDisk(int discNum)
|
public void SetDisk(int discNum)
|
||||||
|
|
|
@ -7,11 +7,6 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class Components
|
public sealed class Components
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// for deserialization only!!
|
|
||||||
/// </summary>
|
|
||||||
public Components() { }
|
|
||||||
|
|
||||||
public Components(byte[] appleIIe, byte[] diskIIRom)
|
public Components(byte[] appleIIe, byte[] diskIIRom)
|
||||||
{
|
{
|
||||||
Events = new MachineEvents();
|
Events = new MachineEvents();
|
||||||
|
@ -19,6 +14,9 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
|
||||||
Cpu = new Cpu(Memory);
|
Cpu = new Cpu(Memory);
|
||||||
Video = new Video(Events, Memory);
|
Video = new Video(Events, Memory);
|
||||||
|
|
||||||
|
NoSlotClock = new NoSlotClock(Video);
|
||||||
|
DiskIIController = new DiskIIController(Video, diskIIRom);
|
||||||
|
|
||||||
var emptySlot = new EmptyPeripheralCard(Video);
|
var emptySlot = new EmptyPeripheralCard(Video);
|
||||||
|
|
||||||
// Necessary because of tangling dependencies between memory and video classes
|
// Necessary because of tangling dependencies between memory and video classes
|
||||||
|
@ -28,7 +26,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
|
||||||
new EmptyCassetteComponent(),
|
new EmptyCassetteComponent(),
|
||||||
new Speaker(Events, Cpu),
|
new Speaker(Events, Cpu),
|
||||||
Video,
|
Video,
|
||||||
new NoSlotClock(Video),
|
NoSlotClock,
|
||||||
emptySlot,
|
emptySlot,
|
||||||
emptySlot,
|
emptySlot,
|
||||||
emptySlot,
|
emptySlot,
|
||||||
|
@ -42,9 +40,13 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
|
||||||
Video.Reset();
|
Video.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MachineEvents Events { get; set; }
|
public MachineEvents Events { get; }
|
||||||
public Memory Memory { get; private set; }
|
public Memory Memory { get; }
|
||||||
public Cpu Cpu { get; private set; }
|
public Cpu Cpu { get; }
|
||||||
public Video Video { get; private set; }
|
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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<Assembly> ass)
|
|
||||||
{
|
|
||||||
_assemblies = ass.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly List<Assembly> _assemblies;
|
|
||||||
private readonly Dictionary<string, Type> _readLookup = new Dictionary<string, Type>();
|
|
||||||
|
|
||||||
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<string> values = serializer.Deserialize<List<string>>(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<Slug>(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<Type> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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(); }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +1,50 @@
|
||||||
using Newtonsoft.Json;
|
using System;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
// ReSharper disable once UnusedMember.Global
|
// ReSharper disable once UnusedMember.Global
|
||||||
public partial class Cpu
|
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;
|
private Action[] _executeOpCode65N02;
|
||||||
[JsonIgnore]
|
|
||||||
private Action[] _executeOpCode65C02;
|
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 Pc = 0x01;
|
||||||
private const int Pz = 0x02;
|
private const int Pz = 0x02;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
|
@ -13,19 +12,15 @@ namespace Jellyfish.Virtu
|
||||||
int Execute();
|
int Execute();
|
||||||
long Cycles { get; }
|
long Cycles { get; }
|
||||||
int Multiplier { get; }
|
int Multiplier { get; }
|
||||||
|
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
|
void Sync(IComponentSerializer ser);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once UnusedMember.Global
|
// ReSharper disable once UnusedMember.Global
|
||||||
public sealed partial class Cpu : ICpu
|
public sealed partial class Cpu : ICpu
|
||||||
{
|
{
|
||||||
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
private readonly IMemoryBus _memory;
|
||||||
private IMemoryBus _memory;
|
|
||||||
|
|
||||||
// ReSharper disable once UnusedMember.Global
|
|
||||||
public Cpu()
|
|
||||||
{
|
|
||||||
InitializeOpCodeDelegates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cpu(IMemoryBus memory)
|
public Cpu(IMemoryBus memory)
|
||||||
{
|
{
|
||||||
|
@ -3432,29 +3427,22 @@ namespace Jellyfish.Virtu
|
||||||
set { _is65C02 = value; _executeOpCode = _is65C02 ? _executeOpCode65C02 : _executeOpCode65N02; }
|
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 RA { get => _ra; set => _ra = value; }
|
||||||
public int RX { get; set; }
|
public int RX { get => _rx; set => _rx = value; }
|
||||||
public int RY { get; set; }
|
public int RY { get => _ry; set => _ry = value; }
|
||||||
public int RS { get; set; }
|
public int RS { get => _rs; set => _rs = value; }
|
||||||
public int RP { get; set; }
|
public int RP { get => _rp; set => _rp = value; }
|
||||||
public int RPC { get; set; }
|
public int RPC { get => _rpc; set => _rpc = value; }
|
||||||
public int EA { get; private set; }
|
public int EA { get => _ea; private set => _ea = value; }
|
||||||
public int CC { get; private set; }
|
public int CC { get => _cc; private set => _cc = value; }
|
||||||
public int OpCode { get; private set; }
|
public int OpCode { get => _opCode; private set => _opCode = value; }
|
||||||
public long Cycles { get; private set; }
|
public long Cycles { get => _cycles; private set => _cycles = value; }
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
private bool _is65C02;
|
|
||||||
[JsonIgnore]
|
|
||||||
private Action[] _executeOpCode;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Action<string[]> TraceCallback;
|
public Action<string[]> TraceCallback;
|
||||||
|
|
||||||
/// <summary>Carry Flag</summary>
|
/// <summary>Carry Flag</summary>
|
||||||
[JsonIgnore]
|
|
||||||
public bool FlagC
|
public bool FlagC
|
||||||
{
|
{
|
||||||
get => (RP & 0x01) != 0;
|
get => (RP & 0x01) != 0;
|
||||||
|
@ -3462,7 +3450,6 @@ namespace Jellyfish.Virtu
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Zero Flag</summary>
|
/// <summary>Zero Flag</summary>
|
||||||
[JsonIgnore]
|
|
||||||
public bool FlagZ
|
public bool FlagZ
|
||||||
{
|
{
|
||||||
get => (RP & 0x02) != 0;
|
get => (RP & 0x02) != 0;
|
||||||
|
@ -3470,7 +3457,6 @@ namespace Jellyfish.Virtu
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Interrupt Disable Flag</summary>
|
/// <summary>Interrupt Disable Flag</summary>
|
||||||
[JsonIgnore]
|
|
||||||
public bool FlagI
|
public bool FlagI
|
||||||
{
|
{
|
||||||
get => (RP & 0x04) != 0;
|
get => (RP & 0x04) != 0;
|
||||||
|
@ -3478,7 +3464,6 @@ namespace Jellyfish.Virtu
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Decimal Mode Flag</summary>
|
/// <summary>Decimal Mode Flag</summary>
|
||||||
[JsonIgnore]
|
|
||||||
public bool FlagD
|
public bool FlagD
|
||||||
{
|
{
|
||||||
get => (RP & 0x08) != 0;
|
get => (RP & 0x08) != 0;
|
||||||
|
@ -3486,7 +3471,6 @@ namespace Jellyfish.Virtu
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Break Flag</summary>
|
/// <summary>Break Flag</summary>
|
||||||
[JsonIgnore]
|
|
||||||
public bool FlagB
|
public bool FlagB
|
||||||
{
|
{
|
||||||
get => (RP & 0x10) != 0;
|
get => (RP & 0x10) != 0;
|
||||||
|
@ -3494,7 +3478,6 @@ namespace Jellyfish.Virtu
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>T... Flag</summary>
|
/// <summary>T... Flag</summary>
|
||||||
[JsonIgnore]
|
|
||||||
public bool FlagT
|
public bool FlagT
|
||||||
{
|
{
|
||||||
get => (RP & 0x20) != 0;
|
get => (RP & 0x20) != 0;
|
||||||
|
@ -3502,7 +3485,6 @@ namespace Jellyfish.Virtu
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Overflow Flag</summary>
|
/// <summary>Overflow Flag</summary>
|
||||||
[JsonIgnore]
|
|
||||||
public bool FlagV
|
public bool FlagV
|
||||||
{
|
{
|
||||||
get => (RP & 0x40) != 0;
|
get => (RP & 0x40) != 0;
|
||||||
|
@ -3510,7 +3492,6 @@ namespace Jellyfish.Virtu
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Negative Flag</summary>
|
/// <summary>Negative Flag</summary>
|
||||||
[JsonIgnore]
|
|
||||||
public bool FlagN
|
public bool FlagN
|
||||||
{
|
{
|
||||||
get => (RP & 0x80) != 0;
|
get => (RP & 0x80) != 0;
|
||||||
|
|
|
@ -4,9 +4,8 @@ namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
internal abstract class Disk525
|
internal abstract class Disk525
|
||||||
{
|
{
|
||||||
// ReSharper disable once UnusedMember.Global
|
protected byte[] Data;
|
||||||
// ReSharper disable once PublicConstructorInAbstractClass
|
public bool IsWriteProtected;
|
||||||
public Disk525() { }
|
|
||||||
|
|
||||||
protected Disk525(byte[] data, bool isWriteProtected)
|
protected Disk525(byte[] data, bool isWriteProtected)
|
||||||
{
|
{
|
||||||
|
@ -14,6 +13,12 @@ namespace Jellyfish.Virtu
|
||||||
IsWriteProtected = isWriteProtected;
|
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)
|
public static Disk525 CreateDisk(string name, byte[] data, bool isWriteProtected)
|
||||||
{
|
{
|
||||||
if (name == null)
|
if (name == null)
|
||||||
|
@ -43,11 +48,6 @@ namespace Jellyfish.Virtu
|
||||||
public abstract void ReadTrack(int number, int fraction, byte[] buffer);
|
public abstract void ReadTrack(int number, int fraction, byte[] buffer);
|
||||||
public abstract void WriteTrack(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 SectorCount = 16;
|
||||||
public const int SectorSize = 0x100;
|
public const int SectorSize = 0x100;
|
||||||
public const int TrackSize = 0x1A00;
|
public const int TrackSize = 0x1A00;
|
||||||
|
|
|
@ -2,12 +2,19 @@
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
internal enum SectorSkew { None = 0, Dos, ProDos };
|
internal enum SectorSkew { None = 0, Dos, ProDos };
|
||||||
|
|
||||||
internal sealed class DiskDsk : Disk525
|
internal sealed class DiskDsk : Disk525
|
||||||
{
|
{
|
||||||
// ReSharper disable once UnusedMember.Global
|
private const int SecondaryBufferLength = 0x56;
|
||||||
public DiskDsk() { }
|
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)
|
public DiskDsk(byte[] data, bool isWriteProtected, SectorSkew sectorSkew)
|
||||||
: base(data, isWriteProtected)
|
: base(data, isWriteProtected)
|
||||||
|
@ -15,6 +22,16 @@ namespace Jellyfish.Virtu
|
||||||
_sectorSkew = SectorSkewMode[(int)sectorSkew];
|
_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)
|
public override void ReadTrack(int number, int fraction, byte[] buffer)
|
||||||
{
|
{
|
||||||
int track = number / 2;
|
int track = number / 2;
|
||||||
|
@ -165,13 +182,12 @@ namespace Jellyfish.Virtu
|
||||||
|
|
||||||
private int ReadNibble44()
|
private int ReadNibble44()
|
||||||
{
|
{
|
||||||
return (((ReadNibble() << 1) | 0x1) & ReadNibble());
|
return ((ReadNibble() << 1) | 0x1) & ReadNibble();
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte ReadTranslatedNibble()
|
private byte ReadTranslatedNibble()
|
||||||
{
|
{
|
||||||
byte data = NibbleToByte[ReadNibble()];
|
return NibbleToByte[ReadNibble()];
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ReadDataNibbles(int sectorOffset)
|
private bool ReadDataNibbles(int sectorOffset)
|
||||||
|
@ -268,14 +284,6 @@ namespace Jellyfish.Virtu
|
||||||
WriteNibble(ByteToNibble[a]); // data checksum
|
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 byte[] SwapBits = { 0, 2, 1, 3 };
|
||||||
|
|
||||||
private static readonly int[] SectorSkewNone =
|
private static readonly int[] SectorSkewNone =
|
||||||
|
|
|
@ -8,16 +8,26 @@ namespace Jellyfish.Virtu
|
||||||
|
|
||||||
// ReSharper disable once UnusedMemberInSuper.Global
|
// ReSharper disable once UnusedMemberInSuper.Global
|
||||||
DiskIIDrive Drive1 { get; }
|
DiskIIDrive Drive1 { get; }
|
||||||
|
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
|
void Sync(IComponentSerializer ser);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once UnusedMember.Global
|
// ReSharper disable once UnusedMember.Global
|
||||||
public sealed class DiskIIController : IDiskIIController
|
public sealed class DiskIIController : IDiskIIController
|
||||||
{
|
{
|
||||||
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
private const int Phase1On = 1 << 1;
|
||||||
private IVideo _video;
|
private readonly IVideo _video;
|
||||||
|
private readonly byte[] _romRegionC1C7;
|
||||||
|
|
||||||
// ReSharper disable once UnusedMember.Global
|
private bool _driveLight;
|
||||||
public DiskIIController() { }
|
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)
|
public DiskIIController(IVideo video, byte[] diskIIRom)
|
||||||
{
|
{
|
||||||
|
@ -32,8 +42,30 @@ namespace Jellyfish.Virtu
|
||||||
_writeMode = false;
|
_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<DiskIIDrive> Drives => new List<DiskIIDrive> { Drive1, Drive2 };
|
public IList<DiskIIDrive> Drives => new List<DiskIIDrive> { Drive1, Drive2 };
|
||||||
|
|
||||||
public void WriteIoRegionC8CF(int address, int data) => _video.ReadFloatingBus();
|
public void WriteIoRegionC8CF(int address, int data) => _video.ReadFloatingBus();
|
||||||
|
@ -236,24 +268,5 @@ namespace Jellyfish.Virtu
|
||||||
Drives[_driveNumber].ApplyPhaseChange(_phaseStates);
|
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];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
using Newtonsoft.Json;
|
namespace Jellyfish.Virtu
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
|
||||||
{
|
{
|
||||||
public sealed class DiskIIDrive
|
public sealed class DiskIIDrive
|
||||||
{
|
{
|
||||||
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
private readonly IDiskIIController _diskController;
|
||||||
private IDiskIIController _diskController;
|
private readonly int[][] _driveArmStepDelta = new int[PhaseCount][];
|
||||||
|
|
||||||
// ReSharper disable once UnusedMember.Global
|
private bool _trackLoaded;
|
||||||
public DiskIIDrive() { }
|
private bool _trackChanged;
|
||||||
|
private int _trackNumber;
|
||||||
|
private int _trackOffset;
|
||||||
|
|
||||||
|
private byte[] _trackData = new byte[Disk525.TrackSize];
|
||||||
|
private Disk525 _disk;
|
||||||
|
|
||||||
public DiskIIDrive(IDiskIIController diskController)
|
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
|
_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
|
// ReSharper disable once UnusedMember.Global
|
||||||
public void InsertDisk(string name, byte[] data, bool isWriteProtected)
|
public void InsertDisk(string name, byte[] data, bool isWriteProtected)
|
||||||
{
|
{
|
||||||
|
@ -101,24 +116,9 @@ namespace Jellyfish.Virtu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
public bool IsWriteProtected => _disk?.IsWriteProtected ?? false;
|
||||||
public bool IsWriteProtected => _disk.IsWriteProtected;
|
|
||||||
|
|
||||||
private const int TrackNumberMax = 0x44;
|
private const int TrackNumberMax = 0x44;
|
||||||
|
|
||||||
private const int PhaseCount = 4;
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,6 @@ namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
internal sealed class DiskNib : Disk525
|
internal sealed class DiskNib : Disk525
|
||||||
{
|
{
|
||||||
// ReSharper disable once UnusedMember.Global
|
|
||||||
public DiskNib() { }
|
|
||||||
|
|
||||||
public DiskNib(byte[] data, bool isWriteProtected)
|
public DiskNib(byte[] data, bool isWriteProtected)
|
||||||
: base(data, isWriteProtected)
|
: base(data, isWriteProtected)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -143,9 +143,6 @@ namespace Jellyfish.Virtu
|
||||||
|
|
||||||
public sealed class Keyboard
|
public sealed class Keyboard
|
||||||
{
|
{
|
||||||
// ReSharper disable once UnusedMember.Global
|
|
||||||
public Keyboard() { }
|
|
||||||
|
|
||||||
static Keyboard()
|
static Keyboard()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 62; i++)
|
for (int i = 0; i < 62; i++)
|
||||||
|
@ -226,15 +223,15 @@ namespace Jellyfish.Virtu
|
||||||
0x7a1a5a1a, // z
|
0x7a1a5a1a, // z
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <param name="key">0 - 55</param>
|
// key: 0 - 55
|
||||||
private static int KeyToAscii(int key, bool control, bool shift)
|
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);
|
return (int)(KeyAsciiData[key] >> s & 0x7f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
private static Dictionary<string, Keys> DescriptionsToKeys = new Dictionary<string, Keys>();
|
private static readonly Dictionary<string, Keys> DescriptionsToKeys = new Dictionary<string, Keys>();
|
||||||
|
|
||||||
private static Keys FromStrings(IEnumerable<string> keynames)
|
private static Keys FromStrings(IEnumerable<string> keynames)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,29 +1,87 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
|
public enum EventCallbacks
|
||||||
|
{
|
||||||
|
FlushOutput,
|
||||||
|
FlushRow,
|
||||||
|
LeaveVBlank,
|
||||||
|
ResetVsync,
|
||||||
|
InverseText
|
||||||
|
}
|
||||||
|
|
||||||
internal sealed class MachineEvent
|
internal sealed class MachineEvent
|
||||||
{
|
{
|
||||||
public MachineEvent(int delta, Action action)
|
public MachineEvent(int delta, EventCallbacks type)
|
||||||
{
|
{
|
||||||
Delta = delta;
|
Delta = delta;
|
||||||
Action = action;
|
Type = type;
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.InvariantCulture, "Delta = {0} Action = {{{1}.{2}}}", Delta, Action.Method.DeclaringType?.Name, Action.Method.Name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Delta { get; set; }
|
public int Delta { get; set; }
|
||||||
public Action Action { get; set; }
|
public EventCallbacks Type { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class MachineEvents
|
public sealed class MachineEvents
|
||||||
{
|
{
|
||||||
public void AddEvent(int delta, Action action)
|
private readonly Dictionary<EventCallbacks, Action> _eventDelegates = new Dictionary<EventCallbacks, Action>();
|
||||||
|
|
||||||
|
private readonly LinkedList<MachineEvent> _used = new LinkedList<MachineEvent>();
|
||||||
|
private readonly LinkedList<MachineEvent> _free = new LinkedList<MachineEvent>();
|
||||||
|
|
||||||
|
// 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<MachineEvent>(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
_free.Clear();
|
||||||
|
for (int i = 0; i < freeDelta.Length; i++)
|
||||||
|
{
|
||||||
|
var e = new MachineEvent(freeDelta[i], (EventCallbacks)freeType[i]);
|
||||||
|
_free.AddLast(new LinkedListNode<MachineEvent>(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;
|
var node = _used.First;
|
||||||
for (; node != null; node = node.Next)
|
for (; node != null; node = node.Next)
|
||||||
|
@ -44,11 +102,11 @@ namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
_free.RemoveFirst();
|
_free.RemoveFirst();
|
||||||
newNode.Value.Delta = delta;
|
newNode.Value.Delta = delta;
|
||||||
newNode.Value.Action = action;
|
newNode.Value.Type = type;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
newNode = new LinkedListNode<MachineEvent>(new MachineEvent(delta, action));
|
newNode = new LinkedListNode<MachineEvent>(new MachineEvent(delta, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node != null)
|
if (node != null)
|
||||||
|
@ -61,7 +119,7 @@ namespace Jellyfish.Virtu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int FindEvent(Action action)
|
public int FindEvent(EventCallbacks type)
|
||||||
{
|
{
|
||||||
int delta = 0;
|
int delta = 0;
|
||||||
|
|
||||||
|
@ -69,9 +127,9 @@ namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
delta += node.Value.Delta;
|
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;
|
return delta;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +146,7 @@ namespace Jellyfish.Virtu
|
||||||
|
|
||||||
while (node.Value.Delta <= 0)
|
while (node.Value.Delta <= 0)
|
||||||
{
|
{
|
||||||
node.Value.Action();
|
_eventDelegates[node.Value.Type]();
|
||||||
RemoveEvent(node);
|
RemoveEvent(node);
|
||||||
node = _used.First;
|
node = _used.First;
|
||||||
}
|
}
|
||||||
|
@ -104,11 +162,5 @@ namespace Jellyfish.Virtu
|
||||||
_used.Remove(node);
|
_used.Remove(node);
|
||||||
_free.AddFirst(node); // cache node; avoids garbage
|
_free.AddFirst(node); // cache node; avoids garbage
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
|
||||||
private LinkedList<MachineEvent> _used = new LinkedList<MachineEvent>();
|
|
||||||
|
|
||||||
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
|
||||||
private LinkedList<MachineEvent> _free = new LinkedList<MachineEvent>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using Newtonsoft.Json;
|
using System;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
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
|
Video.ModeC, Video.ModeD, Video.Mode1, Video.Mode2, Video.ModeE, Video.ModeF, Video.Mode1, Video.Mode2
|
||||||
};
|
};
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
private Action<int, byte>[][][] WriteRamModeBankRegion;
|
private Action<int, byte>[][][] WriteRamModeBankRegion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
public enum MonitorType { Unknown, Standard, Enhanced }
|
public enum MonitorType { Unknown, Standard, Enhanced }
|
||||||
|
|
||||||
public interface IMemoryBus
|
public interface IMemoryBus
|
||||||
|
@ -31,8 +31,12 @@ namespace Jellyfish.Virtu
|
||||||
|
|
||||||
int VideoMode { get; }
|
int VideoMode { get; }
|
||||||
MonitorType Monitor { get; }
|
MonitorType Monitor { get; }
|
||||||
|
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
|
void Sync(IComponentSerializer ser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
public sealed partial class Memory : IMemoryBus
|
public sealed partial class Memory : IMemoryBus
|
||||||
{
|
{
|
||||||
private IGamePort _gamePort;
|
private IGamePort _gamePort;
|
||||||
|
@ -47,15 +51,30 @@ namespace Jellyfish.Virtu
|
||||||
private IPeripheralCard _slot5;
|
private IPeripheralCard _slot5;
|
||||||
private IPeripheralCard _slot7;
|
private IPeripheralCard _slot7;
|
||||||
|
|
||||||
// TODO: this shouldn't be in savestates!
|
private readonly byte[] _appleIIe;
|
||||||
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
private readonly byte[][] _regionRead = new byte[RegionCount][];
|
||||||
private byte[] _appleIIe;
|
private readonly byte[][] _regionWrite = new byte[RegionCount][];
|
||||||
|
private readonly Action<int, byte>[] _writeRegion = new Action<int, byte>[RegionCount];
|
||||||
|
|
||||||
// ReSharper disable once UnusedMember.Global
|
private bool _lagged;
|
||||||
public Memory()
|
private int _state;
|
||||||
{
|
private int _slotRegionC8CF;
|
||||||
InitializeWriteDelegates();
|
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
|
// ReSharper disable once UnusedMember.Global
|
||||||
public Memory(byte[] appleIIe)
|
public Memory(byte[] appleIIe)
|
||||||
|
@ -64,6 +83,37 @@ namespace Jellyfish.Virtu
|
||||||
InitializeWriteDelegates();
|
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
|
// ReSharper disable once UnusedMember.Global
|
||||||
public void Initialize(
|
public void Initialize(
|
||||||
Keyboard keyboard,
|
Keyboard keyboard,
|
||||||
|
@ -123,7 +173,7 @@ namespace Jellyfish.Virtu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Lagged { get; set; }
|
public bool Lagged { get => _lagged; set => _lagged = value; }
|
||||||
|
|
||||||
private IList<IPeripheralCard> Slots => new List<IPeripheralCard>
|
private IList<IPeripheralCard> Slots => new List<IPeripheralCard>
|
||||||
{
|
{
|
||||||
|
@ -2100,51 +2150,15 @@ namespace Jellyfish.Virtu
|
||||||
public MonitorType Monitor { get; private set; }
|
public MonitorType Monitor { get; private set; }
|
||||||
public int VideoMode => StateVideoMode[_state & StateVideo];
|
public int VideoMode => StateVideoMode[_state & StateVideo];
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
private Action<int, byte> _writeIoRegionC0C0;
|
private Action<int, byte> _writeIoRegionC0C0;
|
||||||
[JsonIgnore]
|
|
||||||
private Action<int, byte> _writeIoRegionC1C7;
|
private Action<int, byte> _writeIoRegionC1C7;
|
||||||
[JsonIgnore]
|
|
||||||
private Action<int, byte> _writeIoRegionC3C3;
|
private Action<int, byte> _writeIoRegionC3C3;
|
||||||
[JsonIgnore]
|
|
||||||
private Action<int, byte> _writeIoRegionC8CF;
|
private Action<int, byte> _writeIoRegionC8CF;
|
||||||
[JsonIgnore]
|
|
||||||
private Action<int, byte> _writeRomRegionD0FF;
|
private Action<int, byte> _writeRomRegionD0FF;
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Action<uint> ReadCallback;
|
public Action<uint> ReadCallback;
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Action<uint> WriteCallback;
|
public Action<uint> WriteCallback;
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Action<uint> ExecuteCallback;
|
public Action<uint> ExecuteCallback;
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Action InputCallback;
|
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<int, byte>[] _writeRegion = new Action<int, byte>[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];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,20 @@ namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
int Read(int address, int value);
|
int Read(int address, int value);
|
||||||
void Write(int address);
|
void Write(int address);
|
||||||
|
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
|
void Sync(IComponentSerializer ser);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once UnusedMember.Global
|
// ReSharper disable once UnusedMember.Global
|
||||||
public sealed class NoSlotClock : ISlotClock
|
public sealed class NoSlotClock : ISlotClock
|
||||||
{
|
{
|
||||||
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
private readonly Video _video;
|
||||||
private Video _video;
|
|
||||||
|
|
||||||
// ReSharper disable once UnusedMember.Global
|
private bool _clockEnabled;
|
||||||
public NoSlotClock() { }
|
private bool _writeEnabled;
|
||||||
|
private RingRegister _clockRegister;
|
||||||
|
private RingRegister _comparisonRegister;
|
||||||
|
|
||||||
public NoSlotClock(Video video)
|
public NoSlotClock(Video video)
|
||||||
{
|
{
|
||||||
|
@ -27,6 +31,14 @@ namespace Jellyfish.Virtu
|
||||||
_comparisonRegister = new RingRegister(ClockInitSequence, 0x1);
|
_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)
|
public int Read(int address, int value)
|
||||||
{
|
{
|
||||||
// this may read or write the clock
|
// this may read or write the clock
|
||||||
|
@ -142,11 +154,6 @@ namespace Jellyfish.Virtu
|
||||||
|
|
||||||
private const ulong ClockInitSequence = 0x5CA33AC55CA33AC5;
|
private const ulong ClockInitSequence = 0x5CA33AC55CA33AC5;
|
||||||
|
|
||||||
private bool _clockEnabled;
|
|
||||||
private bool _writeEnabled;
|
|
||||||
private RingRegister _clockRegister;
|
|
||||||
private RingRegister _comparisonRegister;
|
|
||||||
|
|
||||||
private struct RingRegister
|
private struct RingRegister
|
||||||
{
|
{
|
||||||
public RingRegister(ulong data, ulong mask)
|
public RingRegister(ulong data, ulong mask)
|
||||||
|
@ -155,6 +162,12 @@ namespace Jellyfish.Virtu
|
||||||
_mask = mask;
|
_mask = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Sync(IComponentSerializer ser)
|
||||||
|
{
|
||||||
|
ser.Sync(nameof(_data), ref _data);
|
||||||
|
ser.Sync(nameof(_mask), ref _mask);
|
||||||
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
_mask = 0x1;
|
_mask = 0x1;
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
using System;
|
namespace Jellyfish.Virtu
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
|
||||||
{
|
{
|
||||||
public interface ISpeaker
|
public interface ISpeaker
|
||||||
{
|
{
|
||||||
|
@ -13,46 +9,46 @@ namespace Jellyfish.Virtu
|
||||||
|
|
||||||
// ReSharper disable once UnusedMember.Global
|
// ReSharper disable once UnusedMember.Global
|
||||||
void GetSamples(out short[] samples, out int nSamp);
|
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
|
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;
|
private const int CyclesPerFlush = 23;
|
||||||
|
|
||||||
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
private readonly MachineEvents _events;
|
||||||
private Action _flushOutputEvent;
|
private readonly ICpu _cpu;
|
||||||
|
|
||||||
private bool _isHigh;
|
private bool _isHigh;
|
||||||
private int _highCycles;
|
private int _highCycles;
|
||||||
private int _totalCycles;
|
private int _totalCycles;
|
||||||
private long _lastCycles;
|
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];
|
private readonly short[] _buffer = new short[4096];
|
||||||
|
|
||||||
[JsonIgnore] // only relevant if trying to savestate mid-frame
|
|
||||||
private int _position;
|
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()
|
public void Clear()
|
||||||
{
|
{
|
||||||
|
@ -66,8 +62,6 @@ namespace Jellyfish.Virtu
|
||||||
_position = 0;
|
_position = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public void ToggleOutput()
|
public void ToggleOutput()
|
||||||
{
|
{
|
||||||
UpdateCycles();
|
UpdateCycles();
|
||||||
|
@ -81,7 +75,7 @@ namespace Jellyfish.Virtu
|
||||||
Output(_highCycles * short.MaxValue / _totalCycles);
|
Output(_highCycles * short.MaxValue / _totalCycles);
|
||||||
_highCycles = _totalCycles = 0;
|
_highCycles = _totalCycles = 0;
|
||||||
|
|
||||||
_events.AddEvent(CyclesPerFlush * _cpu.Multiplier, _flushOutputEvent);
|
_events.AddEvent(CyclesPerFlush * _cpu.Multiplier, EventCallbacks.FlushOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateCycles()
|
private void UpdateCycles()
|
||||||
|
@ -104,12 +98,5 @@ namespace Jellyfish.Virtu
|
||||||
_buffer[_position++] = (short)data;
|
_buffer[_position++] = (short)data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[OnDeserialized]
|
|
||||||
private void OnDeserialized(StreamingContext context)
|
|
||||||
{
|
|
||||||
_position = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1615,8 +1615,7 @@ namespace Jellyfish.Virtu
|
||||||
public const int ModeE = 0xE;
|
public const int ModeE = 0xE;
|
||||||
public const int ModeF = 0xF;
|
public const int ModeF = 0xF;
|
||||||
|
|
||||||
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
private readonly Action<int>[] _flushRowMode;
|
||||||
private Action<int>[] _flushRowMode;
|
|
||||||
|
|
||||||
private const int Width = 560;
|
private const int Width = 560;
|
||||||
private const int Height = VLineEnterVBlank;
|
private const int Height = VLineEnterVBlank;
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
|
@ -9,7 +7,9 @@ namespace Jellyfish.Virtu
|
||||||
|
|
||||||
public interface IVideo
|
public interface IVideo
|
||||||
{
|
{
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
int[] GetVideoBuffer();
|
int[] GetVideoBuffer();
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
void DirtyCell(int addressOffset);
|
void DirtyCell(int addressOffset);
|
||||||
|
@ -20,26 +20,42 @@ namespace Jellyfish.Virtu
|
||||||
void SetCharSet();
|
void SetCharSet();
|
||||||
|
|
||||||
bool IsVBlank { get; }
|
bool IsVBlank { get; }
|
||||||
|
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
|
void Sync(IComponentSerializer ser);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed partial class Video : IVideo
|
public sealed partial class Video : IVideo
|
||||||
{
|
{
|
||||||
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
private readonly MachineEvents _events;
|
||||||
private MachineEvents _events;
|
private readonly IMemoryBus _memory;
|
||||||
|
|
||||||
// ReSharper disable once FieldCanBeMadeReadOnly.Local
|
private readonly int[] _colorPalette = new int[ColorPaletteCount];
|
||||||
private IMemoryBus _memory;
|
|
||||||
|
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)
|
public Video(MachineEvents events, IMemoryBus memory)
|
||||||
{
|
{
|
||||||
_events = events;
|
_events = events;
|
||||||
_memory = memory;
|
_memory = memory;
|
||||||
|
|
||||||
_flushRowEvent = FlushRowEvent; // cache delegates; avoids garbage
|
_events.AddEventDelegate(EventCallbacks.FlushRow, FlushRowEvent);
|
||||||
_inverseTextEvent = InverseTextEvent;
|
_events.AddEventDelegate(EventCallbacks.InverseText, InverseTextEvent);
|
||||||
_leaveVBlankEvent = LeaveVBlankEvent;
|
_events.AddEventDelegate(EventCallbacks.LeaveVBlank, LeaveVBlankEvent);
|
||||||
_resetVSyncEvent = ResetVSyncEvent;
|
_events.AddEventDelegate(EventCallbacks.ResetVsync, ResetVSyncEvent);
|
||||||
|
|
||||||
_flushRowMode = new Action<int>[]
|
_flushRowMode = new Action<int>[]
|
||||||
{
|
{
|
||||||
|
@ -73,22 +89,43 @@ namespace Jellyfish.Virtu
|
||||||
IsMonochrome = false;
|
IsMonochrome = false;
|
||||||
ScannerOptions = ScannerOptions.None;
|
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(_cyclesPerVBlankPreset, EventCallbacks.LeaveVBlank); // align flush events with scanner; assumes vcount preset at start of frame [3-15, 3-16]
|
||||||
_events.AddEvent(_cyclesPerVSync, _resetVSyncEvent);
|
_events.AddEvent(_cyclesPerVSync, EventCallbacks.ResetVsync);
|
||||||
_events.AddEvent(_cyclesPerFlash, _inverseTextEvent);
|
_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
|
// ReSharper disable once UnusedMember.Global
|
||||||
public int[] GetVideoBuffer() => Framebuffer;
|
public int[] GetVideoBuffer() => _framebuffer;
|
||||||
|
|
||||||
[OnDeserialized]
|
|
||||||
private void OnDeserialized(StreamingContext context)
|
|
||||||
{
|
|
||||||
// the VideoService forgets all of its information on LoadState
|
|
||||||
DirtyScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
|
@ -149,7 +186,7 @@ namespace Jellyfish.Virtu
|
||||||
public int ReadFloatingBus() // [5-40]
|
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]
|
// 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 hClock = cycles % CyclesPerHSync;
|
||||||
int hCount = (hClock != 0) ? HCountPreset + hClock - 1 : 0;
|
int hCount = (hClock != 0) ? HCountPreset + hClock - 1 : 0;
|
||||||
int vLine = cycles / CyclesPerHSync;
|
int vLine = cycles / CyclesPerHSync;
|
||||||
|
@ -918,18 +955,18 @@ namespace Jellyfish.Virtu
|
||||||
|
|
||||||
private void FlushRowEvent()
|
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
|
_flushRowMode[_memory.VideoMode](y - CellHeight); // in arrears
|
||||||
|
|
||||||
if (y < Height)
|
if (y < Height)
|
||||||
{
|
{
|
||||||
_events.AddEvent(CyclesPerFlush, _flushRowEvent);
|
_events.AddEvent(CyclesPerFlush, EventCallbacks.FlushRow);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IsVBlank = true;
|
_isVBlank = true;
|
||||||
_events.AddEvent(_cyclesPerVBlank, _leaveVBlankEvent);
|
_events.AddEvent(_cyclesPerVBlank, EventCallbacks.LeaveVBlank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -946,18 +983,18 @@ namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
_isTextInversed = !_isTextInversed;
|
_isTextInversed = !_isTextInversed;
|
||||||
DirtyScreenText();
|
DirtyScreenText();
|
||||||
_events.AddEvent(_cyclesPerFlash, _inverseTextEvent);
|
_events.AddEvent(_cyclesPerFlash, EventCallbacks.InverseText);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LeaveVBlankEvent()
|
private void LeaveVBlankEvent()
|
||||||
{
|
{
|
||||||
IsVBlank = false;
|
_isVBlank = false;
|
||||||
_events.AddEvent(CyclesPerFlush, _flushRowEvent);
|
_events.AddEvent(CyclesPerFlush, EventCallbacks.FlushRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetVSyncEvent()
|
private void ResetVSyncEvent()
|
||||||
{
|
{
|
||||||
_events.AddEvent(_cyclesPerVSync, _resetVSyncEvent);
|
_events.AddEvent(_cyclesPerVSync, EventCallbacks.ResetVsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetPalette()
|
private void SetPalette()
|
||||||
|
@ -1002,13 +1039,10 @@ namespace Jellyfish.Virtu
|
||||||
DirtyScreen();
|
DirtyScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local
|
|
||||||
private int[] Framebuffer { get; set; } = new int[560 * 384];
|
|
||||||
|
|
||||||
private void SetPixel(int x, int y, int color)
|
private void SetPixel(int x, int y, int color)
|
||||||
{
|
{
|
||||||
int i = 560 * (2 * y) + x;
|
int i = 560 * (2 * y) + x;
|
||||||
Framebuffer[i] = Framebuffer[i + 560] = _colorPalette[color];
|
_framebuffer[i] = _framebuffer[i + 560] = _colorPalette[color];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetScanner()
|
private void SetScanner()
|
||||||
|
@ -1030,53 +1064,36 @@ namespace Jellyfish.Virtu
|
||||||
_cyclesPerFlash = VSyncsPerFlash * _cyclesPerVSync;
|
_cyclesPerFlash = VSyncsPerFlash * _cyclesPerVSync;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
public bool IsMonochrome
|
||||||
public bool IsMonochrome { get => _isMonochrome; set { _isMonochrome = value; DirtyScreen(); } }
|
{
|
||||||
|
get => _isMonochrome;
|
||||||
|
set { _isMonochrome = value; DirtyScreen(); }
|
||||||
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
internal ScannerOptions ScannerOptions
|
||||||
internal ScannerOptions ScannerOptions { get => _scannerOptions; set { _scannerOptions = value; SetScanner(); } }
|
{
|
||||||
|
get => _scannerOptions;
|
||||||
|
set { _scannerOptions = value; SetScanner(); }
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsVBlank { get; private set; }
|
public bool IsVBlank => _isVBlank;
|
||||||
|
|
||||||
private Action _flushRowEvent;
|
private readonly int _colorBlack;
|
||||||
private Action _inverseTextEvent;
|
private readonly int _colorDarkBlue;
|
||||||
private Action _leaveVBlankEvent;
|
private readonly int _colorDarkGreen;
|
||||||
private Action _resetVSyncEvent;
|
private readonly int _colorMediumBlue;
|
||||||
|
private readonly int _colorBrown;
|
||||||
private int _colorBlack;
|
private readonly int _colorLightGrey;
|
||||||
private int _colorDarkBlue;
|
private readonly int _colorGreen;
|
||||||
private int _colorDarkGreen;
|
private readonly int _colorAquamarine;
|
||||||
private int _colorMediumBlue;
|
private readonly int _colorDeepRed;
|
||||||
private int _colorBrown;
|
private readonly int _colorPurple;
|
||||||
private int _colorLightGrey;
|
private readonly int _colorDarkGrey;
|
||||||
private int _colorGreen;
|
private readonly int _colorLightBlue;
|
||||||
private int _colorAquamarine;
|
private readonly int _colorOrange;
|
||||||
private int _colorDeepRed;
|
private readonly int _colorPink;
|
||||||
private int _colorPurple;
|
private readonly int _colorYellow;
|
||||||
private int _colorDarkGrey;
|
private readonly int _colorWhite;
|
||||||
private int _colorLightBlue;
|
private readonly int _colorMonochrome;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,6 @@
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Newtonsoft.Json">
|
|
||||||
<HintPath>..\..\References\Newtonsoft.Json.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
@ -53,6 +50,7 @@
|
||||||
<Compile Include="DiskIIController.cs" />
|
<Compile Include="DiskIIController.cs" />
|
||||||
<Compile Include="DiskIIDrive.cs" />
|
<Compile Include="DiskIIDrive.cs" />
|
||||||
<Compile Include="DiskNib.cs" />
|
<Compile Include="DiskNib.cs" />
|
||||||
|
<Compile Include="IComponentSerializer.cs" />
|
||||||
<Compile Include="IGamePort.cs" />
|
<Compile Include="IGamePort.cs" />
|
||||||
<Compile Include="Keyboard.cs" />
|
<Compile Include="Keyboard.cs" />
|
||||||
<Compile Include="MachineEvents.cs" />
|
<Compile Include="MachineEvents.cs" />
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CheckNamespace/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CheckNamespace/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=0DF/@EntryIndexedValue">0DF</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AA/@EntryIndexedValue">AA</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AA/@EntryIndexedValue">AA</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AB/@EntryIndexedValue">AB</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AB/@EntryIndexedValue">AB</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AC/@EntryIndexedValue">AC</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AC/@EntryIndexedValue">AC</s:String>
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue