diff --git a/Library/StreamExtensions.cs b/Library/StreamExtensions.cs index 4e4c005..16d7651 100644 --- a/Library/StreamExtensions.cs +++ b/Library/StreamExtensions.cs @@ -1,31 +1,30 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; namespace Jellyfish.Library { public static class StreamExtensions { - public static byte[] ReadBlock(this Stream stream, int count) + [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] + public static int ReadBlock(this Stream stream, byte[] buffer, int offset = 0, int minCount = int.MaxValue) { - return ReadBlock(stream, new byte[count], 0, count); + return ReadBlock(stream, buffer, offset, int.MaxValue, minCount); } - public static byte[] ReadBlock(this Stream stream, byte[] buffer) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - - return ReadBlock(stream, buffer, 0, buffer.Length); - } - - public static byte[] ReadBlock(this Stream stream, byte[] buffer, int offset, int count) + public static int ReadBlock(this Stream stream, byte[] buffer, int offset, int count, int minCount) { if (stream == null) { throw new ArgumentNullException("stream"); } + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + count = Math.Min(count, buffer.Length - offset); + minCount = Math.Min(minCount, buffer.Length - offset); int total = 0; int read; @@ -35,12 +34,12 @@ public static byte[] ReadBlock(this Stream stream, byte[] buffer, int offset, in } while ((read > 0) && (total < count)); - if (total < count) + if (total < minCount) { throw new EndOfStreamException(); } - return buffer; + return total; } public static void SkipBlock(this Stream stream, int count) @@ -57,8 +56,7 @@ public static void SkipBlock(this Stream stream, int count) else { const int BufferSize = 1024; - byte[] buffer = new byte[BufferSize]; - + var buffer = new byte[BufferSize]; int total = 0; int read; do diff --git a/Virtu/Disk525.cs b/Virtu/Disk525.cs index 6ef1f7a..9737e97 100644 --- a/Virtu/Disk525.cs +++ b/Virtu/Disk525.cs @@ -27,12 +27,14 @@ public static Disk525 CreateDisk(string name, Stream stream, bool isWriteProtect if (name.EndsWith(".dsk", StringComparison.OrdinalIgnoreCase)) { - byte[] data = stream.ReadBlock(TrackCount * SectorCount * SectorSize); + var data = new byte[TrackCount * SectorCount * SectorSize]; + stream.ReadBlock(data); return new DiskDsk(name, data, isWriteProtected); } else if (name.EndsWith(".nib", StringComparison.OrdinalIgnoreCase)) { - byte[] data = stream.ReadBlock(TrackCount * TrackSize); + var data = new byte[TrackCount * TrackSize]; + stream.ReadBlock(data); return new DiskNib(name, data, isWriteProtected); } @@ -49,7 +51,7 @@ public static Disk525 LoadState(BinaryReader reader, Version version) string name = reader.ReadString(); bool isWriteProtected = reader.ReadBoolean(); - byte[] data = reader.ReadBytes(reader.ReadInt32()); + var data = reader.ReadBytes(reader.ReadInt32()); if (name.EndsWith(".dsk", StringComparison.OrdinalIgnoreCase)) { diff --git a/Virtu/DiskIIController.cs b/Virtu/DiskIIController.cs index 5b5e7f9..3d78dd5 100644 --- a/Virtu/DiskIIController.cs +++ b/Virtu/DiskIIController.cs @@ -16,7 +16,6 @@ public DiskIIController(Machine machine) : public override void Initialize() { StorageService.LoadResource("Roms/DiskII.rom", stream => stream.ReadBlock(_romRegionC1C7)); - StorageService.LoadResource("Disks/Default.dsk", stream => _drives[0].InsertDisk("Default.dsk", stream, false)); } public override void Reset() diff --git a/Virtu/DiskIIDrive.cs b/Virtu/DiskIIDrive.cs index 298e5fe..d8c746d 100644 --- a/Virtu/DiskIIDrive.cs +++ b/Virtu/DiskIIDrive.cs @@ -55,11 +55,6 @@ public void SaveState(BinaryWriter writer) } } - public void InsertDisk(string fileName, bool isWriteProtected) - { - StorageService.LoadFile(fileName, stream => InsertDisk(fileName, stream, isWriteProtected)); - } - public void InsertDisk(string name, Stream stream, bool isWriteProtected) { FlushTrack(); diff --git a/Virtu/Machine.cs b/Virtu/Machine.cs index ae7eef2..ce6235e 100644 --- a/Virtu/Machine.cs +++ b/Virtu/Machine.cs @@ -1,10 +1,10 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; +using System.Text.RegularExpressions; using System.Threading; -using Jellyfish.Library; using Jellyfish.Virtu.Services; namespace Jellyfish.Virtu @@ -48,6 +48,11 @@ public void Dispose() _unpauseEvent.Close(); } + public IEnumerable GetCards() where T : PeripheralCard + { + return Slots.Where(card => card is T).Cast(); + } + public void Reset() { foreach (var component in Components) @@ -88,20 +93,6 @@ public void Stop() State = MachineState.Stopped; } - public DiskIIController FindDiskIIController() - { - for (int i = 7; i >= 1; i--) - { - var diskII = Slots[i] as DiskIIController; - if (diskII != null) - { - return diskII; - } - } - - return null; - } - private void Initialize() { foreach (var component in Components) @@ -112,36 +103,59 @@ private void Initialize() } } - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] private void LoadState() { - _storageService.Load(Machine.LastStateFileName, stream => +#if WINDOWS + var args = Environment.GetCommandLineArgs(); + if (args.Length > 1) { - try + string name = args[1]; + Func, bool> loader = StorageService.LoadFile; + + if (name.StartsWith("res://")) { - using (var reader = new BinaryReader(stream)) - { - string stateSignature = reader.ReadString(); - var stateVersion = new Version(reader.ReadString()); - if ((stateSignature == StateSignature) && (stateVersion == new Version(Machine.Version))) // avoid state version mismatch (for now) - { - foreach (var component in Components) - { - //_debugService.WriteLine("Loading component '{0}' state", component.GetType().Name); - component.LoadState(reader, stateVersion); - //_debugService.WriteLine("Loaded component '{0}' state", component.GetType().Name); - } - } - } + name = name.Substring(6); + loader = StorageService.LoadResource; } - catch (Exception) + + if (name.EndsWith(".bin", StringComparison.OrdinalIgnoreCase)) { - if (Debugger.IsAttached) - { - Debugger.Break(); - } + loader(name, stream => LoadState(stream)); } - }); + else if (name.EndsWith(".prg", StringComparison.OrdinalIgnoreCase)) + { + loader(name, stream => Memory.LoadProgram(stream)); + } + else if (Regex.IsMatch(name, @"\.(dsk|nib)$", RegexOptions.IgnoreCase)) + { + loader(name, stream => BootDiskII.Drives[0].InsertDisk(name, stream, false)); + } + } + else +#endif + if (!_storageService.Load(Machine.StateFileName, stream => LoadState(stream))) + { + StorageService.LoadResource("Disks/Default.dsk", stream => BootDiskII.Drives[0].InsertDisk("Default.dsk", stream, false)); + } + } + + private void LoadState(Stream stream) + { + using (var reader = new BinaryReader(stream)) + { + string stateSignature = reader.ReadString(); + var stateVersion = new Version(reader.ReadString()); + if ((stateSignature != StateSignature) || (stateVersion != new Version(Machine.Version))) // avoid state version mismatch (for now) + { + throw new InvalidOperationException(); + } + foreach (var component in Components) + { + //_debugService.WriteLine("Loading component '{0}' state", component.GetType().Name); + component.LoadState(reader, stateVersion); + //_debugService.WriteLine("Loaded component '{0}' state", component.GetType().Name); + } + } } private void Uninitialize() @@ -156,26 +170,29 @@ private void Uninitialize() private void SaveState() { - _storageService.Save(Machine.LastStateFileName, stream => + _storageService.Save(Machine.StateFileName, stream => SaveState(stream)); + } + + private void SaveState(Stream stream) + { + using (var writer = new BinaryWriter(stream)) { - using (var writer = new BinaryWriter(stream)) + writer.Write(StateSignature); + writer.Write(Machine.Version); + foreach (var component in Components) { - writer.Write(StateSignature); - writer.Write(Machine.Version); - foreach (var component in Components) - { - //_debugService.WriteLine("Saving component '{0}' state", component.GetType().Name); - component.SaveState(writer); - //_debugService.WriteLine("Saved component '{0}' state", component.GetType().Name); - } + //_debugService.WriteLine("Saving component '{0}' state", component.GetType().Name); + component.SaveState(writer); + //_debugService.WriteLine("Saved component '{0}' state", component.GetType().Name); } - }); + } } private void Run() // machine thread { - _debugService = Services.GetService(); + //_debugService = Services.GetService(); _storageService = Services.GetService(); + _bootDiskII = GetCards().Last(); Initialize(); Reset(); @@ -225,17 +242,20 @@ private void Run() // machine thread public PeripheralCard Slot6 { get; private set; } public PeripheralCard Slot7 { get; private set; } + public DiskIIController BootDiskII { get { return _bootDiskII; } } + public Collection Slots { get; private set; } public Collection Components { get; private set; } public Thread Thread { get; private set; } - private const string LastStateFileName = "LastState.bin"; + private const string StateFileName = "State.bin"; private const string StateSignature = "Virtu"; - private DebugService _debugService; + //private DebugService _debugService; private StorageService _storageService; private volatile MachineState _state; + private DiskIIController _bootDiskII; private AutoResetEvent _pauseEvent = new AutoResetEvent(false); private AutoResetEvent _unpauseEvent = new AutoResetEvent(false); diff --git a/Virtu/Memory.cs b/Virtu/Memory.cs index 86e39ca..f55c6d7 100644 --- a/Virtu/Memory.cs +++ b/Virtu/Memory.cs @@ -124,6 +124,45 @@ public override void Reset() // [7-3] MapRegionD0FF(); } + public void LoadProgram(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + int address = stream.ReadByte(); + address |= stream.ReadByte() << 8; + if (address < 0) + { + throw new EndOfStreamException(); + } + int entry = address; + + if (address < 0x0200) + { + address += stream.ReadBlock(_ramMainRegion0001, address, 0); + } + if ((0x0200 <= address) && (address < 0xC000)) + { + address += stream.ReadBlock(_ramMainRegion02BF, address - 0x0200, 0); + } + if ((0xC000 <= address) && (address < 0xD000)) + { + address += stream.ReadBlock(_ramMainBank1RegionD0DF, address - 0xC000, 0); + } + if ((0xD000 <= address) && (address < 0xE000)) + { + address += stream.ReadBlock(_ramMainBank2RegionD0DF, address - 0xD000, 0); + } + if (0xE000 <= address) + { + address += stream.ReadBlock(_ramMainRegionE0FF, address - 0xE000, 0); + } + + SetWarmEntry(entry); // assumes autostart monitor + } + public override void LoadState(BinaryReader reader, Version version) { if (reader == null) @@ -1507,6 +1546,13 @@ private void SetZeroPage(bool value) } #endregion + private void SetWarmEntry(int address) + { + _ramMainRegion02BF[0x03F2 - 0x0200] = (byte)(address & 0xFF); + _ramMainRegion02BF[0x03F3 - 0x0200] = (byte)(address >> 8); + _ramMainRegion02BF[0x03F4 - 0x0200] = (byte)((address >> 8) ^ 0xA5); + } + private static int SetBit7(int data, bool value) { return value ? (data | 0x80) : (data & 0x7F); diff --git a/Virtu/Services/IsolatedStorageService.cs b/Virtu/Services/IsolatedStorageService.cs index 2e41a1b..68159a9 100644 --- a/Virtu/Services/IsolatedStorageService.cs +++ b/Virtu/Services/IsolatedStorageService.cs @@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.IsolatedStorage; -using Jellyfish.Library; namespace Jellyfish.Virtu.Services { @@ -13,51 +12,36 @@ public IsolatedStorageService(Machine machine) : { } - public override void Load(string fileName, Action reader) + protected override void OnLoad(string fileName, Action reader) { if (reader == null) { throw new ArgumentNullException("reader"); } - try + using (var store = GetStore()) { - using (var store = GetStore()) + using (var stream = store.OpenFile(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { - using (var stream = store.OpenFile(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - reader(stream); - } + reader(stream); } } - catch (FileNotFoundException) - { - } - catch (IsolatedStorageException) - { - } } - public override void Save(string fileName, Action writer) + protected override void OnSave(string fileName, Action writer) { if (writer == null) { throw new ArgumentNullException("writer"); } - try + using (var store = GetStore()) { - using (var store = GetStore()) + using (var stream = store.OpenFile(fileName, FileMode.Create, FileAccess.Write, FileShare.None)) { - using (var stream = store.OpenFile(fileName, FileMode.Create, FileAccess.Write, FileShare.None)) - { - writer(stream); - } + writer(stream); } } - catch (IsolatedStorageException) - { - } } [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] diff --git a/Virtu/Services/StorageService.cs b/Virtu/Services/StorageService.cs index 3945547..5bf1894 100644 --- a/Virtu/Services/StorageService.cs +++ b/Virtu/Services/StorageService.cs @@ -1,9 +1,9 @@ using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; -using System.Resources; using System.Security; -using Jellyfish.Library; using Jellyfish.Virtu.Properties; namespace Jellyfish.Virtu.Services @@ -15,12 +15,27 @@ protected StorageService(Machine machine) : { } - public abstract void Load(string fileName, Action reader); + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public bool Load(string fileName, Action reader) + { + try + { + OnLoad(fileName, reader); + } + catch (Exception e) + { + Debug.WriteLine(e.ToString()); + return false; + } + + return true; + } #if !WINDOWS [SecuritySafeCritical] #endif - public static void LoadFile(string fileName, Action reader) + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public static bool LoadFile(string fileName, Action reader) { if (reader == null) { @@ -34,15 +49,20 @@ public static void LoadFile(string fileName, Action reader) reader(stream); } } - catch (FileNotFoundException) + catch (Exception e) { + Debug.WriteLine(e.ToString()); + return false; } + + return true; } #if !WINDOWS [SecuritySafeCritical] #endif - public static void LoadFile(FileInfo fileInfo, Action reader) + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public static bool LoadFile(FileInfo fileInfo, Action reader) { if (fileInfo == null) { @@ -60,12 +80,17 @@ public static void LoadFile(FileInfo fileInfo, Action reader) reader(stream); } } - catch (SecurityException) + catch (Exception e) { + Debug.WriteLine(e.ToString()); + return false; } + + return true; } - public static void LoadResource(string resourceName, Action reader) + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public static bool LoadResource(string resourceName, Action reader) { if (reader == null) { @@ -79,33 +104,63 @@ public static void LoadResource(string resourceName, Action reader) reader(stream); } } - catch (FileNotFoundException) + catch (Exception e) { + Debug.WriteLine(e.ToString()); + return false; } + + return true; } - public abstract void Save(string fileName, Action writer); + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public bool Save(string fileName, Action writer) + { + try + { + OnSave(fileName, writer); + } + catch (Exception e) + { + Debug.WriteLine(e.ToString()); + return false; + } + + return true; + } #if !WINDOWS [SecuritySafeCritical] #endif - public static void SaveFile(string fileName, Action writer) + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public static bool SaveFile(string fileName, Action writer) { if (writer == null) { throw new ArgumentNullException("writer"); } - using (var stream = File.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.None)) + try { - writer(stream); + using (var stream = File.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.None)) + { + writer(stream); + } } + catch (Exception e) + { + Debug.WriteLine(e.ToString()); + return false; + } + + return true; } #if !WINDOWS [SecuritySafeCritical] #endif - public static void SaveFile(FileInfo fileInfo, Action writer) + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public static bool SaveFile(FileInfo fileInfo, Action writer) { if (fileInfo == null) { @@ -116,12 +171,26 @@ public static void SaveFile(FileInfo fileInfo, Action writer) throw new ArgumentNullException("writer"); } - using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write, FileShare.None)) + try { - writer(stream); + using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write, FileShare.None)) + { + writer(stream); + } } + catch (Exception e) + { + Debug.WriteLine(e.ToString()); + return false; + } + + return true; } + protected abstract void OnLoad(string fileName, Action reader); + + protected abstract void OnSave(string fileName, Action writer); + private static Stream GetResourceStream(string resourceName) { resourceName = "Jellyfish.Virtu." + resourceName.Replace('/', '.'); diff --git a/Virtu/Silverlight/MainPage.xaml.cs b/Virtu/Silverlight/MainPage.xaml.cs index 1757cd6..8b5dce9 100644 --- a/Virtu/Silverlight/MainPage.xaml.cs +++ b/Virtu/Silverlight/MainPage.xaml.cs @@ -70,11 +70,7 @@ private void OnDiskButtonClick(int drive) if (result.HasValue && result.Value) { _machine.Pause(); - var diskII = _machine.FindDiskIIController(); - if (diskII != null) - { - StorageService.LoadFile(dialog.File, stream => diskII.Drives[drive].InsertDisk(dialog.File.Name, stream, false)); - } + StorageService.LoadFile(dialog.File, stream => _machine.BootDiskII.Drives[drive].InsertDisk(dialog.File.Name, stream, false)); _machine.Unpause(); } } diff --git a/Virtu/Silverlight/Phone/MainPage.xaml.cs b/Virtu/Silverlight/Phone/MainPage.xaml.cs index 4df1174..c111040 100644 --- a/Virtu/Silverlight/Phone/MainPage.xaml.cs +++ b/Virtu/Silverlight/Phone/MainPage.xaml.cs @@ -70,11 +70,7 @@ private void OnCompositionTargetRendering(object sender, EventArgs e) // if (result.HasValue && result.Value) // { // _machine.Pause(); - // var diskII = _machine.FindDiskIIController(); - // if (diskII != null) - // { - // StorageService.LoadFile(dialog.File, stream => diskII.Drives[drive].InsertDisk(dialog.File.Name, stream, false)); - // } + // StorageService.LoadFile(dialog.File, stream => _machine.BootDiskII.Drives[drive].InsertDisk(dialog.File.Name, stream, false)); // _machine.Unpause(); // } //} diff --git a/Virtu/Wpf/MainPage.xaml.cs b/Virtu/Wpf/MainPage.xaml.cs index 9a51b7c..95c0091 100644 --- a/Virtu/Wpf/MainPage.xaml.cs +++ b/Virtu/Wpf/MainPage.xaml.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel; -using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Media; @@ -72,11 +71,7 @@ private void OnDiskButtonClick(int drive) if (result.HasValue && result.Value) { _machine.Pause(); - var diskII = _machine.FindDiskIIController(); - if (diskII != null) - { - StorageService.LoadFile(dialog.FileName, stream => diskII.Drives[drive].InsertDisk(dialog.FileName, stream, false)); - } + StorageService.LoadFile(dialog.FileName, stream => _machine.BootDiskII.Drives[drive].InsertDisk(dialog.FileName, stream, false)); _machine.Unpause(); } } diff --git a/Virtu/Xna/Services/XnaStorageService.cs b/Virtu/Xna/Services/XnaStorageService.cs index 29675a2..e2ae277 100644 --- a/Virtu/Xna/Services/XnaStorageService.cs +++ b/Virtu/Xna/Services/XnaStorageService.cs @@ -18,29 +18,23 @@ public XnaStorageService(Machine machine, GameBase game) : _game = game; } - public override void Load(string fileName, Action reader) + protected override void OnLoad(string fileName, Action reader) { if (reader == null) { throw new ArgumentNullException("reader"); } - try + using (var storageContainer = OpenContainer()) { - using (var storageContainer = OpenContainer()) + using (var stream = storageContainer.OpenFile(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { - using (var stream = storageContainer.OpenFile(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - reader(stream); - } + reader(stream); } } - catch (FileNotFoundException) - { - } } - public override void Save(string fileName, Action writer) + protected override void OnSave(string fileName, Action writer) { if (writer == null) {