[C64] Fix up implementation of SaveRam

This commit is contained in:
saxxonpike 2025-01-06 19:18:24 +10:00 committed by YoshiRulz
parent 6f7097ee07
commit 0adf2f97d7
5 changed files with 110 additions and 150 deletions

View File

@ -10,7 +10,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
public const int FluxBitsPerTrack = 16000000 / 5;
public const int FluxEntriesPerTrack = FluxBitsPerTrack / FluxBitsPerEntry;
private readonly DiskTrack[] _tracks;
private bool[] _usedTracks;
public bool Valid;
public bool WriteProtected;
@ -21,7 +20,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
WriteProtected = false;
_tracks = new DiskTrack[trackCount];
_usedTracks = new bool[trackCount];
FillMissingTracks();
Valid = true;
}
@ -37,7 +35,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
WriteProtected = true;
_tracks = new DiskTrack[trackCapacity];
_usedTracks = new bool[trackCapacity];
for (var i = 0; i < trackData.Count; i++)
{
var track = new DiskTrack();
@ -67,24 +64,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
}
}
/// <summary>
/// Generic update of the deltas stored in Drive1541's ISaveRam implementation.
/// deltaUpdateCallback will be called for each track which has been possibly dirtied
/// </summary>
/// <param name="deltaUpdateCallback">callback</param>
public void DeltaUpdate(Action<int, DiskTrack> deltaUpdateCallback)
{
for (var i = 0; i < _tracks.Length; i++)
{
if (_usedTracks[i])
{
deltaUpdateCallback(i, _tracks[i]);
}
}
}
public DiskTrack GetTrack(int trackNumber)
=> _tracks[trackNumber];
public IReadOnlyList<DiskTrack> Tracks
=> _tracks;
public void SyncState(Serializer ser)
{

View File

@ -1,7 +1,5 @@
using System.Buffers;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media;
/// <summary>
@ -23,7 +21,7 @@ public sealed class DiskTrack
/// <summary>
/// Number of bits contained in a single value of the Bits array.
/// </summary>
private const int FluxBitsPerEntry = BytesPerEntry * 8;
public const int FluxBitsPerEntry = BytesPerEntry * 8;
/// <summary>
/// The number of flux transition bits stored for each track.
@ -35,42 +33,19 @@ public sealed class DiskTrack
/// </summary>
private const int FluxEntriesPerTrack = FluxBitsPerTrack / FluxBitsPerEntry;
/// <summary>
/// The number of bytes contained in the cached delta, for use with save states.
/// </summary>
private const int DeltaBytesPerTrack = FluxEntriesPerTrack * BytesPerEntry + 4;
private int[] _bits = new int[FluxEntriesPerTrack];
private int[] _original = new int[FluxEntriesPerTrack];
private byte[] _delta = new byte[DeltaBytesPerTrack];
private bool _dirty = true;
private bool _modified = false;
/// <summary>
/// Current state of the disk, which may be changed from the original media.
/// </summary>
public ReadOnlySpan<int> Bits => _bits;
public Span<int> Bits => _bits;
/// <summary>
/// Fixed state of the original media, from which deltas will be calculated.
/// </summary>
public ReadOnlySpan<int> Original => _original;
/// <summary>
/// The compressed difference between
/// </summary>
public byte[] Delta => _delta;
/// <summary>
/// If true, the delta needs to be recalculated.
/// </summary>
public bool IsDirty => _dirty;
/// <summary>
/// If true, the track data has been modified.
/// </summary>
public bool IsModified => _modified;
/// <summary>
/// Create a clone of the DiskTrack.
/// </summary>
@ -86,42 +61,13 @@ public sealed class DiskTrack
}
/// <summary>
/// Prepare the <see cref="IsModified"/> property.
/// Check to see if the original bits and current bits are equivalent.
/// </summary>
/// <returns>
/// The new value of <see cref="IsModified"/>.
/// True only if the content differs.
/// </returns>
private bool CheckModified()
=> _modified = !_original.AsSpan().SequenceEqual(_bits);
/// <summary>
/// Apply a compressed delta over the original media.
/// </summary>
/// <param name="delta">
/// Compressed delta data.
/// </param>
public void ApplyDelta(ReadOnlySpan<byte> delta)
{
DeltaSerializer.ApplyDelta<int>(_original, _bits, delta);
_delta = delta.ToArray();
_dirty = false;
CheckModified();
}
/// <summary>
/// Updates the delta for this track.
/// </summary>
/// <returns>
/// True if the delta has updated, false otherwise.
/// </returns>
public bool UpdateDelta()
{
if (!_dirty) return false;
_delta = DeltaSerializer.GetDelta<int>(_original, _bits).ToArray();
_dirty = false;
return true;
}
public bool IsModified()
=> !_original.AsSpan().SequenceEqual(_bits);
/// <summary>
/// Resets this track to the state of the original media.
@ -129,29 +75,28 @@ public sealed class DiskTrack
public void Reset()
{
_original.CopyTo(_bits.AsSpan());
_delta = Array.Empty<byte>();
_dirty = false;
}
/// <summary>
/// Synchronize state.
/// Write an entry to <see cref="Bits"/>.
/// </summary>
/// <param name="ser">
/// Serializer with which to synchronize.
/// <param name="index">
/// Index of the entry to write.
/// </param>
public void SyncState(Serializer ser, string deltaId)
{
ser.Sync(deltaId, ref _delta, useNull: true);
}
public void Write(int index, int bits)
/// <param name="bits">
/// The new content of the entry.
/// </param>
/// <returns>
/// True only if data in <see cref="Bits"/> has been altered.
/// </returns>
public bool Write(int index, int bits)
{
// We only need to update delta if the bits actually changed.
if (_bits[index] == bits) return;
if (_bits[index] == bits) return false;
_bits[index] = bits;
_dirty = true;
return true;
}
public void ReadFromGCR(int density, ReadOnlySpan<byte> bytes, int fluxBitOffset)
@ -213,5 +158,7 @@ public sealed class DiskTrack
break;
}
}
_bits.CopyTo(_original.AsSpan());
}
}

