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 callback) { FlushTrack(); _disk.DeltaUpdate(callback); } } }