mirror of
https://github.com/digital-jellyfish/Virtu.git
synced 2024-11-27 00:49:39 +00:00
Added support for 'executable' (.xex) files.
Added 'default' debug service using Trace output. Added more debug service logging to machine.
This commit is contained in:
parent
b24bcd767e
commit
7aacc26962
@ -163,7 +163,7 @@ private void Run() // com mta thread
|
||||
private IntPtr _window;
|
||||
private IDirectSound _device;
|
||||
private IDirectSoundBuffer _buffer;
|
||||
private object _bufferLock = new object();
|
||||
private readonly object _bufferLock = new object();
|
||||
private Action<IntPtr, int> _updater;
|
||||
|
||||
private AutoResetEvent _position1Event = new AutoResetEvent(false);
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Jellyfish.Library
|
||||
{
|
||||
@ -16,13 +15,11 @@ public T Value
|
||||
{
|
||||
if (_value == null)
|
||||
{
|
||||
T value = _initializer();
|
||||
if (Interlocked.CompareExchange(ref _value, value, null) != null)
|
||||
lock (_lock)
|
||||
{
|
||||
var disposable = value as IDisposable; // dispose preempted instance
|
||||
if (disposable != null)
|
||||
if (_value == null)
|
||||
{
|
||||
disposable.Dispose();
|
||||
_value = _initializer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -32,6 +29,7 @@ public T Value
|
||||
}
|
||||
|
||||
private Func<T> _initializer;
|
||||
private T _value;
|
||||
private readonly object _lock = new object();
|
||||
private volatile T _value;
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,16 @@ namespace Jellyfish.Library
|
||||
{
|
||||
public static class StreamExtensions
|
||||
{
|
||||
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
|
||||
public static int ReadBlock(this Stream stream, byte[] buffer, int offset = 0, int minCount = int.MaxValue)
|
||||
[SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#")]
|
||||
public static int ReadBlock(this Stream stream, byte[] buffer, int offset, ref int count)
|
||||
{
|
||||
return ReadBlock(stream, buffer, offset, int.MaxValue, minCount);
|
||||
int read = ReadBlock(stream, buffer, offset, count, count);
|
||||
count -= read;
|
||||
return read;
|
||||
}
|
||||
|
||||
public static int ReadBlock(this Stream stream, byte[] buffer, int offset, int count, int minCount)
|
||||
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
|
||||
public static int ReadBlock(this Stream stream, byte[] buffer, int offset = 0, int count = int.MaxValue, int minCount = int.MaxValue)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
@ -42,6 +45,25 @@ public static int ReadBlock(this Stream stream, byte[] buffer, int offset, int c
|
||||
return total;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
|
||||
public static int ReadWord(this Stream stream, bool optional = false)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException("stream");
|
||||
}
|
||||
|
||||
int lowByte = stream.ReadByte();
|
||||
int highByte = stream.ReadByte();
|
||||
int word = lowByte | (highByte << 8);
|
||||
if ((word < 0) && !optional)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
return word;
|
||||
}
|
||||
|
||||
public static void SkipBlock(this Stream stream, int count)
|
||||
{
|
||||
if (stream == null)
|
||||
@ -55,16 +77,18 @@ public static void SkipBlock(this Stream stream, int count)
|
||||
}
|
||||
else
|
||||
{
|
||||
const int BufferSize = 1024;
|
||||
var buffer = new byte[BufferSize];
|
||||
int total = 0;
|
||||
int read;
|
||||
do
|
||||
{
|
||||
total += read = stream.Read(buffer, 0, Math.Min(count - total, BufferSize));
|
||||
total += read = stream.Read(_skipBuffer, 0, Math.Min(count - total, SkipBufferSize));
|
||||
}
|
||||
while ((read > 0) && (total < count));
|
||||
}
|
||||
}
|
||||
|
||||
private const int SkipBufferSize = 1024;
|
||||
|
||||
private static byte[] _skipBuffer = new byte[SkipBufferSize];
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,10 @@
|
||||
<Recognized>
|
||||
<Word>Annunciator</Word>
|
||||
<Word>Dsk</Word>
|
||||
<Word>Prg</Word>
|
||||
<Word>Unpause</Word>
|
||||
<Word>Virtu</Word>
|
||||
<Word>Xex</Word>
|
||||
<Word>Xna</Word>
|
||||
<Word>x</Word>
|
||||
<Word>y</Word>
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using Jellyfish.Library;
|
||||
@ -11,6 +12,12 @@ public sealed class DiskIIController : PeripheralCard
|
||||
public DiskIIController(Machine machine) :
|
||||
base(machine)
|
||||
{
|
||||
Drive1 = new DiskIIDrive(machine);
|
||||
Drive2 = new DiskIIDrive(machine);
|
||||
|
||||
Drives = new Collection<DiskIIDrive> { Drive1, Drive2 };
|
||||
|
||||
BootDrive = Drive1;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
@ -41,7 +48,12 @@ public override void LoadState(BinaryReader reader, Version version)
|
||||
_loadMode = reader.ReadBoolean();
|
||||
_writeMode = reader.ReadBoolean();
|
||||
_driveSpin = reader.ReadBoolean();
|
||||
_drives.ForEach(drive => drive.LoadState(reader, version));
|
||||
foreach (var drive in Drives)
|
||||
{
|
||||
DebugService.WriteMessage("Loading machine '{0}'", drive.GetType().Name);
|
||||
drive.LoadState(reader, version);
|
||||
//DebugService.WriteMessage("Loaded machine '{0}'", drive.GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SaveState(BinaryWriter writer)
|
||||
@ -58,7 +70,12 @@ public override void SaveState(BinaryWriter writer)
|
||||
writer.Write(_loadMode);
|
||||
writer.Write(_writeMode);
|
||||
writer.Write(_driveSpin);
|
||||
_drives.ForEach(drive => drive.SaveState(writer));
|
||||
foreach (var drive in Drives)
|
||||
{
|
||||
DebugService.WriteMessage("Saving machine '{0}'", drive.GetType().Name);
|
||||
drive.SaveState(writer);
|
||||
//DebugService.WriteMessage("Saved machine '{0}'", drive.GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
|
||||
@ -92,7 +109,7 @@ public override int ReadIoRegionC0C0(int address)
|
||||
{
|
||||
if (!_writeMode)
|
||||
{
|
||||
return _latch = _drives[_driveNumber].Read();
|
||||
return _latch = Drives[_driveNumber].Read();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -107,7 +124,7 @@ public override int ReadIoRegionC0C0(int address)
|
||||
{
|
||||
// write protect is forced if phase 1 is on [F9.7]
|
||||
_latch &= 0x7F;
|
||||
if (_drives[_driveNumber].IsWriteProtected ||
|
||||
if (Drives[_driveNumber].IsWriteProtected ||
|
||||
(_phaseStates & Phase1On) != 0)
|
||||
{
|
||||
_latch |= 0x80;
|
||||
@ -205,13 +222,13 @@ private void WriteLatch()
|
||||
// write protect is forced if phase 1 is on [F9.7]
|
||||
if ((_phaseStates & Phase1On) == 0)
|
||||
{
|
||||
_drives[_driveNumber].Write(_latch);
|
||||
Drives[_driveNumber].Write(_latch);
|
||||
}
|
||||
}
|
||||
|
||||
private void Flush()
|
||||
{
|
||||
_drives[_driveNumber].FlushTrack();
|
||||
Drives[_driveNumber].FlushTrack();
|
||||
}
|
||||
|
||||
private void SetDriveNumber(int driveNumber)
|
||||
@ -241,19 +258,22 @@ private void SetPhase(int address)
|
||||
|
||||
if (_motorOn)
|
||||
{
|
||||
_drives[_driveNumber].ApplyPhaseChange(_phaseStates);
|
||||
Drives[_driveNumber].ApplyPhaseChange(_phaseStates);
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
||||
public DiskIIDrive[] Drives { get { return _drives; } }
|
||||
public DiskIIDrive Drive1 { get; private set; }
|
||||
public DiskIIDrive Drive2 { get; private set; }
|
||||
|
||||
public Collection<DiskIIDrive> Drives { get; private set; }
|
||||
|
||||
public DiskIIDrive BootDrive { get; private set; }
|
||||
|
||||
private const int Phase0On = 1 << 0;
|
||||
private const int Phase1On = 1 << 1;
|
||||
private const int Phase2On = 1 << 2;
|
||||
private const int Phase3On = 1 << 3;
|
||||
|
||||
private DiskIIDrive[] _drives = new DiskIIDrive[] { new DiskIIDrive(), new DiskIIDrive() };
|
||||
private int _latch;
|
||||
private int _phaseStates;
|
||||
private bool _motorOn;
|
||||
|
@ -5,9 +5,10 @@
|
||||
|
||||
namespace Jellyfish.Virtu
|
||||
{
|
||||
public sealed class DiskIIDrive
|
||||
public sealed class DiskIIDrive : MachineComponent
|
||||
{
|
||||
public DiskIIDrive()
|
||||
public DiskIIDrive(Machine machine) :
|
||||
base(machine)
|
||||
{
|
||||
DriveArmStepDelta[0] = new int[] { 0, 0, 1, 1, 0, 0, 1, 1, -1, -1, 0, 0, -1, -1, 0, 0 }; // phase 0
|
||||
DriveArmStepDelta[1] = new int[] { 0, -1, 0, -1, 1, 0, 1, 0, 0, -1, 0, -1, 1, 0, 1, 0 }; // phase 1
|
||||
@ -15,7 +16,7 @@ public DiskIIDrive()
|
||||
DriveArmStepDelta[3] = new int[] { 0, 1, 0, 1, -1, 0, -1, 0, 0, 1, 0, 1, -1, 0, -1, 0 }; // phase 3
|
||||
}
|
||||
|
||||
public void LoadState(BinaryReader reader, Version version)
|
||||
public override void LoadState(BinaryReader reader, Version version)
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
@ -30,10 +31,18 @@ public void LoadState(BinaryReader reader, Version version)
|
||||
{
|
||||
reader.Read(_trackData, 0, _trackData.Length);
|
||||
}
|
||||
_disk = reader.ReadBoolean() ? Disk525.LoadState(reader, version) : null;
|
||||
if (reader.ReadBoolean())
|
||||
{
|
||||
DebugService.WriteMessage("Loading machine '{0}'", typeof(Disk525).Name);
|
||||
_disk = Disk525.LoadState(reader, version);
|
||||
}
|
||||
else
|
||||
{
|
||||
_disk = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveState(BinaryWriter writer)
|
||||
public override void SaveState(BinaryWriter writer)
|
||||
{
|
||||
if (writer == null)
|
||||
{
|
||||
@ -51,12 +60,14 @@ public void SaveState(BinaryWriter writer)
|
||||
writer.Write(_disk != null);
|
||||
if (_disk != null)
|
||||
{
|
||||
DebugService.WriteMessage("Saving machine '{0}'", _disk.GetType().Name);
|
||||
_disk.SaveState(writer);
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertDisk(string name, Stream stream, bool isWriteProtected)
|
||||
{
|
||||
DebugService.WriteMessage("Inserting disk '{0}'", name);
|
||||
FlushTrack();
|
||||
_disk = Disk525.CreateDisk(name, stream, isWriteProtected);
|
||||
_trackLoaded = false;
|
||||
@ -64,11 +75,15 @@ public void InsertDisk(string name, Stream stream, bool isWriteProtected)
|
||||
|
||||
public void RemoveDisk()
|
||||
{
|
||||
_trackLoaded = false;
|
||||
_trackChanged = false;
|
||||
_trackNumber = 0;
|
||||
_trackOffset = 0;
|
||||
_disk = null;
|
||||
if (_disk != null)
|
||||
{
|
||||
DebugService.WriteMessage("Removing disk '{0}'", _disk.Name);
|
||||
_trackLoaded = false;
|
||||
_trackChanged = false;
|
||||
_trackNumber = 0;
|
||||
_trackOffset = 0;
|
||||
_disk = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyPhaseChange(int phaseState)
|
||||
|
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
@ -39,6 +39,8 @@ public Machine()
|
||||
Slots = new Collection<PeripheralCard> { null, Slot1, Slot2, Slot3, Slot4, Slot5, Slot6, Slot7 };
|
||||
Components = new Collection<MachineComponent> { Cpu, Memory, Keyboard, GamePort, Cassette, Speaker, Video, NoSlotClock, Slot1, Slot2, Slot3, Slot4, Slot5, Slot6, Slot7 };
|
||||
|
||||
BootDiskII = Slots.OfType<DiskIIController>().Last();
|
||||
|
||||
Thread = new Thread(Run) { Name = "Machine" };
|
||||
}
|
||||
|
||||
@ -52,33 +54,45 @@ public void Reset()
|
||||
{
|
||||
foreach (var component in Components)
|
||||
{
|
||||
//_debugService.WriteLine("Resetting component '{0}'", component.GetType().Name);
|
||||
_debugService.WriteMessage("Resetting machine '{0}'", component.GetType().Name);
|
||||
component.Reset();
|
||||
//_debugService.WriteLine("Reset component '{0}'", component.GetType().Name);
|
||||
//_debugService.WriteMessage("Reset machine '{0}'", component.GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Jellyfish.Virtu.Services.DebugService.WriteMessage(System.String)")]
|
||||
public void Start()
|
||||
{
|
||||
_debugService = Services.GetService<DebugService>();
|
||||
_storageService = Services.GetService<StorageService>();
|
||||
|
||||
_debugService.WriteMessage("Starting machine");
|
||||
State = MachineState.Starting;
|
||||
Thread.Start();
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Jellyfish.Virtu.Services.DebugService.WriteMessage(System.String)")]
|
||||
public void Pause()
|
||||
{
|
||||
_debugService.WriteMessage("Pausing machine");
|
||||
State = MachineState.Pausing;
|
||||
_pauseEvent.WaitOne();
|
||||
State = MachineState.Paused;
|
||||
_debugService.WriteMessage("Paused machine");
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Jellyfish.Virtu.Services.DebugService.WriteMessage(System.String)")]
|
||||
public void Unpause()
|
||||
{
|
||||
_debugService.WriteMessage("Running machine");
|
||||
State = MachineState.Running;
|
||||
_unpauseEvent.Set();
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Jellyfish.Virtu.Services.DebugService.WriteMessage(System.String)")]
|
||||
public void Stop()
|
||||
{
|
||||
_debugService.WriteMessage("Stopping machine");
|
||||
State = MachineState.Stopping;
|
||||
_unpauseEvent.Set();
|
||||
if (Thread.IsAlive)
|
||||
@ -86,15 +100,16 @@ public void Stop()
|
||||
Thread.Join();
|
||||
}
|
||||
State = MachineState.Stopped;
|
||||
_debugService.WriteMessage("Stopped machine");
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
foreach (var component in Components)
|
||||
{
|
||||
//_debugService.WriteLine("Initializing component '{0}'", component.GetType().Name);
|
||||
_debugService.WriteMessage("Initializing machine '{0}'", component.GetType().Name);
|
||||
component.Initialize();
|
||||
//_debugService.WriteLine("Initialized component '{0}'", component.GetType().Name);
|
||||
//_debugService.WriteMessage("Initialized machine '{0}'", component.GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,18 +134,22 @@ private void LoadState()
|
||||
}
|
||||
else if (name.EndsWith(".prg", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
loader(name, stream => Memory.LoadProgram(stream));
|
||||
loader(name, stream => Memory.LoadPrg(stream));
|
||||
}
|
||||
else if (name.EndsWith(".xex", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
loader(name, stream => Memory.LoadXex(stream));
|
||||
}
|
||||
else if (Regex.IsMatch(name, @"\.(dsk|nib)$", RegexOptions.IgnoreCase))
|
||||
{
|
||||
loader(name, stream => BootDiskII.Drives[0].InsertDisk(name, stream, false));
|
||||
loader(name, stream => BootDiskII.BootDrive.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));
|
||||
StorageService.LoadResource("Disks/Default.dsk", stream => BootDiskII.BootDrive.InsertDisk("Default.dsk", stream, false));
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,17 +157,17 @@ 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)
|
||||
string signature = reader.ReadString();
|
||||
var version = new Version(reader.ReadString());
|
||||
if ((signature != StateSignature) || (version != 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);
|
||||
_debugService.WriteMessage("Loading machine '{0}'", component.GetType().Name);
|
||||
component.LoadState(reader, version);
|
||||
//_debugService.WriteMessage("Loaded machine '{0}'", component.GetType().Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,9 +185,9 @@ private void SaveState(Stream stream)
|
||||
writer.Write(Machine.Version);
|
||||
foreach (var component in Components)
|
||||
{
|
||||
//_debugService.WriteLine("Saving component '{0}' state", component.GetType().Name);
|
||||
_debugService.WriteMessage("Saving machine '{0}'", component.GetType().Name);
|
||||
component.SaveState(writer);
|
||||
//_debugService.WriteLine("Saved component '{0}' state", component.GetType().Name);
|
||||
//_debugService.WriteMessage("Saved machine '{0}'", component.GetType().Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,22 +196,20 @@ private void Uninitialize()
|
||||
{
|
||||
foreach (var component in Components)
|
||||
{
|
||||
//_debugService.WriteLine("Uninitializing component '{0}'", component.GetType().Name);
|
||||
_debugService.WriteMessage("Uninitializing machine '{0}'", component.GetType().Name);
|
||||
component.Uninitialize();
|
||||
//_debugService.WriteLine("Uninitialized component '{0}'", component.GetType().Name);
|
||||
//_debugService.WriteMessage("Uninitialized machine '{0}'", component.GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Jellyfish.Virtu.Services.DebugService.WriteMessage(System.String)")]
|
||||
private void Run() // machine thread
|
||||
{
|
||||
//_debugService = Services.GetService<DebugService>();
|
||||
_storageService = Services.GetService<StorageService>();
|
||||
_bootDiskII = Slots.OfType<DiskIIController>().Last();
|
||||
|
||||
Initialize();
|
||||
Reset();
|
||||
LoadState();
|
||||
|
||||
_debugService.WriteMessage("Running machine");
|
||||
State = MachineState.Running;
|
||||
do
|
||||
{
|
||||
@ -237,20 +254,19 @@ 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<PeripheralCard> Slots { get; private set; }
|
||||
public Collection<MachineComponent> Components { get; private set; }
|
||||
|
||||
public DiskIIController BootDiskII { get; private set; }
|
||||
|
||||
public Thread Thread { get; private set; }
|
||||
|
||||
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);
|
||||
|
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Jellyfish.Library;
|
||||
using Jellyfish.Virtu.Services;
|
||||
|
||||
namespace Jellyfish.Virtu
|
||||
{
|
||||
@ -8,6 +10,8 @@ public abstract class MachineComponent
|
||||
protected MachineComponent(Machine machine)
|
||||
{
|
||||
Machine = machine;
|
||||
|
||||
_debugService = new Lazy<DebugService>(() => Machine.Services.GetService<DebugService>());
|
||||
}
|
||||
|
||||
public virtual void Initialize()
|
||||
@ -31,5 +35,8 @@ public virtual void SaveState(BinaryWriter writer)
|
||||
}
|
||||
|
||||
protected Machine Machine { get; private set; }
|
||||
protected DebugService DebugService { get { return _debugService.Value; } }
|
||||
|
||||
private Lazy<DebugService> _debugService;
|
||||
}
|
||||
}
|
||||
|
140
Virtu/Memory.cs
140
Virtu/Memory.cs
@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using Jellyfish.Library;
|
||||
using Jellyfish.Virtu.Properties;
|
||||
using Jellyfish.Virtu.Services;
|
||||
|
||||
namespace Jellyfish.Virtu
|
||||
@ -124,45 +126,6 @@ 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)
|
||||
@ -212,6 +175,49 @@ public override void SaveState(BinaryWriter writer)
|
||||
writer.Write(_ramAuxRegionE0FF);
|
||||
}
|
||||
|
||||
public void LoadPrg(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException("stream");
|
||||
}
|
||||
|
||||
int startAddress = stream.ReadWord();
|
||||
SetWarmEntry(startAddress); // assumes autostart monitor
|
||||
Load(stream, startAddress);
|
||||
}
|
||||
|
||||
public void LoadXex(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException("stream");
|
||||
}
|
||||
|
||||
const int Marker = 0xFFFF;
|
||||
int marker = stream.ReadWord(); // mandatory marker
|
||||
if (marker != Marker)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, Strings.MarkerNotFound, Marker));
|
||||
}
|
||||
int startAddress = stream.ReadWord();
|
||||
int endAddress = stream.ReadWord();
|
||||
SetWarmEntry(startAddress); // assumes autostart monitor
|
||||
|
||||
do
|
||||
{
|
||||
if (startAddress > endAddress)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, Strings.InvalidAddressRange, startAddress, endAddress));
|
||||
}
|
||||
Load(stream, startAddress, endAddress - startAddress + 1);
|
||||
marker = stream.ReadWord(optional: true); // optional marker
|
||||
startAddress = (marker != Marker) ? marker : stream.ReadWord(optional: true);
|
||||
endAddress = stream.ReadWord(optional: true);
|
||||
}
|
||||
while ((startAddress >= 0) && (endAddress >= 0));
|
||||
}
|
||||
|
||||
#region Core Read & Write
|
||||
public int Read(int address)
|
||||
{
|
||||
@ -1546,6 +1552,62 @@ private void SetZeroPage(bool value)
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void Load(Stream stream, int startAddress)
|
||||
{
|
||||
DebugService.WriteMessage("Loading memory ${0:X04}", startAddress);
|
||||
int address = startAddress;
|
||||
if (address < 0x0200)
|
||||
{
|
||||
address += stream.ReadBlock(_ramMainRegion0001, address, minCount: 0);
|
||||
}
|
||||
if ((0x0200 <= address) && (address < 0xC000))
|
||||
{
|
||||
address += stream.ReadBlock(_ramMainRegion02BF, address - 0x0200, minCount: 0);
|
||||
}
|
||||
if ((0xC000 <= address) && (address < 0xD000))
|
||||
{
|
||||
address += stream.ReadBlock(_ramMainBank1RegionD0DF, address - 0xC000, minCount: 0);
|
||||
}
|
||||
if ((0xD000 <= address) && (address < 0xE000))
|
||||
{
|
||||
address += stream.ReadBlock(_ramMainBank2RegionD0DF, address - 0xD000, minCount: 0);
|
||||
}
|
||||
if (0xE000 <= address)
|
||||
{
|
||||
address += stream.ReadBlock(_ramMainRegionE0FF, address - 0xE000, minCount: 0);
|
||||
}
|
||||
if (address > startAddress)
|
||||
{
|
||||
DebugService.WriteMessage("Loaded memory ${0:X04}-${1:X04} (${2:X04})", startAddress, address - 1, address - startAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private void Load(Stream stream, int startAddress, int length)
|
||||
{
|
||||
DebugService.WriteMessage("Loading memory ${0:X04}-${1:X04} (${2:X04})", startAddress, startAddress + length - 1, length);
|
||||
int address = startAddress;
|
||||
if (address < 0x0200)
|
||||
{
|
||||
address += stream.ReadBlock(_ramMainRegion0001, address, ref length);
|
||||
}
|
||||
if ((0x0200 <= address) && (address < 0xC000))
|
||||
{
|
||||
address += stream.ReadBlock(_ramMainRegion02BF, address - 0x0200, ref length);
|
||||
}
|
||||
if ((0xC000 <= address) && (address < 0xD000))
|
||||
{
|
||||
address += stream.ReadBlock(_ramMainBank1RegionD0DF, address - 0xC000, ref length);
|
||||
}
|
||||
if ((0xD000 <= address) && (address < 0xE000))
|
||||
{
|
||||
address += stream.ReadBlock(_ramMainBank2RegionD0DF, address - 0xD000, ref length);
|
||||
}
|
||||
if (0xE000 <= address)
|
||||
{
|
||||
address += stream.ReadBlock(_ramMainRegionE0FF, address - 0xE000, ref length);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetWarmEntry(int address)
|
||||
{
|
||||
_ramMainRegion02BF[0x03F2 - 0x0200] = (byte)(address & 0xFF);
|
||||
|
20
Virtu/Properties/Strings.Designer.cs
generated
20
Virtu/Properties/Strings.Designer.cs
generated
@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.530
|
||||
// Runtime Version:4.0.30319.544
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
@ -60,6 +60,24 @@ internal Strings() {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid address range ${0:X04}-${1:X04}..
|
||||
/// </summary>
|
||||
internal static string InvalidAddressRange {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidAddressRange", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Marker ${0:X04} not found..
|
||||
/// </summary>
|
||||
internal static string MarkerNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("MarkerNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Resource '{0}' not found..
|
||||
/// </summary>
|
||||
|
@ -117,6 +117,12 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="InvalidAddressRange" xml:space="preserve">
|
||||
<value>Invalid address range ${0:X04}-${1:X04}.</value>
|
||||
</data>
|
||||
<data name="MarkerNotFound" xml:space="preserve">
|
||||
<value>Marker ${0:X04} not found.</value>
|
||||
</data>
|
||||
<data name="ResourceNotFound" xml:space="preserve">
|
||||
<value>Resource '{0}' not found.</value>
|
||||
</data>
|
||||
|
@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Jellyfish.Library;
|
||||
|
||||
namespace Jellyfish.Virtu.Services
|
||||
{
|
||||
@ -11,19 +14,54 @@ public DebugService(Machine machine) :
|
||||
{
|
||||
}
|
||||
|
||||
public void WriteLine(string message)
|
||||
public void WriteMessage(string message)
|
||||
{
|
||||
OnWriteLine(string.Concat(DateTime.Now.TimeOfDay, ' ', message));
|
||||
OnWriteMessage(FormatMessage(message));
|
||||
}
|
||||
|
||||
public void WriteLine(string format, params object[] args)
|
||||
public void WriteMessage(string format, params object[] args)
|
||||
{
|
||||
WriteLine(string.Format(CultureInfo.InvariantCulture, format, args));
|
||||
OnWriteMessage(FormatMessage(format, args));
|
||||
}
|
||||
|
||||
protected virtual void OnWriteLine(string message)
|
||||
protected virtual void OnWriteMessage(string message)
|
||||
{
|
||||
#if SILVERLIGHT || WINDOWS_PHONE || XBOX
|
||||
Debug.WriteLine(message);
|
||||
#else
|
||||
Trace.WriteLine(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
private string FormatMessage(string format, params object[] args)
|
||||
{
|
||||
var message = new StringBuilder(256);
|
||||
message.AppendFormat(CultureInfo.InvariantCulture, "[{0} T{1:X3} Virtu] ", DateTime.Now.ToString("HH:mm:ss.fff", CultureInfo.InvariantCulture), Thread.CurrentThread.ManagedThreadId);
|
||||
if (args.Length > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
message.AppendFormat(CultureInfo.InvariantCulture, format, args);
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
#if WINDOWS_PHONE || XBOX
|
||||
WriteMessage("[DebugService.FormatMessage] format: {0}; exception: {1}", format, ex.Message);
|
||||
#else
|
||||
WriteMessage("[DebugService.FormatMessage] format: {0}; args: {1}; exception: {2}", format, string.Join(", ", args), ex.Message);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
message.Append(format);
|
||||
}
|
||||
|
||||
return message.ToString();
|
||||
}
|
||||
|
||||
public static DebugService Default { get { return _default.Value; } }
|
||||
|
||||
private static readonly Lazy<DebugService> _default = new Lazy<DebugService>(() => new DebugService(null));
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,12 @@ public bool Load(string fileName, Action<Stream> reader)
|
||||
{
|
||||
try
|
||||
{
|
||||
DebugService.WriteMessage("Loading file '{0}'", fileName);
|
||||
OnLoad(fileName, reader);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(e.ToString());
|
||||
DebugService.WriteMessage(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -44,14 +45,15 @@ public static bool LoadFile(string fileName, Action<Stream> reader)
|
||||
|
||||
try
|
||||
{
|
||||
DebugService.Default.WriteMessage("Loading file '{0}'", fileName);
|
||||
using (var stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
reader(stream);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(e.ToString());
|
||||
DebugService.Default.WriteMessage(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -75,14 +77,15 @@ public static bool LoadFile(FileInfo fileInfo, Action<Stream> reader)
|
||||
|
||||
try
|
||||
{
|
||||
DebugService.Default.WriteMessage("Loading file '{0}'", fileInfo.Name);
|
||||
using (var stream = fileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
reader(stream);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(e.ToString());
|
||||
DebugService.Default.WriteMessage(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -99,14 +102,15 @@ public static bool LoadResource(string resourceName, Action<Stream> reader)
|
||||
|
||||
try
|
||||
{
|
||||
DebugService.Default.WriteMessage("Loading resource '{0}'", resourceName);
|
||||
using (var stream = GetResourceStream(resourceName))
|
||||
{
|
||||
reader(stream);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(e.ToString());
|
||||
DebugService.Default.WriteMessage(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -118,11 +122,12 @@ public bool Save(string fileName, Action<Stream> writer)
|
||||
{
|
||||
try
|
||||
{
|
||||
DebugService.WriteMessage("Saving file '{0}'", fileName);
|
||||
OnSave(fileName, writer);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(e.ToString());
|
||||
DebugService.WriteMessage(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -142,14 +147,15 @@ public static bool SaveFile(string fileName, Action<Stream> writer)
|
||||
|
||||
try
|
||||
{
|
||||
DebugService.Default.WriteMessage("Saving file '{0}'", fileName);
|
||||
using (var stream = File.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
writer(stream);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(e.ToString());
|
||||
DebugService.Default.WriteMessage(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -173,14 +179,15 @@ public static bool SaveFile(FileInfo fileInfo, Action<Stream> writer)
|
||||
|
||||
try
|
||||
{
|
||||
DebugService.Default.WriteMessage("Saving file '{0}'", fileInfo.Name);
|
||||
using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
writer(stream);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(e.ToString());
|
||||
DebugService.Default.WriteMessage(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ public MainPage()
|
||||
|
||||
if (!DesignerProperties.IsInDesignTool)
|
||||
{
|
||||
_debugService = new SilverlightDebugService(_machine, this);
|
||||
_debugService = DebugService.Default;
|
||||
_storageService = new IsolatedStorageService(_machine);
|
||||
_keyboardService = new SilverlightKeyboardService(_machine, this);
|
||||
_gamePortService = new GamePortService(_machine); // not connected
|
||||
@ -49,9 +49,9 @@ public void Dispose()
|
||||
_videoService.Dispose();
|
||||
}
|
||||
|
||||
public void WriteLine(string message)
|
||||
public void WriteMessage(string message)
|
||||
{
|
||||
_debugText.Text += message;
|
||||
_debugText.Text += message + Environment.NewLine;
|
||||
_debugScrollViewer.UpdateLayout();
|
||||
_debugScrollViewer.ScrollToVerticalOffset(double.MaxValue);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ public MainPage()
|
||||
|
||||
if (!DesignerProperties.IsInDesignTool)
|
||||
{
|
||||
_debugService = new SilverlightDebugService(_machine, this);
|
||||
_debugService = DebugService.Default;
|
||||
_storageService = new IsolatedStorageService(_machine);
|
||||
_keyboardService = new SilverlightKeyboardService(_machine, this);
|
||||
_gamePortService = new GamePortService(_machine); // not connected
|
||||
@ -49,9 +49,9 @@ public void Dispose()
|
||||
_videoService.Dispose();
|
||||
}
|
||||
|
||||
public void WriteLine(string message)
|
||||
public void WriteMessage(string message)
|
||||
{
|
||||
_debugText.Text += message;
|
||||
_debugText.Text += message + Environment.NewLine;
|
||||
_debugScrollViewer.UpdateLayout();
|
||||
_debugScrollViewer.ScrollToVerticalOffset(double.MaxValue);
|
||||
}
|
||||
|
@ -16,9 +16,9 @@ public SilverlightDebugService(Machine machine, MainPage page) :
|
||||
_page = page;
|
||||
}
|
||||
|
||||
protected override void OnWriteLine(string message)
|
||||
protected override void OnWriteMessage(string message)
|
||||
{
|
||||
_page.Dispatcher.Post(() => _page.WriteLine(message + Environment.NewLine));
|
||||
_page.Dispatcher.Post(() => _page.WriteMessage(message));
|
||||
}
|
||||
|
||||
private MainPage _page;
|
||||
|
@ -29,6 +29,8 @@
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<RunCodeAnalysis>false</RunCodeAnalysis>
|
||||
<NoWarn>
|
||||
</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
@ -41,6 +43,8 @@
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<RunCodeAnalysis>false</RunCodeAnalysis>
|
||||
<NoWarn>
|
||||
</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>Jellyfish.Virtu.MainApp</StartupObject>
|
||||
|
@ -16,7 +16,7 @@ public MainPage()
|
||||
|
||||
if (!DesignerProperties.GetIsInDesignMode(this))
|
||||
{
|
||||
_debugService = new WpfDebugService(_machine, this);
|
||||
_debugService = DebugService.Default;
|
||||
_storageService = new WpfStorageService(_machine);
|
||||
_keyboardService = new WpfKeyboardService(_machine, this);
|
||||
_gamePortService = new GamePortService(_machine); // not connected
|
||||
@ -50,9 +50,9 @@ public void Dispose()
|
||||
_videoService.Dispose();
|
||||
}
|
||||
|
||||
public void WriteLine(string message)
|
||||
public void WriteMessage(string message)
|
||||
{
|
||||
_debugText.Text += message;
|
||||
_debugText.Text += message + Environment.NewLine;
|
||||
_debugScrollViewer.UpdateLayout();
|
||||
_debugScrollViewer.ScrollToVerticalOffset(double.MaxValue);
|
||||
}
|
||||
|
@ -16,9 +16,9 @@ public WpfDebugService(Machine machine, MainPage page) :
|
||||
_page = page;
|
||||
}
|
||||
|
||||
protected override void OnWriteLine(string message)
|
||||
protected override void OnWriteMessage(string message)
|
||||
{
|
||||
_page.Dispatcher.Post(() => _page.WriteLine(message + Environment.NewLine));
|
||||
_page.Dispatcher.Post(() => _page.WriteMessage(message));
|
||||
}
|
||||
|
||||
private MainPage _page;
|
||||
|
@ -20,7 +20,7 @@ public MainGame() :
|
||||
frameRateCounter.DrawOrder = 1;
|
||||
frameRateCounter.FontName = "Consolas";
|
||||
|
||||
_debugService = new DebugService(_machine);
|
||||
_debugService = DebugService.Default;
|
||||
#if WINDOWS_PHONE
|
||||
_storageService = new IsolatedStorageService(_machine);
|
||||
#else
|
||||
|
Loading…
Reference in New Issue
Block a user