View File

@ -38,8 +38,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
private void ExecuteFlux()
{
var track = _disk.GetTrack(_trackNumber);
var bits = track.Bits;
var track = _disk?.Tracks[_trackNumber];
var bits = track == null ? Span<int>.Empty : track.Bits;
// This actually executes the main 16mhz clock
while (_clocks > 0)
@ -58,8 +58,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
if (_diskBitsLeft <= 0)
{
if (_diskWriteEnabled)
track.Write(_diskByteOffset, _diskOutputBits);
if (_diskWriteEnabled && track.Write(_diskByteOffset, _diskOutputBits))
{
_dirtyDiskTracks[_getCurrentDiskNumber()][_trackNumber] = true;
}
_diskByteOffset++;
@ -70,7 +72,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
_diskBits = bits[_diskByteOffset];
_diskOutputBits = 0;
_diskBitsLeft = Disk.FluxBitsPerEntry;
_diskBitsLeft = DiskTrack.FluxBitsPerEntry;
}
}
_diskOutputBits >>= 1;
@ -200,8 +202,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
_diskDensityCounter++;
_diskCycle = (_diskCycle + 1) & 0xF;
}
if (_diskWriteEnabled && track.UpdateDelta()) SaveDelta(_trackNumber, track.Delta);
}
}
}

View File

