mirror of
https://github.com/digital-jellyfish/Virtu.git
synced 2024-05-31 15:41:33 +00:00
Added support for 'Dos Order' (.do) disk images.
Added support for 'ProDos Order' (.po) disk images.
This commit is contained in:
parent
7aacc26962
commit
c149a7260c
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using Jellyfish.Library;
|
using Jellyfish.Library;
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
namespace Jellyfish.Virtu
|
||||||
|
@ -20,22 +21,19 @@ public static Disk525 CreateDisk(string name, Stream stream, bool isWriteProtect
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("name");
|
throw new ArgumentNullException("name");
|
||||||
}
|
}
|
||||||
if (stream == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("stream");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.EndsWith(".dsk", StringComparison.OrdinalIgnoreCase))
|
if (name.EndsWith(".do", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
name.EndsWith(".dsk", StringComparison.OrdinalIgnoreCase)) // assumes dos sector skew
|
||||||
{
|
{
|
||||||
var data = new byte[TrackCount * SectorCount * SectorSize];
|
return new DiskDsk(name, stream, isWriteProtected, SectorSkew.Dos);
|
||||||
stream.ReadBlock(data);
|
|
||||||
return new DiskDsk(name, data, isWriteProtected);
|
|
||||||
}
|
}
|
||||||
else if (name.EndsWith(".nib", StringComparison.OrdinalIgnoreCase))
|
else if (name.EndsWith(".nib", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var data = new byte[TrackCount * TrackSize];
|
return new DiskNib(name, stream, isWriteProtected);
|
||||||
stream.ReadBlock(data);
|
}
|
||||||
return new DiskNib(name, data, isWriteProtected);
|
else if (name.EndsWith(".po", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return new DiskDsk(name, stream, isWriteProtected, SectorSkew.ProDos);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -50,17 +48,22 @@ public static Disk525 LoadState(BinaryReader reader, Version version)
|
||||||
}
|
}
|
||||||
|
|
||||||
string name = reader.ReadString();
|
string name = reader.ReadString();
|
||||||
bool isWriteProtected = reader.ReadBoolean();
|
|
||||||
var data = reader.ReadBytes(reader.ReadInt32());
|
var data = reader.ReadBytes(reader.ReadInt32());
|
||||||
|
bool isWriteProtected = reader.ReadBoolean();
|
||||||
|
|
||||||
if (name.EndsWith(".dsk", StringComparison.OrdinalIgnoreCase))
|
if (name.EndsWith(".do", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
name.EndsWith(".dsk", StringComparison.OrdinalIgnoreCase)) // assumes dos sector skew
|
||||||
{
|
{
|
||||||
return new DiskDsk(name, data, isWriteProtected);
|
return new DiskDsk(name, data, isWriteProtected, SectorSkew.Dos);
|
||||||
}
|
}
|
||||||
else if (name.EndsWith(".nib", StringComparison.OrdinalIgnoreCase))
|
else if (name.EndsWith(".nib", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return new DiskNib(name, data, isWriteProtected);
|
return new DiskNib(name, data, isWriteProtected);
|
||||||
}
|
}
|
||||||
|
else if (name.EndsWith(".po", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return new DiskDsk(name, data, isWriteProtected, SectorSkew.ProDos);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -73,19 +76,18 @@ public void SaveState(BinaryWriter writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(Name);
|
writer.Write(Name);
|
||||||
writer.Write(IsWriteProtected);
|
|
||||||
writer.Write(Data.Length);
|
writer.Write(Data.Length);
|
||||||
writer.Write(Data);
|
writer.Write(Data);
|
||||||
|
writer.Write(IsWriteProtected);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void ReadTrack(int number, int fraction, byte[] buffer);
|
public abstract void ReadTrack(int number, int fraction, byte[] buffer);
|
||||||
public abstract void WriteTrack(int number, int fraction, byte[] buffer);
|
public abstract void WriteTrack(int number, int fraction, byte[] buffer);
|
||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
public bool IsWriteProtected { get; private set; }
|
|
||||||
|
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
||||||
protected byte[] Data { get; private set; }
|
public byte[] Data { get; protected set; }
|
||||||
|
public bool IsWriteProtected { get; private set; }
|
||||||
|
|
||||||
public const int SectorCount = 16;
|
public const int SectorCount = 16;
|
||||||
public const int SectorSize = 0x100;
|
public const int SectorSize = 0x100;
|
||||||
|
|
|
@ -1,12 +1,29 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Jellyfish.Library;
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
public sealed class DiskDsk : Disk525
|
public enum SectorSkew { None = 0, Dos, ProDos };
|
||||||
|
|
||||||
|
public class DiskDsk : Disk525
|
||||||
{
|
{
|
||||||
public DiskDsk(string name, byte[] data, bool isWriteProtected) :
|
public DiskDsk(string name, byte[] data, bool isWriteProtected, SectorSkew sectorSkew) :
|
||||||
base(name, data, isWriteProtected)
|
base(name, data, isWriteProtected)
|
||||||
{
|
{
|
||||||
|
_sectorSkew = SectorSkewMode[(int)sectorSkew];
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiskDsk(string name, Stream stream, bool isWriteProtected, SectorSkew sectorSkew) :
|
||||||
|
base(name, new byte[TrackCount * SectorCount * SectorSize], isWriteProtected)
|
||||||
|
{
|
||||||
|
if (stream == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.ReadBlock(Data);
|
||||||
|
_sectorSkew = SectorSkewMode[(int)sectorSkew];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ReadTrack(int number, int fraction, byte[] buffer)
|
public override void ReadTrack(int number, int fraction, byte[] buffer)
|
||||||
|
@ -38,7 +55,7 @@ public override void ReadTrack(int number, int fraction, byte[] buffer)
|
||||||
WriteNibble(0xAA);
|
WriteNibble(0xAA);
|
||||||
WriteNibble(0xAD);
|
WriteNibble(0xAD);
|
||||||
|
|
||||||
WriteDataNibbles((track * SectorCount + DosOrderToLogicalSector[sector]) * SectorSize);
|
WriteDataNibbles((track * SectorCount + _sectorSkew[sector]) * SectorSize);
|
||||||
|
|
||||||
WriteNibble(0xDE); // data epilogue
|
WriteNibble(0xDE); // data epilogue
|
||||||
WriteNibble(0xAA);
|
WriteNibble(0xAA);
|
||||||
|
@ -84,7 +101,7 @@ public override void WriteTrack(int number, int fraction, byte[] buffer)
|
||||||
if (!Read3Nibbles(0xD5, 0xAA, 0xAD, 0x20))
|
if (!Read3Nibbles(0xD5, 0xAA, 0xAD, 0x20))
|
||||||
break; // no data prologue
|
break; // no data prologue
|
||||||
|
|
||||||
if (!ReadDataNibbles((track * SectorCount + DosOrderToLogicalSector[sector]) * SectorSize))
|
if (!ReadDataNibbles((track * SectorCount + _sectorSkew[sector]) * SectorSize))
|
||||||
break; // bad data checksum
|
break; // bad data checksum
|
||||||
|
|
||||||
if ((ReadNibble() != 0xDE) || (ReadNibble() != 0xAA))
|
if ((ReadNibble() != 0xDE) || (ReadNibble() != 0xAA))
|
||||||
|
@ -245,15 +262,33 @@ private void WriteDataNibbles(int sectorOffset)
|
||||||
private byte[] _primaryBuffer = new byte[0x100];
|
private byte[] _primaryBuffer = new byte[0x100];
|
||||||
private const int SecondaryBufferLength = 0x56;
|
private const int SecondaryBufferLength = 0x56;
|
||||||
private byte[] _secondaryBuffer = new byte[SecondaryBufferLength + 1];
|
private byte[] _secondaryBuffer = new byte[SecondaryBufferLength + 1];
|
||||||
|
private int[] _sectorSkew;
|
||||||
private const int Volume = 0xFE;
|
private const int Volume = 0xFE;
|
||||||
|
|
||||||
private static readonly byte[] SwapBits = { 0, 2, 1, 3 };
|
private static readonly byte[] SwapBits = { 0, 2, 1, 3 };
|
||||||
|
|
||||||
private static readonly int[] DosOrderToLogicalSector = new int[]
|
private static readonly int[] SectorSkewNone = new int[SectorCount]
|
||||||
|
{
|
||||||
|
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly int[] SectorSkewDos = new int[SectorCount]
|
||||||
{
|
{
|
||||||
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF
|
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static readonly int[] SectorSkewProDos = new int[SectorCount]
|
||||||
|
{
|
||||||
|
0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF
|
||||||
|
};
|
||||||
|
|
||||||
|
private const int SectorSkewCount = 3;
|
||||||
|
|
||||||
|
private static readonly int[][] SectorSkewMode = new int[SectorSkewCount][]
|
||||||
|
{
|
||||||
|
SectorSkewNone, SectorSkewDos, SectorSkewProDos
|
||||||
|
};
|
||||||
|
|
||||||
private static readonly byte[] ByteToNibble = new byte[]
|
private static readonly byte[] ByteToNibble = new byte[]
|
||||||
{
|
{
|
||||||
0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
|
0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Jellyfish.Library;
|
||||||
|
|
||||||
namespace Jellyfish.Virtu
|
namespace Jellyfish.Virtu
|
||||||
{
|
{
|
||||||
|
@ -9,6 +11,17 @@ public DiskNib(string name, byte[] data, bool isWriteProtected) :
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DiskNib(string name, Stream stream, bool isWriteProtected) :
|
||||||
|
base(name, new byte[TrackCount * TrackSize], isWriteProtected)
|
||||||
|
{
|
||||||
|
if (stream == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.ReadBlock(Data);
|
||||||
|
}
|
||||||
|
|
||||||
public override void ReadTrack(int number, int fraction, byte[] buffer)
|
public override void ReadTrack(int number, int fraction, byte[] buffer)
|
||||||
{
|
{
|
||||||
Buffer.BlockCopy(Data, (number / 2) * TrackSize, buffer, 0, TrackSize);
|
Buffer.BlockCopy(Data, (number / 2) * TrackSize, buffer, 0, TrackSize);
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Jellyfish.Virtu.Services;
|
using Jellyfish.Virtu.Services;
|
||||||
|
|
||||||
|
@ -140,7 +139,7 @@ private void LoadState()
|
||||||
{
|
{
|
||||||
loader(name, stream => Memory.LoadXex(stream));
|
loader(name, stream => Memory.LoadXex(stream));
|
||||||
}
|
}
|
||||||
else if (Regex.IsMatch(name, @"\.(dsk|nib)$", RegexOptions.IgnoreCase))
|
else
|
||||||
{
|
{
|
||||||
loader(name, stream => BootDiskII.BootDrive.InsertDisk(name, stream, false));
|
loader(name, stream => BootDiskII.BootDrive.InsertDisk(name, stream, false));
|
||||||
}
|
}
|
||||||
|
@ -231,7 +230,7 @@ private void Run() // machine thread
|
||||||
Uninitialize();
|
Uninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public const string Version = "0.9.2.0";
|
public const string Version = "0.9.3.0";
|
||||||
|
|
||||||
public MachineEvents Events { get; private set; }
|
public MachineEvents Events { get; private set; }
|
||||||
public MachineServices Services { get; private set; }
|
public MachineServices Services { get; private set; }
|
||||||
|
|
|
@ -65,7 +65,7 @@ private void OnCompositionTargetRendering(object sender, EventArgs e)
|
||||||
|
|
||||||
private void OnDiskButtonClick(int drive)
|
private void OnDiskButtonClick(int drive)
|
||||||
{
|
{
|
||||||
var dialog = new OpenFileDialog() { Filter = "Disk Files (*.dsk;*.nib)|*.dsk;*.nib|All Files (*.*)|*.*" };
|
var dialog = new OpenFileDialog() { Filter = "Disk Files (*.do;*.dsk;*.nib;*.po)|*.do;*.dsk;*.nib;*.po|All Files (*.*)|*.*" };
|
||||||
bool? result = dialog.ShowDialog();
|
bool? result = dialog.ShowDialog();
|
||||||
if (result.HasValue && result.Value)
|
if (result.HasValue && result.Value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -65,7 +65,7 @@ private void OnCompositionTargetRendering(object sender, EventArgs e)
|
||||||
|
|
||||||
//private void OnDiskButtonClick(int drive) // TODO
|
//private void OnDiskButtonClick(int drive) // TODO
|
||||||
//{
|
//{
|
||||||
// var dialog = new OpenFileDialog() { Filter = "Disk Files (*.dsk;*.nib)|*.dsk;*.nib|All Files (*.*)|*.*" };
|
// var dialog = new OpenFileDialog() { Filter = "Disk Files (*.do;*.dsk;*.nib;*.po)|*.do;*.dsk;*.nib;*.po|All Files (*.*)|*.*" };
|
||||||
// bool? result = dialog.ShowDialog();
|
// bool? result = dialog.ShowDialog();
|
||||||
// if (result.HasValue && result.Value)
|
// if (result.HasValue && result.Value)
|
||||||
// {
|
// {
|
||||||
|
|
|
@ -66,7 +66,7 @@ private void OnCompositionTargetRendering(object sender, EventArgs e)
|
||||||
|
|
||||||
private void OnDiskButtonClick(int drive)
|
private void OnDiskButtonClick(int drive)
|
||||||
{
|
{
|
||||||
var dialog = new OpenFileDialog() { Filter = "Disk Files (*.dsk;*.nib)|*.dsk;*.nib|All Files (*.*)|*.*" };
|
var dialog = new OpenFileDialog() { Filter = "Disk Files (*.do;*.dsk;*.nib;*.po)|*.do;*.dsk;*.nib;*.po|All Files (*.*)|*.*" };
|
||||||
bool? result = dialog.ShowDialog();
|
bool? result = dialog.ShowDialog();
|
||||||
if (result.HasValue && result.Value)
|
if (result.HasValue && result.Value)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user