From c149a7260c102b7eaa9549842fe96c01835eacf6 Mon Sep 17 00:00:00 2001 From: Sean Fausett Date: Tue, 17 Jul 2012 21:42:04 +1200 Subject: [PATCH] Added support for 'Dos Order' (.do) disk images. Added support for 'ProDos Order' (.po) disk images. --- Virtu/Disk525.cs | 38 ++++++++++---------- Virtu/DiskDsk.cs | 45 +++++++++++++++++++++--- Virtu/DiskNib.cs | 13 +++++++ Virtu/Machine.cs | 5 ++- Virtu/Silverlight/MainPage.xaml.cs | 2 +- Virtu/Silverlight/Phone/MainPage.xaml.cs | 2 +- Virtu/Wpf/MainPage.xaml.cs | 2 +- 7 files changed, 78 insertions(+), 29 deletions(-) diff --git a/Virtu/Disk525.cs b/Virtu/Disk525.cs index 9737e97..6f9c92c 100644 --- a/Virtu/Disk525.cs +++ b/Virtu/Disk525.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Text.RegularExpressions; using Jellyfish.Library; namespace Jellyfish.Virtu @@ -20,22 +21,19 @@ public static Disk525 CreateDisk(string name, Stream stream, bool isWriteProtect { 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]; - stream.ReadBlock(data); - return new DiskDsk(name, data, isWriteProtected); + return new DiskDsk(name, stream, isWriteProtected, SectorSkew.Dos); } else if (name.EndsWith(".nib", StringComparison.OrdinalIgnoreCase)) { - var data = new byte[TrackCount * TrackSize]; - stream.ReadBlock(data); - return new DiskNib(name, data, isWriteProtected); + return new DiskNib(name, stream, isWriteProtected); + } + else if (name.EndsWith(".po", StringComparison.OrdinalIgnoreCase)) + { + return new DiskDsk(name, stream, isWriteProtected, SectorSkew.ProDos); } return null; @@ -50,17 +48,22 @@ public static Disk525 LoadState(BinaryReader reader, Version version) } string name = reader.ReadString(); - bool isWriteProtected = reader.ReadBoolean(); 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)) { return new DiskNib(name, data, isWriteProtected); } + else if (name.EndsWith(".po", StringComparison.OrdinalIgnoreCase)) + { + return new DiskDsk(name, data, isWriteProtected, SectorSkew.ProDos); + } return null; } @@ -73,19 +76,18 @@ public void SaveState(BinaryWriter writer) } writer.Write(Name); - writer.Write(IsWriteProtected); writer.Write(Data.Length); writer.Write(Data); + writer.Write(IsWriteProtected); } public abstract void ReadTrack(int number, int fraction, byte[] buffer); public abstract void WriteTrack(int number, int fraction, byte[] buffer); public string Name { get; private set; } - public bool IsWriteProtected { get; private set; } - [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 SectorSize = 0x100; diff --git a/Virtu/DiskDsk.cs b/Virtu/DiskDsk.cs index b7f1492..7b2b6f1 100644 --- a/Virtu/DiskDsk.cs +++ b/Virtu/DiskDsk.cs @@ -1,12 +1,29 @@ using System; +using System.IO; +using Jellyfish.Library; 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) { + _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) @@ -38,7 +55,7 @@ public override void ReadTrack(int number, int fraction, byte[] buffer) WriteNibble(0xAA); WriteNibble(0xAD); - WriteDataNibbles((track * SectorCount + DosOrderToLogicalSector[sector]) * SectorSize); + WriteDataNibbles((track * SectorCount + _sectorSkew[sector]) * SectorSize); WriteNibble(0xDE); // data epilogue WriteNibble(0xAA); @@ -84,7 +101,7 @@ public override void WriteTrack(int number, int fraction, byte[] buffer) if (!Read3Nibbles(0xD5, 0xAA, 0xAD, 0x20)) break; // no data prologue - if (!ReadDataNibbles((track * SectorCount + DosOrderToLogicalSector[sector]) * SectorSize)) + if (!ReadDataNibbles((track * SectorCount + _sectorSkew[sector]) * SectorSize)) break; // bad data checksum if ((ReadNibble() != 0xDE) || (ReadNibble() != 0xAA)) @@ -245,15 +262,33 @@ private void WriteDataNibbles(int sectorOffset) 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[] 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 }; + 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[] { 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3, diff --git a/Virtu/DiskNib.cs b/Virtu/DiskNib.cs index 1466abc..e186e00 100644 --- a/Virtu/DiskNib.cs +++ b/Virtu/DiskNib.cs @@ -1,4 +1,6 @@ using System; +using System.IO; +using Jellyfish.Library; 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) { Buffer.BlockCopy(Data, (number / 2) * TrackSize, buffer, 0, TrackSize); diff --git a/Virtu/Machine.cs b/Virtu/Machine.cs index 73f4af4..febbb4d 100644 --- a/Virtu/Machine.cs +++ b/Virtu/Machine.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; -using System.Text.RegularExpressions; using System.Threading; using Jellyfish.Virtu.Services; @@ -140,7 +139,7 @@ private void LoadState() { 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)); } @@ -231,7 +230,7 @@ private void Run() // machine thread Uninitialize(); } - public const string Version = "0.9.2.0"; + public const string Version = "0.9.3.0"; public MachineEvents Events { get; private set; } public MachineServices Services { get; private set; } diff --git a/Virtu/Silverlight/MainPage.xaml.cs b/Virtu/Silverlight/MainPage.xaml.cs index 1abea46..e6461c3 100644 --- a/Virtu/Silverlight/MainPage.xaml.cs +++ b/Virtu/Silverlight/MainPage.xaml.cs @@ -65,7 +65,7 @@ private void OnCompositionTargetRendering(object sender, EventArgs e) 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(); if (result.HasValue && result.Value) { diff --git a/Virtu/Silverlight/Phone/MainPage.xaml.cs b/Virtu/Silverlight/Phone/MainPage.xaml.cs index eb0f317..e9f8f4c 100644 --- a/Virtu/Silverlight/Phone/MainPage.xaml.cs +++ b/Virtu/Silverlight/Phone/MainPage.xaml.cs @@ -65,7 +65,7 @@ private void OnCompositionTargetRendering(object sender, EventArgs e) //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(); // if (result.HasValue && result.Value) // { diff --git a/Virtu/Wpf/MainPage.xaml.cs b/Virtu/Wpf/MainPage.xaml.cs index d030c7b..041d411 100644 --- a/Virtu/Wpf/MainPage.xaml.cs +++ b/Virtu/Wpf/MainPage.xaml.cs @@ -66,7 +66,7 @@ private void OnCompositionTargetRendering(object sender, EventArgs e) 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(); if (result.HasValue && result.Value) {