131 lines
3.2 KiB
C#
131 lines
3.2 KiB
C#
using System;
|
|
|
|
namespace Jellyfish.Virtu
|
|
{
|
|
public sealed class DiskIIDrive
|
|
{
|
|
private readonly IDiskIIController _diskController;
|
|
private readonly int[][] _driveArmStepDelta = new int[PhaseCount][];
|
|
|
|
private bool _trackLoaded;
|
|
private bool _trackChanged;
|
|
private int _trackNumber;
|
|
private int _trackOffset;
|
|
|
|
private byte[] _trackData = new byte[Disk525.TrackSize];
|
|
private Disk525 _disk;
|
|
|
|
public DiskIIDrive(IDiskIIController diskController)
|
|
{
|
|
_diskController = diskController;
|
|
_driveArmStepDelta[0] = new[] { 0, 0, 1, 1, 0, 0, 1, 1, -1, -1, 0, 0, -1, -1, 0, 0 }; // phase 0
|
|
_driveArmStepDelta[1] = new[] { 0, -1, 0, -1, 1, 0, 1, 0, 0, -1, 0, -1, 1, 0, 1, 0 }; // phase 1
|
|
_driveArmStepDelta[2] = new[] { 0, 0, -1, -1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 1, 0, 0 }; // phase 2
|
|
_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);
|
|
_disk?.Sync(ser);
|
|
}
|
|
|
|
// ReSharper disable once UnusedMember.Global
|
|
public void InsertDisk(string name, byte[] data, bool isWriteProtected)
|
|
{
|
|
FlushTrack();
|
|
_disk = Disk525.CreateDisk(name, data, isWriteProtected);
|
|
_trackLoaded = false;
|
|
}
|
|
|
|
private static int Clamp(int value, int min, int max)
|
|
{
|
|
return value < min ? min : value > max ? max : value;
|
|
}
|
|
|
|
internal void ApplyPhaseChange(int phaseState)
|
|
{
|
|
// step the drive head according to stepper magnet changes
|
|
int delta = _driveArmStepDelta[_trackNumber & 0x3][phaseState];
|
|
if (delta != 0)
|
|
{
|
|
int newTrackNumber = Clamp(_trackNumber + delta, 0, TrackNumberMax);
|
|
if (newTrackNumber != _trackNumber)
|
|
{
|
|
FlushTrack();
|
|
_trackNumber = newTrackNumber;
|
|
_trackOffset = 0;
|
|
_trackLoaded = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal int Read()
|
|
{
|
|
if (LoadTrack())
|
|
{
|
|
int data = _trackData[_trackOffset++];
|
|
if (_trackOffset >= Disk525.TrackSize)
|
|
{
|
|
_trackOffset = 0;
|
|
}
|
|
|
|
_diskController.DriveLight = true;
|
|
return data;
|
|
}
|
|
|
|
return 0x80;
|
|
}
|
|
|
|
internal void Write(int data)
|
|
{
|
|
if (LoadTrack())
|
|
{
|
|
_trackChanged = true;
|
|
_trackData[_trackOffset++] = (byte)data;
|
|
if (_trackOffset >= Disk525.TrackSize)
|
|
{
|
|
_trackOffset = 0;
|
|
}
|
|
|
|
_diskController.DriveLight = true;
|
|
}
|
|
}
|
|
|
|
private bool LoadTrack()
|
|
{
|
|
if (!_trackLoaded && (_disk != null))
|
|
{
|
|
_disk.ReadTrack(_trackNumber, 0, _trackData);
|
|
_trackLoaded = true;
|
|
}
|
|
|
|
return _trackLoaded;
|
|
}
|
|
|
|
internal void FlushTrack()
|
|
{
|
|
if (_trackChanged)
|
|
{
|
|
_disk.WriteTrack(_trackNumber, 0, _trackData);
|
|
_trackChanged = false;
|
|
}
|
|
}
|
|
|
|
public bool IsWriteProtected => _disk?.IsWriteProtected ?? false;
|
|
|
|
private const int TrackNumberMax = 0x44;
|
|
private const int PhaseCount = 4;
|
|
|
|
public void DeltaUpdate(Action<byte[], byte[]> callback)
|
|
{
|
|
FlushTrack();
|
|
_disk.DeltaUpdate(callback);
|
|
}
|
|
}
|
|
}
|