BizHawk/ExternalCoreProjects/Virtu/DiskDsk.cs

334 lines
8.7 KiB
C#

using System;
namespace Jellyfish.Virtu
{
internal enum SectorSkew { None = 0, Dos, ProDos };
internal sealed class DiskDsk : Disk525
{
// ReSharper disable once UnusedMember.Global
public DiskDsk() { }
public DiskDsk(byte[] data, bool isWriteProtected, SectorSkew sectorSkew)
: base(data, isWriteProtected)
{
_sectorSkew = SectorSkewMode[(int)sectorSkew];
}
public override void ReadTrack(int number, int fraction, byte[] buffer)
{
int track = number / 2;
_trackBuffer = buffer;
_trackOffset = 0;
WriteNibble(0xFF, 48); // gap 0
for (int sector = 0; sector < SectorCount; sector++)
{
WriteNibble(0xD5); // address prologue
WriteNibble(0xAA);
WriteNibble(0x96);
WriteNibble44(Volume);
WriteNibble44(track);
WriteNibble44(sector);
WriteNibble44(Volume ^ track ^ sector);
WriteNibble(0xDE); // address epilogue
WriteNibble(0xAA);
WriteNibble(0xEB);
WriteNibble(0xFF, 8);
WriteNibble(0xD5); // data prologue
WriteNibble(0xAA);
WriteNibble(0xAD);
WriteDataNibbles((track * SectorCount + _sectorSkew[sector]) * SectorSize);
WriteNibble(0xDE); // data epilogue
WriteNibble(0xAA);
WriteNibble(0xEB);
WriteNibble(0xFF, 16);
}
}
public override void WriteTrack(int number, int fraction, byte[] buffer)
{
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 + _sectorSkew[sector]) * SectorSize))
{
break; // bad data checksum
}
if (ReadNibble() != 0xDE || ReadNibble() != 0xAA)
{
break; // bad data epilogue
}
sectorsDone |= 0x1 << sector;
}
if (sectorsDone != 0xFFFF)
{
throw new InvalidOperationException("disk error"); // TODO: we should alert the user and "dump" a NIB
}
}
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;
while (--maxReads > 0)
{
if (ReadNibble() != data1)
{
continue;
}
if (ReadNibble() != data2)
{
continue;
}
if (ReadNibble() != data3)
{
continue;
}
result = true;
break;
}
return result;
}
private int ReadNibble44()
{
return (((ReadNibble() << 1) | 0x1) & ReadNibble());
}
private byte ReadTranslatedNibble()
{
byte data = NibbleToByte[ReadNibble()];
return data;
}
private bool ReadDataNibbles(int sectorOffset)
{
byte y = SecondaryBufferLength;
byte a = 0;
do // fill and de-nibblize secondary buffer
{
a = _secondaryBuffer[--y] = (byte)(a ^ ReadTranslatedNibble());
}
while (y > 0);
do // fill and de-nibblize secondary buffer
{
a = _primaryBuffer[y++] = (byte)(a ^ ReadTranslatedNibble());
}
while (y != 0);
int checksum = a ^ ReadTranslatedNibble(); // should be 0
var 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 == 0);
}
private void WriteNibble(int data)
{
_trackBuffer[_trackOffset++] = (byte)data;
}
private void WriteNibble(int data, int count)
{
while (count-- > 0)
{
WriteNibble(data);
}
}
private void WriteNibble44(int data)
{
WriteNibble((data >> 1) | 0xAA);
WriteNibble(data | 0xAA);
}
private void WriteDataNibbles(int sectorOffset)
{
byte a, x;
for (x = 0; x < SecondaryBufferLength; x++)
{
_secondaryBuffer[x] = 0; // zero secondary buffer
}
byte y = 2;
do // fill buffers
{
x = 0;
do
{
a = Data[sectorOffset + --y];
_secondaryBuffer[x] = (byte)((_secondaryBuffer[x] << 2) | SwapBits[a & 0x03]); // b1,b0 -> secondary buffer
_primaryBuffer[y] = (byte)(a >> 2); // b7-b2 -> primary buffer
}
while (++x < SecondaryBufferLength);
}
while (y != 0);
y = SecondaryBufferLength;
do // write secondary buffer
{
WriteNibble(ByteToNibble[_secondaryBuffer[y] ^ _secondaryBuffer[y - 1]]);
}
while (--y != 0);
a = _secondaryBuffer[0];
do // write primary buffer
{
WriteNibble(ByteToNibble[a ^ _primaryBuffer[y]]);
a = _primaryBuffer[y];
}
while (++y != 0);
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 int[] SectorSkewNone =
{
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF
};
private static readonly int[] SectorSkewDos =
{
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF
};
private static readonly int[] SectorSkewProDos =
{
0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF
};
private static readonly int[][] SectorSkewMode =
{
SectorSkewNone, SectorSkewDos, SectorSkewProDos
};
private static readonly byte[] ByteToNibble =
{
0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
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 =
{
// 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
};
}
}