diff --git a/Virtu/Disk525.cs b/Virtu/Disk525.cs index 43fb1f0..a688295 100644 --- a/Virtu/Disk525.cs +++ b/Virtu/Disk525.cs @@ -9,7 +9,7 @@ protected Disk525(string name, byte[] data, bool isWriteProtected) { Name = name; Data = data; - IsWriteProtected = true; // TODO use isWriteProtected + IsWriteProtected = isWriteProtected; } public static Disk525 CreateDisk(string name, byte[] data, bool isWriteProtected) diff --git a/Virtu/DiskDsk.cs b/Virtu/DiskDsk.cs index c2de580..d124536 100644 --- a/Virtu/DiskDsk.cs +++ b/Virtu/DiskDsk.cs @@ -49,8 +49,124 @@ public override void ReadTrack(int number, int fraction, byte[] buffer) public override void WriteTrack(int number, int fraction, byte[] buffer) { - // TODO - throw new NotImplementedException(); + if (IsWriteProtected) + return; + + int track = number / 2; + + _trackBuffer = buffer; + _trackOffset = 0; + int sectorsDone = 0; + + for (int sector = 0; sector < SectorCount; sector++) + { + if (!Read3Nibbles(0xD5, 0xAA, 0x96, 0x304)) + break; // no address prologue + + int readVolume = ReadNibble44(); + + int readTrack = ReadNibble44(); + if (readTrack != track) + break; // bad track number + + int readSector = ReadNibble44(); + if (readSector > SectorCount) + break; // bad sector number + if ((sectorsDone & (0x1 << readSector)) != 0) + break; // already done this sector + + if (ReadNibble44() != (Volume ^ readTrack ^ readSector)) + break; // bad address checksum + + if ((ReadNibble() != 0xDE) || (ReadNibble() != 0xAA)) + break; // bad address epilogue + + if (!Read3Nibbles(0xD5, 0xAA, 0xAD, 0x20)) + break; // no data prologue + + if (ReadDataNibbles((track * SectorCount + DosOrderToLogicalSector[sector]) * SectorSize) != 0) + break; // bad data checksum + + if ((ReadNibble() != 0xDE) || (ReadNibble() != 0xAA)) + break; // bad data epilogue + + sectorsDone |= 0x1 << sector; + } + + if (sectorsDone != 0xFFFF) + throw new Exception("disk error"); // TODO: not sure what we want here + } + + private byte ReadNibble() + { + byte data = _trackBuffer[_trackOffset]; + if (_trackOffset++ == TrackSize) + { + _trackOffset = 0; + } + return data; + } + + private bool Read3Nibbles(byte data1, byte data2, byte data3, int maxReads) + { + bool result = false; + byte nibble; + while (--maxReads > 0) + { + if ((nibble = ReadNibble()) != data1) + continue; + + if ((nibble = ReadNibble()) != data2) + continue; + + if ((nibble = ReadNibble()) != data3) + continue; + + result = true; + break; + } + return result; + } + + private int ReadNibble44() + { + return (((ReadNibble() << 1) | 0x1) & ReadNibble()); + } + + private byte ReadDataNibbles(int sectorOffset) + { + byte a, x, y; + + y = SecondaryBufferLength; + a = 0; + do // fill and de-nibblize secondary buffer + { + a = _secondaryBuffer[--y] = (byte)(a ^ NibbleToByte[ReadNibble()]); + } + while (y > 0); + + do // fill and de-nibblize secondary buffer + { + a = _primaryBuffer[y++] = (byte)(a ^ NibbleToByte[ReadNibble()]); + } + while (y != 0); + + byte checksum = NibbleToByte[ReadNibble()]; // should be 0 + + x = y = 0; + do // decode data + { + if (x == 0) + { + x = SecondaryBufferLength; + } + a = (byte)((_primaryBuffer[y] << 2) | SwapBits[_secondaryBuffer[--x] & 0x03]); + _secondaryBuffer[x] >>= 2; + Data[sectorOffset + y] = a; + } + while (++y != 0); + + return checksum; } private void WriteNibble(int data) @@ -134,5 +250,29 @@ private void WriteDataNibbles(int sectorOffset) 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; + + private static readonly byte[] NibbleToByte = new byte[] + { + // padding for offset (not used) + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, + + // nibble translate table + 0x00, 0x01, 0x98, 0x99, 0x02, 0x03, 0x9C, 0x04, 0x05, 0x06, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x07, 0x08, 0xA8, 0xA9, 0xAA, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0xB0, 0xB1, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xB8, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0x1B, 0xCC, 0x1C, 0x1D, 0x1E, + 0xD0, 0xD1, 0xD2, 0x1F, 0xD4, 0xD5, 0x20, 0x21, 0xD8, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0x29, 0x2A, 0x2B, 0xE8, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, + 0xF0, 0xF1, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0xF8, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F + }; } } diff --git a/Virtu/DiskII.cs b/Virtu/DiskII.cs index 57ac150..9d3fcb3 100644 --- a/Virtu/DiskII.cs +++ b/Virtu/DiskII.cs @@ -75,9 +75,16 @@ public int Read(int address) case 0xC: _loadMode = false; - if (_motorOn && !_writeMode) + if (_motorOn) { - return _latch = _drives[_driveNumber].Read(); + if (!_writeMode) + { + return _latch = _drives[_driveNumber].Read(); + } + else + { + WriteLatch(); + } } break; @@ -146,11 +153,9 @@ public void Write(int address, int data) case 0xC: _loadMode = false; - - // write protect is forced if phase 1 is on [F9.7] - if ((_phaseStates & Phase1On) != 0) + if (_writeMode) { - _drives[_driveNumber].Write(_latch); + WriteLatch(); } break; @@ -177,6 +182,15 @@ public void Write(int address, int data) } } + private void WriteLatch() + { + // write protect is forced if phase 1 is on [F9.7] + if ((_phaseStates & Phase1On) == 0) + { + _drives[_driveNumber].Write(_latch); + } + } + private void Flush() { _drives[_driveNumber].FlushTrack(); diff --git a/Virtu/DiskNib.cs b/Virtu/DiskNib.cs index 2bef503..1466abc 100644 --- a/Virtu/DiskNib.cs +++ b/Virtu/DiskNib.cs @@ -16,8 +16,7 @@ public override void ReadTrack(int number, int fraction, byte[] buffer) public override void WriteTrack(int number, int fraction, byte[] buffer) { - // TODO - throw new NotImplementedException(); + Buffer.BlockCopy(buffer, 0, Data, (number / 2) * TrackSize, TrackSize); } } } diff --git a/Virtu/Drive525.cs b/Virtu/Drive525.cs index ddca5be..d07142a 100644 --- a/Virtu/Drive525.cs +++ b/Virtu/Drive525.cs @@ -13,15 +13,6 @@ public Drive525() DriveArmStepDelta[3] = new int[] { 0, 1, 0, 1, -1, 0, -1, 0, 0, 1, 0, 1, -1, 0, -1, 0 }; // phase 3 } - public void FlushTrack() - { - if (_trackChanged) - { - // TODO - _trackChanged = false; - } - } - public void InsertDisk(string fileName, bool isWriteProtected) { FlushTrack(); @@ -42,6 +33,7 @@ public void ApplyPhaseChange(int phaseState) int newTrackNumber = MathHelpers.Clamp(_trackNumber + delta, 0, TrackNumberMax); if (newTrackNumber != _trackNumber) { + FlushTrack(); _trackNumber = newTrackNumber; _trackOffset = 0; _trackLoaded = false; @@ -69,6 +61,7 @@ public void Write(int data) { if (LoadTrack()) { + _trackChanged = true; _trackData[_trackOffset++] = (byte)data; if (_trackOffset >= Disk525.TrackSize) { @@ -88,6 +81,15 @@ private bool LoadTrack() return _trackLoaded; } + public void FlushTrack() + { + if (_trackChanged) + { + _disk.WriteTrack(_trackNumber, 0, _trackData); + _trackChanged = false; + } + } + public bool IsWriteProtected { get { return _disk.IsWriteProtected; } } private const int TrackNumberMax = 0x44;