@ -13,29 +13,34 @@ public sealed partial class Drive1541 : ISaveRam
// we keep it here for all disks as we need to remember it when swapping disks around
// _usedDiskTracks.Length also doubles as a way to remember the disk count
private bool[][] _usedDiskTracks;
private bool[][] _dirtyDiskTracks;
private readonly Func<int> _getCurrentDiskNumber;
private int _diskCount;
public void InitSaveRam(int diskCount)
{
_diskCount = diskCount;
_usedDiskTracks = new bool[diskCount][];
_dirtyDiskTracks = new bool[diskCount][];
_diskDeltas = new byte[diskCount][][];
for (var i = 0; i < diskCount; i++)
for (var diskNumber = 0; diskNumber < diskCount; diskNumber++)
{
_usedDiskTracks[i] = new bool[84];
_diskDeltas[i] = new byte[84][];
for (var j = 0; j < 84; j++)
_usedDiskTracks[diskNumber] = new bool[84];
_diskDeltas[diskNumber] = new byte[84][];
_dirtyDiskTracks[diskNumber] = new bool[84];
for (var trackNumber = 0; trackNumber < 84; trackNumber++)
{
_diskDeltas[i][j] = Array.Empty<byte>();
_diskDeltas[diskNumber][trackNumber] = Array.Empty<byte>();
}
}
SaveRamModified = false;
}
public bool SaveRamModified { get; private set; } = false;
public byte[] CloneSaveRam()
{
SaveDeltas(); // update the current deltas
using var ms = new MemoryStream();
using var bw = new BinaryWriter(ms);
bw.Write(_usedDiskTracks.Length);
@ -49,6 +54,7 @@ public sealed partial class Drive1541 : ISaveRam
}
}
SaveRamModified = false;
return ms.ToArray();
}
@ -63,8 +69,6 @@ public sealed partial class Drive1541 : ISaveRam
throw new InvalidOperationException("Disk count mismatch!");
}
ResetDeltas();
for (var i = 0; i < _usedDiskTracks.Length; i++)
{
_usedDiskTracks[i] = br.ReadByteBuffer(returnNull: false)!.ToBoolBuffer();
@ -74,50 +78,82 @@ public sealed partial class Drive1541 : ISaveRam
}
}
LoadDeltas(); // load up new deltas
_usedDiskTracks[_getCurrentDiskNumber()][_trackNumber] = true; // make sure this gets set to true now
LoadDeltas();
SaveRamModified = false;
}
/// <summary>
/// Clear all cached deltas.
/// </summary>
public void ResetDeltas()
{
for (var i = 0; i < _diskCount; i++)
{
for (var j = 0; j < 84; j++)
{
_diskDeltas[i][j] = Array.Empty<byte>();
_usedDiskTracks[i][j] = false;
}
}
}
/// <summary>
/// Calculate and cache the deltas for each track on the current disk.
/// </summary>
public void SaveDeltas()
{
_disk?.DeltaUpdate((tracknum, track) =>
if (_disk == null) return;
var diskNumber = _getCurrentDiskNumber();
var deltas = _diskDeltas[diskNumber];
for (var trackNumber = 0; trackNumber < 84; trackNumber++)
{
SaveDelta(tracknum, track.Delta);
});
var track = _disk.Tracks[trackNumber];
var isModified = track.IsModified();
_usedDiskTracks[diskNumber][trackNumber] = isModified;
if (_dirtyDiskTracks[diskNumber][trackNumber])
{
SaveRamModified = true;
deltas[trackNumber] = isModified
? DeltaSerializer.GetDelta(track.Original, track.Bits).ToArray()
: Array.Empty<byte>();
_dirtyDiskTracks[diskNumber][trackNumber] = false;
}
}
}
/// <summary>
/// Apply new deltas for each track on the current disk.
/// </summary>
public void LoadDeltas()
{
_disk?.DeltaUpdate((tracknum, track) =>
if (_disk == null) return;
var diskNumber = _getCurrentDiskNumber();
var deltas = _diskDeltas[diskNumber];
for (var trackNumber = 0; trackNumber < 84; trackNumber++)
{
LoadDelta(tracknum, track.Delta);
});
}
var track = _disk.Tracks[trackNumber];
var delta = deltas[trackNumber];
private void ResetDeltas()
{
_disk?.DeltaUpdate(static (_, track) =>
{
track.Reset();
});
}
if (delta == null || delta.Length == 0)
{
track.Reset();
}
else
{
DeltaSerializer.ApplyDelta(track.Original, track.Bits, delta);
SaveRamModified = true;
}
private void SaveDelta(int trackNumber, byte[] delta)
{
SaveRamModified = true;
_diskDeltas[_getCurrentDiskNumber()][trackNumber] = delta;
}
private void LoadDelta(int trackNumber, byte[] delta)
{
_diskDeltas[_getCurrentDiskNumber()][trackNumber] = delta;
_disk.GetTrack(trackNumber).ApplyDelta(delta);
}
private void ResetDelta(int trackNumber)
{
SaveRamModified = true;
_diskDeltas[_getCurrentDiskNumber()][trackNumber] = Array.Empty<byte>();
_disk.GetTrack(trackNumber).Reset();
_usedDiskTracks[diskNumber][trackNumber] = track.IsModified();
_dirtyDiskTracks[diskNumber][trackNumber] = false;
}
}
}

View File

@ -195,11 +195,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
Via0.HardReset();
Via1.HardReset();
_trackNumber = 34;
for (var i = 0; i < _ram.Length; i++)
{
_ram[i] = 0x00;
}
_ram.AsSpan().Fill(0);
_diskDensity = 0;
_diskFluxReversalDetected = false;
_diskByteOffset = 0;
@ -232,8 +228,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial
{
if (_disk != null)
{
var track = _disk.GetTrack(_trackNumber);
_diskBits = track.Bits[_diskByteOffset] >> (Disk.FluxBitsPerEntry - _diskBitsLeft);
var track = _disk.Tracks[_trackNumber];
_diskBits = track.Bits[_diskByteOffset] >> (DiskTrack.FluxBitsPerEntry - _diskBitsLeft);
_diskWriteProtected = _disk.WriteProtected;
}
else