Screwball mode Apple II savestates

This commit is contained in:
goyuken 2015-05-18 00:14:00 +00:00
parent 9024df91f5
commit 6ab8be0a1d
38 changed files with 911 additions and 1891 deletions

View File

@ -119,11 +119,12 @@
</Compile>
<Compile Include="Calculator\TI83LinkPort.cs" />
<Compile Include="Computers\AppleII\AppleII.cs" />
<Compile Include="Computers\AppleII\AppleII.IAudioProvider.cs" />
<Compile Include="Computers\AppleII\AppleII.IEmulator.cs">
<DependentUpon>AppleII.cs</DependentUpon>
</Compile>
<Compile Include="Computers\AppleII\AppleII.IInputPollable.cs">
<DependentUpon>AppleII.cs</DependentUpon>
<DependentUpon>AppleII.cs</DependentUpon>
</Compile>
<Compile Include="Computers\AppleII\AppleII.IMemoryDomains.cs">
<DependentUpon>AppleII.cs</DependentUpon>

View File

@ -15,7 +15,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
[FeatureNotImplemented]
public ISyncSoundProvider SyncSoundProvider
{
get { return _soundService; }
get { return this; }
}
[FeatureNotImplemented]

View File

@ -1,5 +1,8 @@
using BizHawk.Emulation.Common;
using System.IO;
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BizHawk.Emulation.Cores.Computers.AppleII
{
@ -7,39 +10,84 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
{
public bool BinarySaveStatesPreferred { get { return true; } }
private class OtherData
{
public int Frame;
public int LagCount;
public bool IsLagFrame;
public int CurrentDisk;
public JObject Core;
}
private JsonSerializer ser = new JsonSerializer();
[FeatureNotImplemented]
public void SaveStateText(TextWriter writer)
{
var w = new JTokenWriter();
_machine.Serialize(w);
var o = new OtherData
{
Frame = Frame,
LagCount = LagCount,
IsLagFrame = IsLagFrame,
CurrentDisk = CurrentDisk,
Core = (JObject)w.Token,
};
var jw = new JsonTextWriter(writer) { Formatting = Newtonsoft.Json.Formatting.Indented };
ser.Serialize(jw, o);
}
[FeatureNotImplemented]
public void LoadStateText(TextReader reader)
{
var o = (OtherData)ser.Deserialize(reader, typeof(OtherData));
Frame = o.Frame;
LagCount = o.LagCount;
IsLagFrame = o.IsLagFrame;
CurrentDisk = o.CurrentDisk;
var r = new JTokenReader(o.Core);
try
{
_machine = Jellyfish.Virtu.Machine.Deserialize(r);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
throw;
}
// should not be needed.
// InitDisk();
}
public void SaveStateBinary(BinaryWriter writer)
{
/*
writer.Write(Frame);
writer.Write(LagCount);
writer.Write(IsLagFrame);
writer.Write(CurrentDisk);
_machine.SaveState(writer);
*/
}
public void LoadStateBinary(BinaryReader reader)
{
/*
Frame = reader.ReadInt32();
LagCount = reader.ReadInt32();
IsLagFrame = reader.ReadBoolean();
CurrentDisk = reader.ReadInt32();
InitDisk();
_machine.LoadState(reader);
*/
}
public byte[] SaveStateBinary()
{
return new byte[16];
if (_stateBuffer == null)
{
var stream = new MemoryStream();

View File

@ -3,45 +3,23 @@ using Jellyfish.Virtu;
namespace BizHawk.Emulation.Cores.Computers.AppleII
{
public partial class AppleII
public partial class AppleII : IVideoProvider
{
public class BizVideoService : Jellyfish.Virtu.Services.VideoService, IVideoProvider
{
public int[] fb;
private Jellyfish.Virtu.Services.VideoService _V
{ get { return _machine.Video.VideoService; } }
int[] IVideoProvider.GetVideoBuffer() { return fb; }
int[] IVideoProvider.GetVideoBuffer() { return _V.fb; }
// put together, these describe a metric on the screen
// they should define the smallest size that the buffer can be placed inside such that:
// 1. no actual pixel data is lost
// 2. aspect ratio is accurate
int IVideoProvider.VirtualWidth { get { return 560; } }
int IVideoProvider.VirtualHeight { get { return 384; } }
// put together, these describe a metric on the screen
// they should define the smallest size that the buffer can be placed inside such that:
// 1. no actual pixel data is lost
// 2. aspect ratio is accurate
int IVideoProvider.VirtualWidth { get { return 560; } }
int IVideoProvider.VirtualHeight { get { return 384; } }
int IVideoProvider.BufferWidth { get { return 560; } }
int IVideoProvider.BufferHeight { get { return 384; } }
int IVideoProvider.BackgroundColor { get { return 0; } }
int IVideoProvider.BufferWidth { get { return 560; } }
int IVideoProvider.BufferHeight { get { return 384; } }
int IVideoProvider.BackgroundColor { get { return 0; } }
public BizVideoService(Machine machine) :
base(machine)
{
fb = new int[560 * 384];
}
public override void SetFullScreen(bool isFullScreen)
{
}
public override void SetPixel(int x, int y, uint color)
{
int i = 560 * y + x;
fb[i] = fb[i + 560] = (int)color;
}
public override void Update()
{
}
}
}
}

View File

@ -14,7 +14,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
isPorted: true,
isReleased: false
)]
public partial class AppleII : IEmulator, IStatable
public partial class AppleII : IEmulator
{
public AppleII(CoreComm comm, IEnumerable<GameInfo> gameInfoSet, IEnumerable<byte[]> romSet, object settings)
: this(comm, gameInfoSet.First(), romSet.First(), settings)
@ -42,27 +42,11 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
_machine = new Machine(_appleIIRom, _diskIIRom);
var vidService = new BizVideoService(_machine);
_soundService = new BizAudioService(_machine);
var gpService = new Jellyfish.Virtu.Services.GamePortService(_machine);
var kbService = new BizKeyboardService(_machine);
_machine.Services.AddService(typeof(Jellyfish.Virtu.Services.DebugService), new Jellyfish.Virtu.Services.DebugService(_machine));
_machine.Services.AddService(typeof(Jellyfish.Virtu.Services.AudioService), _soundService);
_machine.Services.AddService(typeof(Jellyfish.Virtu.Services.VideoService), vidService);
_machine.Services.AddService(typeof(Jellyfish.Virtu.Services.GamePortService), gpService);
_machine.Services.AddService(typeof(Jellyfish.Virtu.Services.KeyboardService), kbService);
_machine.BizInitialize();
(ServiceProvider as BasicServiceProvider).Register<IVideoProvider>(vidService);
//make a writeable memory stream cloned from the rom.
//for junk.dsk the .dsk is important because it determines the format from that
var ms = new MemoryStream();
ms.Write(_disk1, 0, _disk1.Length);
ms.Position = 0;
bool writeProtected = false; //!!!!!!!!!!!!!!!!!!!
Jellyfish.Virtu.Services.StorageService.LoadFile(ms, stream => _machine.BootDiskII.Drives[0].InsertDisk("junk.dsk", stream, writeProtected));
InitDisk();
SetupMemoryDomains();
}
@ -108,56 +92,31 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
//make a writeable memory stream cloned from the rom.
//for junk.dsk the .dsk is important because it determines the format from that
var ms = new MemoryStream();
ms.Write(_disk1, 0, _disk1.Length);
ms.Position = 0;
bool writeProtected = false; //!!!!!!!!!!!!!!!!!!!
Jellyfish.Virtu.Services.StorageService.LoadFile(ms, stream => _machine.BootDiskII.Drives[0].InsertDisk("junk.dsk", stream, writeProtected));
_machine.BizNewDisk();
bool writeProtected = false; //!!!!!!!!!!!!!!!!!!
_machine.BootDiskII.Drives[0].InsertDisk("junk.dsk", (byte[])_disk1.Clone(), false);
}
private readonly Machine _machine;
private Machine _machine;
private byte[] _disk1;
private readonly byte[] _appleIIRom;
private readonly byte[] _diskIIRom;
private readonly BizAudioService _soundService;
private static readonly ControllerDefinition AppleIIController =
new ControllerDefinition
{
Name = "Apple II Keyboard",
BoolButtons =
{
"Up", "Down", "Left", "Right",
"Tab", "Enter", "Escape", "Back", "Space",
"Ctrl", "Shift", "Caps",
"1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
"A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R",
"S", "T", "U", "V", "W", "X", "Y", "Z",
"Next Disk", "Previous Disk"
}
};
private static readonly ControllerDefinition AppleIIController;
private class BizKeyboardService : KeyboardService
private static readonly List<string> RealButtons = new List<string>(Keyboard.GetKeyNames());
private static readonly List<string> ExtraButtons = new List<string>
{
public BizKeyboardService(Machine _machine) : base(_machine) { }
public override bool IsKeyDown(int key)
{
return key > 0;
}
}
"Previous Disk",
"Next Disk",
};
private class BizAudioService : AudioService, ISyncSoundProvider
static AppleII()
{
public BizAudioService(Machine _machine) : base(_machine) { }
public override void SetVolume(float volume)
{
}
public void DiscardSamples()
{
Reset();
}
AppleIIController = new ControllerDefinition { Name = "Apple IIe Keyboard" };
AppleIIController.BoolButtons.AddRange(RealButtons);
AppleIIController.BoolButtons.AddRange(ExtraButtons);
}
private void FrameAdv(bool render, bool rendersound)
@ -170,9 +129,8 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
{
DecrementDisk();
}
_machine.Buttons = GetButtons();
_machine.BizFrameAdvance();
_machine.BizFrameAdvance(RealButtons.Where(b => Controller[b]));
if (IsLagFrame)
{
LagCount++;
@ -181,59 +139,5 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
Frame++;
}
private Buttons GetButtons()
{
Jellyfish.Virtu.Buttons ret = 0;
if (Controller["Up"]) ret |= Jellyfish.Virtu.Buttons.Up;
if (Controller["Down"]) ret |= Jellyfish.Virtu.Buttons.Down;
if (Controller["Left"]) ret |= Jellyfish.Virtu.Buttons.Left;
if (Controller["Right"]) ret |= Jellyfish.Virtu.Buttons.Right;
if (Controller["Tab"]) ret |= Jellyfish.Virtu.Buttons.Tab;
if (Controller["Enter"]) ret |= Jellyfish.Virtu.Buttons.Enter;
if (Controller["Escape"]) ret |= Jellyfish.Virtu.Buttons.Escape;
if (Controller["Back"]) ret |= Jellyfish.Virtu.Buttons.Back;
if (Controller["Space"]) ret |= Jellyfish.Virtu.Buttons.Space;
if (Controller["Ctrl"]) ret |= Jellyfish.Virtu.Buttons.Ctrl;
if (Controller["Shift"]) ret |= Jellyfish.Virtu.Buttons.Shift;
if (Controller["Caps"]) ret |= Jellyfish.Virtu.Buttons.Caps;
if (Controller["1"]) ret |= Jellyfish.Virtu.Buttons.Key1;
if (Controller["2"]) ret |= Jellyfish.Virtu.Buttons.Key2;
if (Controller["3"]) ret |= Jellyfish.Virtu.Buttons.Key3;
if (Controller["4"]) ret |= Jellyfish.Virtu.Buttons.Key4;
if (Controller["5"]) ret |= Jellyfish.Virtu.Buttons.Key5;
if (Controller["6"]) ret |= Jellyfish.Virtu.Buttons.Key6;
if (Controller["7"]) ret |= Jellyfish.Virtu.Buttons.Key7;
if (Controller["8"]) ret |= Jellyfish.Virtu.Buttons.Key8;
if (Controller["9"]) ret |= Jellyfish.Virtu.Buttons.Key9;
if (Controller["0"]) ret |= Jellyfish.Virtu.Buttons.Key0;
if (Controller["A"]) ret |= Jellyfish.Virtu.Buttons.KeyA;
if (Controller["B"]) ret |= Jellyfish.Virtu.Buttons.KeyB;
if (Controller["C"]) ret |= Jellyfish.Virtu.Buttons.KeyC;
if (Controller["D"]) ret |= Jellyfish.Virtu.Buttons.KeyD;
if (Controller["E"]) ret |= Jellyfish.Virtu.Buttons.KeyE;
if (Controller["F"]) ret |= Jellyfish.Virtu.Buttons.KeyF;
if (Controller["G"]) ret |= Jellyfish.Virtu.Buttons.KeyG;
if (Controller["H"]) ret |= Jellyfish.Virtu.Buttons.KeyH;
if (Controller["I"]) ret |= Jellyfish.Virtu.Buttons.KeyI;
if (Controller["J"]) ret |= Jellyfish.Virtu.Buttons.KeyJ;
if (Controller["K"]) ret |= Jellyfish.Virtu.Buttons.KeyK;
if (Controller["L"]) ret |= Jellyfish.Virtu.Buttons.KeyL;
if (Controller["M"]) ret |= Jellyfish.Virtu.Buttons.KeyM;
if (Controller["N"]) ret |= Jellyfish.Virtu.Buttons.KeyN;
if (Controller["O"]) ret |= Jellyfish.Virtu.Buttons.KeyO;
if (Controller["P"]) ret |= Jellyfish.Virtu.Buttons.KeyP;
if (Controller["Q"]) ret |= Jellyfish.Virtu.Buttons.KeyQ;
if (Controller["R"]) ret |= Jellyfish.Virtu.Buttons.KeyR;
if (Controller["S"]) ret |= Jellyfish.Virtu.Buttons.KeyS;
if (Controller["T"]) ret |= Jellyfish.Virtu.Buttons.KeyT;
if (Controller["U"]) ret |= Jellyfish.Virtu.Buttons.KeyU;
if (Controller["V"]) ret |= Jellyfish.Virtu.Buttons.KeyV;
if (Controller["W"]) ret |= Jellyfish.Virtu.Buttons.KeyW;
if (Controller["X"]) ret |= Jellyfish.Virtu.Buttons.KeyX;
if (Controller["Y"]) ret |= Jellyfish.Virtu.Buttons.KeyY;
if (Controller["Z"]) ret |= Jellyfish.Virtu.Buttons.KeyZ;
return ret;
}
}
}

View File

@ -130,7 +130,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
prg_bank_mask_8k = Cart.prg_size / 8 - 1;
chr_bank_mask_1k = Cart.chr_size - 1;
chr_bank_mask_1k = 0xff; // Cart.chr_size - 1;
SetMirrorType(EMirrorType.Vertical);

View File

@ -4,18 +4,17 @@ namespace Jellyfish.Virtu
{
public sealed class Cassette : MachineComponent
{
public Cassette() { }
public Cassette(Machine machine) :
base(machine)
{
}
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
public bool ReadInput()
{
return false;
}
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
public void ToggleOutput()
{
}

View File

@ -7,6 +7,7 @@ namespace Jellyfish.Virtu
{
public sealed partial class Cpu : MachineComponent
{
public Cpu() { }
public Cpu(Machine machine) :
base(machine)
{
@ -169,44 +170,6 @@ namespace Jellyfish.Virtu
}
}
public override void LoadState(BinaryReader reader, Version version)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
Is65C02 = reader.ReadBoolean();
IsThrottled = reader.ReadBoolean();
Multiplier = reader.ReadInt32();
RA = reader.ReadInt32();
RX = reader.ReadInt32();
RY = reader.ReadInt32();
RS = reader.ReadInt32();
RP = reader.ReadInt32();
RPC = reader.ReadInt32();
}
public override void SaveState(BinaryWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
writer.Write(Is65C02);
writer.Write(IsThrottled);
writer.Write(Multiplier);
writer.Write(RA);
writer.Write(RX);
writer.Write(RY);
writer.Write(RS);
writer.Write(RP);
writer.Write(RPC);
}
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "A = 0x{0:X2} X = 0x{1:X2} Y = 0x{2:X2} P = 0x{3:X2} S = 0x01{4:X2} PC = 0x{5:X4} EA = 0x{6:X4} CC = {7}",
@ -214,14 +177,15 @@ namespace Jellyfish.Virtu
}
public int Execute()
{
CC = 0;
{
CC = 0;
OpCode = _memory.Read(RPC);
RPC = (RPC + 1) & 0xFFFF;
_executeOpCode[OpCode]();
Cycles += CC;
return CC;
return CC;
}
#region Core Operand Actions
@ -3238,6 +3202,7 @@ namespace Jellyfish.Virtu
}
#endregion
[Newtonsoft.Json.JsonIgnore]
public bool Is65C02 { get { return _is65C02; } set { _is65C02 = value; _executeOpCode = _is65C02 ? ExecuteOpCode65C02 : ExecuteOpCode65N02; } }
public bool IsThrottled { get; set; }
public int Multiplier { get; set; }

View File

@ -6,8 +6,8 @@ namespace Jellyfish.Virtu
{
private const int OpCodeCount = 256;
private readonly Action[] ExecuteOpCode65N02;
private readonly Action[] ExecuteOpCode65C02;
private Action[] ExecuteOpCode65N02;
private Action[] ExecuteOpCode65C02;
private const int PC = 0x01;
private const int PZ = 0x02;

View File

@ -8,6 +8,7 @@ namespace Jellyfish.Virtu
{
public abstract class Disk525
{
public Disk525() { }
protected Disk525(string name, byte[] data, bool isWriteProtected)
{
Name = name;
@ -15,43 +16,13 @@ namespace Jellyfish.Virtu
IsWriteProtected = isWriteProtected;
}
public static Disk525 CreateDisk(string name, Stream stream, bool isWriteProtected)
public static Disk525 CreateDisk(string name, byte[] data, bool isWriteProtected)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
if (name.EndsWith(".do", StringComparison.OrdinalIgnoreCase) ||
name.EndsWith(".dsk", StringComparison.OrdinalIgnoreCase)) // assumes dos sector skew
{
return new DiskDsk(name, stream, isWriteProtected, SectorSkew.Dos);
}
else if (name.EndsWith(".nib", StringComparison.OrdinalIgnoreCase))
{
return new DiskNib(name, stream, isWriteProtected);
}
else if (name.EndsWith(".po", StringComparison.OrdinalIgnoreCase))
{
return new DiskDsk(name, stream, isWriteProtected, SectorSkew.ProDos);
}
return null;
}
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "version")]
public static Disk525 LoadState(BinaryReader reader, Version version)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
string name = reader.ReadString();
var dataSize = reader.ReadInt32();
var data = reader.ReadBytes(dataSize);
bool isWriteProtected = reader.ReadBoolean();
if (name.EndsWith(".do", StringComparison.OrdinalIgnoreCase) ||
name.EndsWith(".dsk", StringComparison.OrdinalIgnoreCase)) // assumes dos sector skew
{
@ -69,19 +40,6 @@ namespace Jellyfish.Virtu
return null;
}
public void SaveState(BinaryWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
writer.Write(Name);
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);

View File

@ -8,6 +8,7 @@ namespace Jellyfish.Virtu
public sealed class DiskDsk : Disk525
{
public DiskDsk() { }
public DiskDsk(string name, byte[] data, bool isWriteProtected, SectorSkew sectorSkew) :
base(name, data, isWriteProtected)
{

View File

@ -4,11 +4,13 @@ using System.Diagnostics.CodeAnalysis;
using System.IO;
using Jellyfish.Library;
using Jellyfish.Virtu.Services;
using System.Collections.Generic;
namespace Jellyfish.Virtu
{
public sealed class DiskIIController : PeripheralCard
{
public DiskIIController() { }
public DiskIIController(Machine machine, byte[] diskIIRom) :
base(machine)
{
@ -16,7 +18,7 @@ namespace Jellyfish.Virtu
Drive1 = new DiskIIDrive(machine);
Drive2 = new DiskIIDrive(machine);
Drives = new Collection<DiskIIDrive> { Drive1, Drive2 };
Drives = new List<DiskIIDrive> { Drive1, Drive2 };
BootDrive = Drive1;
}
@ -32,50 +34,6 @@ namespace Jellyfish.Virtu
_writeMode = false;
}
public override void LoadState(BinaryReader reader, Version version)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
_latch = reader.ReadInt32();
_phaseStates = reader.ReadInt32();
_motorOn = reader.ReadBoolean();
_driveNumber = reader.ReadInt32();
_loadMode = reader.ReadBoolean();
_writeMode = reader.ReadBoolean();
_driveSpin = reader.ReadBoolean();
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)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
writer.Write(_latch);
writer.Write(_phaseStates);
writer.Write(_motorOn);
writer.Write(_driveNumber);
writer.Write(_loadMode);
writer.Write(_writeMode);
writer.Write(_driveSpin);
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")]
public override int ReadIoRegionC0C0(int address)
{
@ -263,7 +221,7 @@ namespace Jellyfish.Virtu
public DiskIIDrive Drive1 { get; private set; }
public DiskIIDrive Drive2 { get; private set; }
public Collection<DiskIIDrive> Drives { get; private set; }
public List<DiskIIDrive> Drives { get; private set; }
public DiskIIDrive BootDrive { get; private set; }

View File

@ -7,6 +7,7 @@ namespace Jellyfish.Virtu
{
public sealed class DiskIIDrive : MachineComponent
{
public DiskIIDrive() { }
public DiskIIDrive(Machine machine) :
base(machine)
{
@ -16,60 +17,11 @@ namespace Jellyfish.Virtu
DriveArmStepDelta[3] = new int[] { 0, 1, 0, 1, -1, 0, -1, 0, 0, 1, 0, 1, -1, 0, -1, 0 }; // phase 3
}
public override void LoadState(BinaryReader reader, Version version)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
_trackLoaded = reader.ReadBoolean();
_trackChanged = reader.ReadBoolean();
_trackNumber = reader.ReadInt32();
_trackOffset = reader.ReadInt32();
if (_trackLoaded)
{
reader.Read(_trackData, 0, _trackData.Length);
}
if (reader.ReadBoolean())
{
DebugService.WriteMessage("Loading machine '{0}'", typeof(Disk525).Name);
_disk = Disk525.LoadState(reader, version);
}
else
{
_disk = null;
}
}
public override void SaveState(BinaryWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
writer.Write(_trackLoaded);
writer.Write(_trackChanged);
writer.Write(_trackNumber);
writer.Write(_trackOffset);
if (_trackLoaded)
{
writer.Write(_trackData);
}
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)
public void InsertDisk(string name, byte[] data, bool isWriteProtected)
{
DebugService.WriteMessage("Inserting disk '{0}'", name);
FlushTrack();
_disk = Disk525.CreateDisk(name, stream, isWriteProtected);
_disk = Disk525.CreateDisk(name, data, isWriteProtected);
_trackLoaded = false;
}
@ -116,7 +68,9 @@ namespace Jellyfish.Virtu
return data;
}
return _random.Next(0x01, 0xFF);
return 0x80;
// TODO: WTF was this
//return _random.Next(0x01, 0xFF);
}
public void Write(int data)
@ -152,13 +106,14 @@ namespace Jellyfish.Virtu
}
}
[Newtonsoft.Json.JsonIgnore]
public bool IsWriteProtected { get { return _disk.IsWriteProtected; } }
private const int TrackNumberMax = 0x44;
private const int PhaseCount = 4;
private readonly int[][] DriveArmStepDelta = new int[PhaseCount][];
private int[][] DriveArmStepDelta = new int[PhaseCount][];
private bool _trackLoaded;
private bool _trackChanged;
@ -166,7 +121,5 @@ namespace Jellyfish.Virtu
private int _trackOffset;
private byte[] _trackData = new byte[Disk525.TrackSize];
private Disk525 _disk;
private Random _random = new Random();
}
}

View File

@ -6,6 +6,7 @@ namespace Jellyfish.Virtu
{
public sealed class DiskNib : Disk525
{
public DiskNib() { }
public DiskNib(string name, byte[] data, bool isWriteProtected) :
base(name, data, isWriteProtected)
{

View File

@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
using System.Reflection;
namespace Jellyfish.Virtu
{
public class ArrayConverter : JsonConverter
{
// JSON.NET cannot, when reading, use PreserveReferencesHandling on arrays, although it fully supports it on writing.
// stupid decision, but there you have it. we need that to work here.
// as an added "bonus", this disables base64ing of byte[] arrays.
// TODO: optimize the byte/short/int cases.
// TODO: on serialization, the type of the object is available, but is the expected type (ie, the one that we'll be fed during deserialization) available?
// need this to at least detect covariance cases...
public override bool CanConvert(Type objectType)
{
if (!typeof(Array).IsAssignableFrom(objectType))
return false;
if (objectType.GetArrayRank() > 1)
throw new NotImplementedException();
return true;
}
public override bool CanRead { get { return true; } }
public override bool CanWrite { get { return true; } }
int nextRef = 1;
Dictionary<object, int> refs = new Dictionary<object, int>();
Dictionary<int, Array> readrefs = new Dictionary<int, Array>();
private static void ReadExpectType(JsonReader reader, JsonToken expected)
{
if (!reader.Read())
throw new InvalidOperationException();
if (reader.TokenType != expected)
throw new InvalidOperationException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
if (reader.TokenType != JsonToken.StartObject)
throw new InvalidOperationException();
ReadExpectType(reader, JsonToken.PropertyName);
if (reader.Value.ToString() != "$myRef")
throw new InvalidOperationException();
ReadExpectType(reader, JsonToken.Integer);
int myRef = Convert.ToInt32(reader.Value);
if (!reader.Read())
throw new InvalidOperationException();
if (reader.TokenType == JsonToken.EndObject)
return readrefs[myRef];
else if (reader.TokenType != JsonToken.PropertyName || reader.Value.ToString() != "$myCount")
throw new InvalidOperationException();
ReadExpectType(reader, JsonToken.Integer);
int myCount = Convert.ToInt32(reader.Value);
ReadExpectType(reader, JsonToken.PropertyName);
if (reader.Value.ToString() != "$myVals")
throw new InvalidOperationException();
ReadExpectType(reader, JsonToken.StartArray);
var elementType = objectType.GetElementType();
Array ret = Array.CreateInstance(elementType, myCount);
for (int i = 0; i < myCount; i++)
{
if (!reader.Read())
throw new InvalidOperationException();
ret.SetValue(serializer.Deserialize(reader, elementType), i);
}
ReadExpectType(reader, JsonToken.EndArray);
ReadExpectType(reader, JsonToken.EndObject);
readrefs[myRef] = ret;
return ret;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
int myRef;
if (refs.TryGetValue(value, out myRef))
{
writer.WriteStartObject();
writer.WritePropertyName("$myRef");
writer.WriteValue(myRef);
writer.WriteEndObject();
}
else
{
myRef = nextRef++;
refs.Add(value, myRef);
writer.WriteStartObject();
writer.WritePropertyName("$myRef");
writer.WriteValue(myRef);
writer.WritePropertyName("$myCount"); // not needed, but avoids us having to make some sort of temp structure on deserialization
writer.WriteValue(((Array)value).Length);
writer.WritePropertyName("$myVals");
writer.WriteStartArray();
var elementType = value.GetType().GetElementType();
foreach (object o in (Array)value)
{
serializer.Serialize(writer, o, elementType);
}
writer.WriteEndArray();
writer.WriteEndObject();
}
}
}
public class DelegateConverter : JsonConverter
{
// caveats: if used on anonymous delegates and/or closures, brittle to name changes in the generated classes and methods
// brittle to type name changes in general
// must be serialized in tree with any real classes referred to by closures
// TODO: much of this could be made somewhat smarter and more resilient
public override bool CanConvert(Type objectType)
{
return typeof(Delegate).IsAssignableFrom(objectType);
}
public override bool CanRead { get { return true; } }
public override bool CanWrite { get { return true; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var slug = serializer.Deserialize<Slug>(reader);
if (slug == null)
return null;
return slug.GetDelegate();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var slug = new Slug((Delegate)value);
serializer.Serialize(writer, slug);
}
private class Slug
{
public Type DelegateType;
public Type MethodDeclaringType;
public string MethodName;
public List<Type> MethodParameters;
public object Target;
public Delegate GetDelegate()
{
var mi = MethodDeclaringType.GetMethod(
MethodName,
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static,
null,
MethodParameters.ToArray(),
null);
return Delegate.CreateDelegate(DelegateType, Target, mi);
}
public Slug() { }
public Slug(Delegate d)
{
DelegateType = d.GetType();
MethodDeclaringType = d.Method.DeclaringType;
MethodName = d.Method.Name;
MethodParameters = d.Method.GetParameters().Select(p => p.ParameterType).ToList();
Target = d.Target;
}
}
}
}

View File

@ -8,15 +8,31 @@ namespace Jellyfish.Virtu
{
public sealed class GamePort : MachineComponent
{
// TODO: ressurect this
public bool ReadButton0() { return false; }
public bool ReadButton1() { return false; }
public bool ReadButton2() { return false; }
public bool Paddle0Strobe { get { return false; } }
public bool Paddle1Strobe { get { return false; } }
public bool Paddle2Strobe { get { return false; } }
public bool Paddle3Strobe { get { return false; } }
public void TriggerTimers() { }
public GamePort() { }
public GamePort(Machine machine) :
base(machine)
{
/*
_resetPaddle0StrobeEvent = ResetPaddle0StrobeEvent; // cache delegates; avoids garbage
_resetPaddle1StrobeEvent = ResetPaddle1StrobeEvent;
_resetPaddle2StrobeEvent = ResetPaddle2StrobeEvent;
_resetPaddle3StrobeEvent = ResetPaddle3StrobeEvent;
*/
}
/*
public override void Initialize()
{
_keyboardService = Machine.Services.GetService<KeyboardService>();
@ -46,137 +62,7 @@ namespace Jellyfish.Virtu
Button2TouchHeight = 0.25f;
Button2TouchOrder = 1;
}
public override void LoadState(BinaryReader reader, Version version)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
InvertPaddles = reader.ReadBoolean();
SwapPaddles = reader.ReadBoolean();
UseShiftKeyMod = reader.ReadBoolean();
JoystickDeadZone = reader.ReadSingle();
UseKeyboard = reader.ReadBoolean();
Joystick0UpLeftKey = reader.ReadInt32();
Joystick0UpKey = reader.ReadInt32();
Joystick0UpRightKey = reader.ReadInt32();
Joystick0LeftKey = reader.ReadInt32();
Joystick0RightKey = reader.ReadInt32();
Joystick0DownLeftKey = reader.ReadInt32();
Joystick0DownKey = reader.ReadInt32();
Joystick0DownRightKey = reader.ReadInt32();
Joystick1UpLeftKey = reader.ReadInt32();
Joystick1UpKey = reader.ReadInt32();
Joystick1UpRightKey = reader.ReadInt32();
Joystick1LeftKey = reader.ReadInt32();
Joystick1RightKey = reader.ReadInt32();
Joystick1DownLeftKey = reader.ReadInt32();
Joystick1DownKey = reader.ReadInt32();
Joystick1DownRightKey = reader.ReadInt32();
Button0Key = reader.ReadInt32();
Button1Key = reader.ReadInt32();
Button2Key = reader.ReadInt32();
UseTouch = reader.ReadBoolean();
Joystick0TouchX = reader.ReadSingle();
Joystick0TouchY = reader.ReadSingle();
Joystick0TouchWidth = reader.ReadSingle();
Joystick0TouchHeight = reader.ReadSingle();
Joystick0TouchOrder = reader.ReadInt32();
Joystick0TouchRadius = reader.ReadSingle();
Joystick0TouchKeepLast = reader.ReadBoolean();
Joystick1TouchX = reader.ReadSingle();
Joystick1TouchY = reader.ReadSingle();
Joystick1TouchWidth = reader.ReadSingle();
Joystick1TouchHeight = reader.ReadSingle();
Joystick1TouchOrder = reader.ReadInt32();
Joystick1TouchRadius = reader.ReadSingle();
Joystick1TouchKeepLast = reader.ReadBoolean();
Button0TouchX = reader.ReadSingle();
Button0TouchY = reader.ReadSingle();
Button0TouchWidth = reader.ReadSingle();
Button0TouchHeight = reader.ReadSingle();
Button0TouchOrder = reader.ReadInt32();
Button1TouchX = reader.ReadSingle();
Button1TouchY = reader.ReadSingle();
Button1TouchWidth = reader.ReadSingle();
Button1TouchHeight = reader.ReadSingle();
Button1TouchOrder = reader.ReadInt32();
Button2TouchX = reader.ReadSingle();
Button2TouchY = reader.ReadSingle();
Button2TouchWidth = reader.ReadSingle();
Button2TouchHeight = reader.ReadSingle();
Button2TouchOrder = reader.ReadInt32();
}
public override void SaveState(BinaryWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
writer.Write(InvertPaddles);
writer.Write(SwapPaddles);
writer.Write(UseShiftKeyMod);
writer.Write(JoystickDeadZone);
writer.Write(UseKeyboard);
writer.Write(Joystick0UpLeftKey);
writer.Write(Joystick0UpKey);
writer.Write(Joystick0UpRightKey);
writer.Write(Joystick0LeftKey);
writer.Write(Joystick0RightKey);
writer.Write(Joystick0DownLeftKey);
writer.Write(Joystick0DownKey);
writer.Write(Joystick0DownRightKey);
writer.Write(Joystick1UpLeftKey);
writer.Write(Joystick1UpKey);
writer.Write(Joystick1UpRightKey);
writer.Write(Joystick1LeftKey);
writer.Write(Joystick1RightKey);
writer.Write(Joystick1DownLeftKey);
writer.Write(Joystick1DownKey);
writer.Write(Joystick1DownRightKey);
writer.Write(Button0Key);
writer.Write(Button1Key);
writer.Write(Button2Key);
writer.Write(UseTouch);
writer.Write(Joystick0TouchX);
writer.Write(Joystick0TouchY);
writer.Write(Joystick0TouchWidth);
writer.Write(Joystick0TouchHeight);
writer.Write(Joystick0TouchOrder);
writer.Write(Joystick0TouchRadius);
writer.Write(Joystick0TouchKeepLast);
writer.Write(Joystick1TouchX);
writer.Write(Joystick1TouchY);
writer.Write(Joystick1TouchWidth);
writer.Write(Joystick1TouchHeight);
writer.Write(Joystick1TouchOrder);
writer.Write(Joystick1TouchRadius);
writer.Write(Joystick1TouchKeepLast);
writer.Write(Button0TouchX);
writer.Write(Button0TouchY);
writer.Write(Button0TouchWidth);
writer.Write(Button0TouchHeight);
writer.Write(Button0TouchOrder);
writer.Write(Button1TouchX);
writer.Write(Button1TouchY);
writer.Write(Button1TouchWidth);
writer.Write(Button1TouchHeight);
writer.Write(Button1TouchOrder);
writer.Write(Button2TouchX);
writer.Write(Button2TouchY);
writer.Write(Button2TouchWidth);
writer.Write(Button2TouchHeight);
writer.Write(Button2TouchOrder);
}
public bool ReadButton0()
{
return (_gamePortService.IsButton0Down || _keyboardService.IsOpenAppleKeyDown ||
@ -365,6 +251,6 @@ namespace Jellyfish.Virtu
private Action _resetPaddle3StrobeEvent;
private KeyboardService _keyboardService;
private GamePortService _gamePortService;
private GamePortService _gamePortService;*/
}
}

View File

@ -1,122 +1,370 @@
using System;
using System.IO;
using System.Collections.Generic;
using Jellyfish.Virtu.Services;
using System.ComponentModel;
using System.Linq;
namespace Jellyfish.Virtu
{
[Flags]
internal enum Keys : ulong
{
// https://archive.org/stream/Apple_IIe_Technical_Reference_Manual
// 56 basic keys as described in the reference manual
[Description("Delete")]
Delete = 1UL,
[Description("Left")]
Left = 2UL,
[Description("Tab")]
Tab = 4UL,
[Description("Down")]
Down = 8UL,
[Description("Up")]
Up = 16UL,
[Description("Return")]
Return = 32UL,
[Description("Right")]
Right = 64UL,
[Description("Escape")]
Escape = 128UL,
[Description("Space")]
Space = 256UL,
[Description("'")]
Apostrophe = 512UL,
[Description(",")]
Comma = 1024UL,
[Description("-")]
Dash = 2048UL,
[Description(".")]
Period = 4096UL,
[Description("/")]
Slash = 8192UL,
[Description("0")]
Key0 = 16384UL,
[Description("1")]
Key1 = 32768UL,
[Description("2")]
Key2 = 65536UL,
[Description("3")]
Key3 = 131072UL,
[Description("4")]
Key4 = 262144UL,
[Description("5")]
Key5 = 524288UL,
[Description("6")]
Key6 = 1048576UL,
[Description("7")]
Key7 = 2097152UL,
[Description("8")]
Key8 = 4194304UL,
[Description("9")]
Key9 = 8388608UL,
[Description(";")]
Semicolon = 16777216UL,
[Description("=")]
Equals = 33554432UL,
[Description("[")]
LeftBracket = 67108864UL,
[Description("\\")]
Backslash = 134217728UL,
[Description("]")]
RightBracket = 268435456UL,
[Description("`")]
Backtick = 536870912UL,
[Description("A")]
A = 1073741824UL,
[Description("B")]
B = 2147483648UL,
[Description("C")]
C = 4294967296UL,
[Description("D")]
D = 8589934592UL,
[Description("E")]
E = 17179869184UL,
[Description("F")]
F = 34359738368UL,
[Description("G")]
G = 68719476736UL,
[Description("H")]
H = 137438953472UL,
[Description("I")]
I = 274877906944UL,
[Description("J")]
J = 549755813888UL,
[Description("K")]
K = 1099511627776UL,
[Description("L")]
L = 2199023255552UL,
[Description("M")]
M = 4398046511104UL,
[Description("N")]
N = 8796093022208UL,
[Description("O")]
O = 17592186044416UL,
[Description("P")]
P = 35184372088832UL,
[Description("Q")]
Q = 70368744177664UL,
[Description("R")]
R = 140737488355328UL,
[Description("S")]
S = 281474976710656UL,
[Description("T")]
T = 562949953421312UL,
[Description("U")]
U = 1125899906842624UL,
[Description("V")]
V = 2251799813685248UL,
[Description("W")]
W = 4503599627370496UL,
[Description("X")]
X = 9007199254740992UL,
[Description("Y")]
Y = 18014398509481984UL,
[Description("Z")]
Z = 36028797018963968UL,
// three modifier keys, cannot be read directly
[Description("Control")]
Control = 72057594037927936UL,
[Description("Shift")]
Shift = 144115188075855872UL,
[Description("Caps Lock")]
CapsLock = 288230376151711744UL,
// three special keys
[Description("White Apple")]
WhiteApple = 576460752303423488UL, // connected to GAME1
[Description("Black Apple")]
BlackApple = 1152921504606846976UL, // connected to GAME2
[Description("Reset")]
Reset = 2305843009213693952UL,
}
public sealed class Keyboard : MachineComponent
{
public Keyboard(Machine machine) :
private static readonly uint[] KeyAsciiData = new uint[]
{
// https://archive.org/stream/Apple_IIe_Technical_Reference_Manual#page/n47/mode/2up
// 0xNNCCSSBB normal, control, shift both
// keys in same order as above
0x7f7f7f7f,
0x08080808,
0x09090909,
0x0a0a0a0a,
0x0b0b0b0b,
0x0d0d0d0d,
0x15151515,
0x1b1b1b1b,
0x20202020,
0x27272222,
0x2c2c3c3c,
0x2d1f5f1f,
0x2e2e3e3e,
0x2f2f3f3f,
0x30302929, // 0
0x31312121,
0x32004000,
0x33332323,
0x34342424,
0x35352525,
0x361e5e1e,
0x37372626,
0x38382a2a,
0x39392828, // 9
0x3b3b3a3a,
0x3d3d2b2b,
0x5b1b7b1b,
0x5c1c7c1c,
0x5d1d7d1d,
0x60607e7e,
0x61014101, // a
0x62024202,
0x63034303,
0x64044404,
0x65054505,
0x66064606,
0x67074707,
0x68084808,
0x69094909,
0x6a0a4a0a,
0x6b0b4b0b,
0x6c0c4c0c,
0x6d0d4d0d,
0x6e0e4e0e,
0x6f0f4f0f,
0x70105010,
0x71115111,
0x72125212,
0x73135313,
0x74145414,
0x75155515,
0x76165616,
0x77175717,
0x78185818,
0x79195919,
0x7a1a5a1a, // z
};
/// <summary>
///
/// </summary>
/// <param name="key">0 - 55</param>
/// <param name="control"></param>
/// <param name="shift"></param>
/// <returns></returns>
private static int KeyToAscii(int key, bool control, bool shift)
{
int s = control ? (shift ? 0 : 16) : (shift ? 8 : 24);
return (int)(KeyAsciiData[key] >> s);
}
private static Dictionary<string, Keys> DescriptionsToKeys = new Dictionary<string, Keys>();
static Keyboard()
{
for (int i = 0; i < 62; i++)
{
// http://stackoverflow.com/questions/2650080/how-to-get-c-sharp-enum-description-from-value
Keys value = (Keys)(1UL << i);
var fi = typeof(Keys).GetField(value.ToString());
var attr = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
string name = attr[0].Description;
DescriptionsToKeys[name] = value;
}
}
public static IEnumerable<string> GetKeyNames()
{
return DescriptionsToKeys.Keys.ToList();
}
private static Keys FromStrings(IEnumerable<string> keynames)
{
Keys ret = 0;
foreach (string s in keynames)
{
ret |= DescriptionsToKeys[s];
}
return ret;
}
public Keyboard() { }
public Keyboard(Machine machine) :
base(machine)
{
}
public override void Initialize()
{
_keyboardService = Machine.Services.GetService<KeyboardService>();
UseGamePort = true; // Raster Blaster
Button2Key = ' ';
}
public override void LoadState(BinaryReader reader, Version version)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
/// <summary>
/// Call this at 60hz with all of the currently pressed keys
/// </summary>
/// <param name="keys"></param>
public void SetKeys(IEnumerable<string> keynames)
{
Keys keys = FromStrings(keynames);
DisableResetKey = reader.ReadBoolean();
if (keys.HasFlag(Keys.WhiteApple)) { } // TODO: set GAME1
if (keys.HasFlag(Keys.BlackApple)) { } // TODO: set GAME2
UseGamePort = reader.ReadBoolean();
Joystick0UpLeftKey = reader.ReadInt32();
Joystick0UpKey = reader.ReadInt32();
Joystick0UpRightKey = reader.ReadInt32();
Joystick0LeftKey = reader.ReadInt32();
Joystick0RightKey = reader.ReadInt32();
Joystick0DownLeftKey = reader.ReadInt32();
Joystick0DownKey = reader.ReadInt32();
Joystick0DownRightKey = reader.ReadInt32();
Joystick1UpLeftKey = reader.ReadInt32();
Joystick1UpKey = reader.ReadInt32();
Joystick1UpRightKey = reader.ReadInt32();
Joystick1LeftKey = reader.ReadInt32();
Joystick1RightKey = reader.ReadInt32();
Joystick1DownLeftKey = reader.ReadInt32();
Joystick1DownKey = reader.ReadInt32();
Joystick1DownRightKey = reader.ReadInt32();
Button0Key = reader.ReadUInt64();
Button1Key = reader.ReadInt32();
Button2Key = reader.ReadInt32();
}
if (keys.HasFlag(Keys.Reset) && keys.HasFlag(Keys.Control)) { } // TODO: reset console
public override void SaveState(BinaryWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
bool control = keys.HasFlag(Keys.Control);
bool shift = keys.HasFlag(Keys.Shift);
writer.Write(DisableResetKey);
bool caps = keys.HasFlag(Keys.CapsLock);
if (caps && !CurrentCapsLockState) // leading edge: toggle capslock
{
CapsActive ^= true;
}
CurrentCapsLockState = caps;
shift ^= CapsActive;
// work with only the first 56 real keys
long k = (long)keys & 0xffffffffffffffL;
IsAnyKeyDown = k != 0;
if (!IsAnyKeyDown)
{
CurrentKeyPressed = -1;
return;
}
// TODO: on real hardware, multiple keys pressed in physical would cause a conflict
// that would be somehow resolved by the scan pattern. we don't emulate that.
// instead, just arbitrarily choose the lowest key in our list
// BSF
int NewKeyPressed = 0;
while ((k & 1) == 0)
{
k >>= 1;
NewKeyPressed++;
}
if (NewKeyPressed != CurrentKeyPressed)
{
// strobe, start new repeat cycle
Strobe = true;
Latch = KeyToAscii(NewKeyPressed, control, shift);
FramesToRepeat = KeyRepeatStart;
}
else
{
// check for repeat
FramesToRepeat--;
if (FramesToRepeat == 0)
{
Strobe = true;
Latch = KeyToAscii(NewKeyPressed, control, shift);
FramesToRepeat = KeyRepeatRate;
}
}
CurrentKeyPressed = NewKeyPressed;
}
writer.Write(UseGamePort);
writer.Write(Joystick0UpLeftKey);
writer.Write(Joystick0UpKey);
writer.Write(Joystick0UpRightKey);
writer.Write(Joystick0LeftKey);
writer.Write(Joystick0RightKey);
writer.Write(Joystick0DownLeftKey);
writer.Write(Joystick0DownKey);
writer.Write(Joystick0DownRightKey);
writer.Write(Joystick1UpLeftKey);
writer.Write(Joystick1UpKey);
writer.Write(Joystick1UpRightKey);
writer.Write(Joystick1LeftKey);
writer.Write(Joystick1RightKey);
writer.Write(Joystick1DownLeftKey);
writer.Write(Joystick1DownKey);
writer.Write(Joystick1DownRightKey);
writer.Write(Button0Key);
writer.Write(Button1Key);
writer.Write(Button2Key);
}
public void ResetStrobe()
{
Strobe = false;
}
public bool DisableResetKey { get; set; }
public bool UseGamePort { get; set; }
public int Joystick0UpLeftKey { get; set; }
public int Joystick0UpKey { get; set; }
public int Joystick0UpRightKey { get; set; }
public int Joystick0LeftKey { get; set; }
public int Joystick0RightKey { get; set; }
public int Joystick0DownLeftKey { get; set; }
public int Joystick0DownKey { get; set; }
public int Joystick0DownRightKey { get; set; }
public int Joystick1UpLeftKey { get; set; }
public int Joystick1UpKey { get; set; }
public int Joystick1UpRightKey { get; set; }
public int Joystick1LeftKey { get; set; }
public int Joystick1RightKey { get; set; }
public int Joystick1DownLeftKey { get; set; }
public int Joystick1DownKey { get; set; }
public int Joystick1DownRightKey { get; set; }
public ulong Button0Key { get; set; }
public int Button1Key { get; set; }
public int Button2Key { get; set; }
public bool IsAnyKeyDown { get { return _keyboardService.IsAnyKeyDown; } }
public bool IsShiftKeyDown { get { return _keyboardService.IsShiftKeyDown; } }
public bool IsControlKeyDown { get { return _keyboardService.IsControlKeyDown; } }
public int Latch { get { return _latch; } set { _latch = value; Strobe = true; } }
/// <summary>
/// true if any of the 56 basic keys are pressed
/// </summary>
public bool IsAnyKeyDown { get; private set; }
/// <summary>
/// the currently latched key; 7 bits.
/// </summary>
public int Latch { get; private set; }
public bool Strobe { get; private set; }
private KeyboardService _keyboardService;
/// <summary>
/// true if caps lock is active
/// </summary>
public bool CapsActive { get; private set; }
private int _latch;
private bool CurrentCapsLockState;
/// <summary>
/// 0-55, -1 = none
/// </summary>
private int CurrentKeyPressed;
private int FramesToRepeat;
private const int KeyRepeatRate = 6; // 10hz
private const int KeyRepeatStart = 40; // ~666ms?
}
}

View File

@ -1,391 +1,190 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading;
using Jellyfish.Virtu.Services;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace Jellyfish.Virtu
{
public enum MachineState { Stopped = 0, Starting, Running, Pausing, Paused, Stopping }
public enum Buttons : ulong
{
Up = 0x0000000000001,
Down = 0x0000000000002,
Left = 0x0000000000004,
Right = 0x0000000000008,
Tab = 0x0000000000010,
Enter = 0x0000000000020,
Escape = 0x0000000000040,
Back = 0x0000000000080,
Space = 0x0000000000100,
Ctrl = 0x0000000000200,
Shift = 0x0000000000400,
Caps = 0x0000000000800,
Key1 = 0x0000000001000,
Key2 = 0x0000000002000,
Key3 = 0x0000000004000,
Key4 = 0x0000000008000,
Key5 = 0x0000000010000,
Key6 = 0x0000000020000,
Key7 = 0x0000000040000,
Key8 = 0x0000000080000,
Key9 = 0x0000000100000,
Key0 = 0x0000000200000,
KeyA = 0x0000001000000,
KeyB = 0x0000002000000,
KeyC = 0x0000004000000,
KeyD = 0x0000008000000,
KeyE = 0x0000010000000,
KeyF = 0x0000020000000,
KeyG = 0x0000040000000,
KeyH = 0x0000080000000,
KeyI = 0x0000100000000,
KeyJ = 0x0000200000000,
KeyK = 0x0000400000000,
KeyL = 0x0000800000000,
KeyM = 0x0001000000000,
KeyN = 0x0002000000000,
KeyO = 0x0004000000000,
KeyP = 0x0008000000000,
KeyQ = 0x0010000000000,
KeyR = 0x0020000000000,
KeyS = 0x0040000000000,
KeyT = 0x0080000000000,
KeyU = 0x0100000000000,
KeyV = 0x0200000000000,
KeyW = 0x0400000000000,
KeyX = 0x0800000000000,
KeyY = 0x1000000000000,
KeyZ = 0x2000000000000
}
public sealed class Machine : IDisposable
{
public Machine(byte[] appleIIe, byte[] diskIIRom)
{
public sealed class Machine : IDisposable
{
public Machine(byte[] appleIIe, byte[] diskIIRom)
{
Events = new MachineEvents();
Services = new MachineServices();
Cpu = new Cpu(this);
Memory = new Memory(this, appleIIe);
Keyboard = new Keyboard(this);
GamePort = new GamePort(this);
Cassette = new Cassette(this);
Speaker = new Speaker(this);
Video = new Video(this);
NoSlotClock = new NoSlotClock(this);
Cpu = new Cpu(this);
Memory = new Memory(this, appleIIe);
Keyboard = new Keyboard(this);
GamePort = new GamePort(this);
Cassette = new Cassette(this);
Speaker = new Speaker(this);
Video = new Video(this);
NoSlotClock = new NoSlotClock(this);
var emptySlot = new PeripheralCard(this);
Slot1 = emptySlot;
Slot2 = emptySlot;
Slot3 = emptySlot;
Slot4 = emptySlot;
Slot5 = emptySlot;
Slot6 = new DiskIIController(this, diskIIRom);
Slot7 = emptySlot;
var emptySlot = new PeripheralCard(this);
Slot1 = emptySlot;
Slot2 = emptySlot;
Slot3 = emptySlot;
Slot4 = emptySlot;
Slot5 = emptySlot;
Slot6 = new DiskIIController(this, diskIIRom);
Slot7 = emptySlot;
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 };
Slots = new List<PeripheralCard> { null, Slot1, Slot2, Slot3, Slot4, Slot5, Slot6, Slot7 };
Components = new List<MachineComponent> { Cpu, Memory, Keyboard, GamePort, Cassette, Speaker, Video, NoSlotClock, Slot1, Slot2, Slot3, Slot4, Slot5, Slot6, Slot7 };
BootDiskII = Slots.OfType<DiskIIController>().Last();
BootDiskII = Slots.OfType<DiskIIController>().Last();
}
//Thread = new Thread(Run) { Name = "Machine" };
}
public void Dispose()
{
_pauseEvent.Close();
_unpauseEvent.Close();
}
public void Reset()
{
foreach (var component in Components)
{
_debugService.WriteMessage("Resetting machine '{0}'", component.GetType().Name);
component.Reset();
//_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)
// {
// Thread.Join();
// }
// State = MachineState.Stopped;
// _debugService.WriteMessage("Stopped machine");
//}
private void Initialize()
{
foreach (var component in Components)
{
_debugService.WriteMessage("Initializing machine '{0}'", component.GetType().Name);
component.Initialize();
//_debugService.WriteMessage("Initialized machine '{0}'", component.GetType().Name);
}
}
private void LoadState()
{
#if WINDOWS
var args = Environment.GetCommandLineArgs();
if (args.Length > 1)
{
string name = args[1];
Func<string, Action<Stream>, bool> loader = StorageService.LoadFile;
if (name.StartsWith("res://", StringComparison.OrdinalIgnoreCase))
{
name = name.Substring(6);
loader = StorageService.LoadResource;
}
if (name.EndsWith(".bin", StringComparison.OrdinalIgnoreCase))
{
loader(name, stream => LoadState(stream));
}
else if (name.EndsWith(".prg", StringComparison.OrdinalIgnoreCase))
{
loader(name, stream => Memory.LoadPrg(stream));
}
else if (name.EndsWith(".xex", StringComparison.OrdinalIgnoreCase))
{
loader(name, stream => Memory.LoadXex(stream));
}
else
{
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.BootDrive.InsertDisk("Default.dsk", stream, false));
}
}
// TODO: don't copy paste
public void LoadState(BinaryReader reader)
public void Dispose()
{
}
public void Reset()
{
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.WriteMessage("Loading machine '{0}'", component.GetType().Name);
component.LoadState(reader, version);
//_debugService.WriteMessage("Loaded machine '{0}'", component.GetType().Name);
DebugService.WriteMessage("Resetting machine '{0}'", component.GetType().Name);
component.Reset();
//DebugService.WriteMessage("Reset machine '{0}'", component.GetType().Name);
}
}
private void LoadState(Stream stream)
{
using (var reader = new BinaryReader(stream))
{
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.WriteMessage("Loading machine '{0}'", component.GetType().Name);
component.LoadState(reader, version);
//_debugService.WriteMessage("Loaded machine '{0}'", component.GetType().Name);
}
}
}
private void SaveState()
{
_storageService.Save(Machine.StateFileName, stream => SaveState(stream));
}
public void SaveState(BinaryWriter writer)
private void Initialize()
{
writer.Write(StateSignature);
writer.Write(Machine.Version);
foreach (var component in Components)
{
_debugService.WriteMessage("Saving machine '{0}'", component.GetType().Name);
component.SaveState(writer);
//_debugService.WriteMessage("Saved machine '{0}'", component.GetType().Name);
DebugService.WriteMessage("Initializing machine '{0}'", component.GetType().Name);
component.Initialize();
//DebugService.WriteMessage("Initialized machine '{0}'", component.GetType().Name);
}
}
private void SaveState(Stream stream)
{
using (var writer = new BinaryWriter(stream))
{
writer.Write(StateSignature);
writer.Write(Machine.Version);
foreach (var component in Components)
{
_debugService.WriteMessage("Saving machine '{0}'", component.GetType().Name);
component.SaveState(writer);
//_debugService.WriteMessage("Saved machine '{0}'", component.GetType().Name);
}
}
}
private void Uninitialize()
{
foreach (var component in Components)
{
DebugService.WriteMessage("Uninitializing machine '{0}'", component.GetType().Name);
component.Uninitialize();
//DebugService.WriteMessage("Uninitialized machine '{0}'", component.GetType().Name);
}
}
private void Uninitialize()
{
foreach (var component in Components)
{
_debugService.WriteMessage("Uninitializing machine '{0}'", component.GetType().Name);
component.Uninitialize();
//_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
//{
// Initialize();
// Reset();
// LoadState();
//
// _debugService.WriteMessage("Running machine");
// State = MachineState.Running;
// do
// {
// do
// {
// Events.HandleEvents(Cpu.Execute());
// }
// while (State == MachineState.Running);
//
// if (State == MachineState.Pausing)
// {
// _pauseEvent.Set();
// _unpauseEvent.WaitOne();
// }
// }
// while (State != MachineState.Stopping);
//
// SaveState();
// Uninitialize();
//}
public void BizInitialize()
{
Initialize();
Reset();
}
public void BizInitialize()
{
_debugService = Services.GetService<DebugService>();
_storageService = Services.GetService<StorageService>();
public void BizFrameAdvance(IEnumerable<string> buttons)
{
Lagged = true;
Initialize();
Reset();
}
Keyboard.SetKeys(buttons);
public void BizNewDisk()
{
_storageService = Services.GetService<StorageService>();
}
//frame begins at vsync.. beginning of vblank
while (Video.IsVBlank)
{
/*
var sb = new System.Text.StringBuilder();
sb.AppendFormat("{0} ", Cpu);
for (int i = 0; i < 256; i++)
sb.AppendFormat("{0:x2} ", Memory.Read(i));
tw.WriteLine(sb.ToString());*/
Events.HandleEvents(Cpu.Execute());
}
//now, while not vblank, we're in a frame
while (!Video.IsVBlank)
{
/*
var sb = new System.Text.StringBuilder();
sb.AppendFormat("{0} ", Cpu);
for (int i = 0; i < 256; i++)
sb.AppendFormat("{0:x2} ", Memory.Read(i));
tw.WriteLine(sb.ToString()); */
public void BizFrameAdvance()
{
Lagged = true;
Services.GetService<KeyboardService>().Update();
Services.GetService<GamePortService>().Update();
//frame begins at vsync.. beginning of vblank
while (Video.IsVBlank)
Events.HandleEvents(Cpu.Execute());
//now, while not vblank, we're in a frame
while (!Video.IsVBlank)
Events.HandleEvents(Cpu.Execute());
}
Events.HandleEvents(Cpu.Execute());
}
}
public void BizShutdown()
{
Uninitialize();
}
public void BizShutdown()
{
Uninitialize();
}
public const string Version = "0.9.4.0";
private static JsonSerializer CreateSerializer()
{
var ser = new JsonSerializer
{
TypeNameHandling = TypeNameHandling.Auto,
PreserveReferencesHandling = PreserveReferencesHandling.All, // leaving out Array is a very important problem, and means that we can't rely on a directly shared array to work.
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
};
public MachineEvents Events { get; private set; }
public MachineServices Services { get; private set; }
public MachineState State { get { return _state; } private set { _state = value; } }
ser.Converters.Add(new DelegateConverter());
ser.Converters.Add(new ArrayConverter());
var cr = new DefaultContractResolver();
cr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic;
ser.ContractResolver = cr;
public Cpu Cpu { get; private set; }
public Memory Memory { get; private set; }
public Keyboard Keyboard { get; private set; }
public GamePort GamePort { get; private set; }
public Cassette Cassette { get; private set; }
public Speaker Speaker { get; private set; }
public Video Video { get; private set; }
public NoSlotClock NoSlotClock { get; private set; }
return ser;
}
public PeripheralCard Slot1 { get; private set; }
public PeripheralCard Slot2 { get; private set; }
public PeripheralCard Slot3 { get; private set; }
public PeripheralCard Slot4 { get; private set; }
public PeripheralCard Slot5 { get; private set; }
public PeripheralCard Slot6 { get; private set; }
public PeripheralCard Slot7 { get; private set; }
public void Serialize(JsonWriter w)
{
CreateSerializer().Serialize(w, this);
}
public Collection<PeripheralCard> Slots { get; private set; }
public Collection<MachineComponent> Components { get; private set; }
public static Machine Deserialize(JsonReader r)
{
var ret = CreateSerializer().Deserialize<Machine>(r);
// for some unknown reason, these particular references get nuked by JSON.NET on deserializating
foreach (var c in ret.Components)
{
c.Machine = ret;
}
ret.BootDiskII.Drive1.Machine = ret;
ret.BootDiskII.Drive2.Machine = ret;
public DiskIIController BootDiskII { get; private set; }
return ret;
}
public Thread Thread { get; private set; }
public const string Version = "0.9.4.0";
public Buttons Buttons;
public MachineEvents Events { get; private set; }
private const string StateFileName = "State.bin";
private const string StateSignature = "Virtu";
public Cpu Cpu { get; private set; }
public Memory Memory { get; private set; }
public Keyboard Keyboard { get; private set; }
public GamePort GamePort { get; private set; }
public Cassette Cassette { get; private set; }
public Speaker Speaker { get; private set; }
public Video Video { get; private set; }
public NoSlotClock NoSlotClock { get; private set; }
private DebugService _debugService;
private StorageService _storageService;
private volatile MachineState _state;
public PeripheralCard Slot1 { get; private set; }
public PeripheralCard Slot2 { get; private set; }
public PeripheralCard Slot3 { get; private set; }
public PeripheralCard Slot4 { get; private set; }
public PeripheralCard Slot5 { get; private set; }
public PeripheralCard Slot6 { get; private set; }
public PeripheralCard Slot7 { get; private set; }
private AutoResetEvent _pauseEvent = new AutoResetEvent(false);
private AutoResetEvent _unpauseEvent = new AutoResetEvent(false);
public IList<PeripheralCard> Slots { get; private set; }
public IList<MachineComponent> Components { get; private set; }
public DiskIIController BootDiskII { get; private set; }
public bool Lagged { get; set; }
}
}
}

View File

@ -7,11 +7,10 @@ namespace Jellyfish.Virtu
{
public abstract class MachineComponent
{
public MachineComponent() { }
protected MachineComponent(Machine machine)
{
Machine = machine;
_debugService = new Lazy<DebugService>(() => Machine.Services.GetService<DebugService>());
_machine = machine;
}
public virtual void Initialize()
@ -22,21 +21,17 @@ namespace Jellyfish.Virtu
{
}
public virtual void LoadState(BinaryReader reader, Version version)
{
}
public virtual void Uninitialize()
{
}
public virtual void SaveState(BinaryWriter writer)
{
}
[Newtonsoft.Json.JsonIgnore]
private Machine _machine;
protected Machine Machine { get; private set; }
protected DebugService DebugService { get { return _debugService.Value; } }
private Lazy<DebugService> _debugService;
public Machine Machine
{
get { return _machine; }
set { _machine = value; }
}
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Jellyfish.Virtu
{
@ -25,6 +26,8 @@ namespace Jellyfish.Virtu
{
public void AddEvent(int delta, Action action)
{
//Console.WriteLine("+{0} @ {1}", action.Method.Name, delta);
var node = _used.First;
for (; node != null; node = node.Next)
{
@ -68,23 +71,37 @@ namespace Jellyfish.Virtu
for (var node = _used.First; node != null; node = node.Next)
{
delta += node.Value.Delta;
if (object.ReferenceEquals(node.Value.Action, action)) // assumes delegate cached
{
return delta;
}
var other = node.Value.Action;
if (other.Method == action.Method && other.Target == action.Target)
{
//Console.WriteLine("={0} @ {1}", action.Method.Name, delta);
return delta;
}
// our delegate serializer doesn't preserve reference equality
//if (object.ReferenceEquals(node.Value.Action, action)) // assumes delegate cached
//{
// return delta;
//}
}
return 0;
//Console.WriteLine("=???? @ 0");
return 0;
}
public void HandleEvents(int delta)
{
//Console.WriteLine("[{0}]", delta);
var node = _used.First;
node.Value.Delta -= delta;
while (node.Value.Delta <= 0)
{
node.Value.Action();
//Console.WriteLine("!{0} @ {1}", node.Value.Action.Method.Name, node.Value.Delta);
node.Value.Action();
RemoveEvent(node);
node = _used.First;
}

View File

@ -13,6 +13,7 @@ namespace Jellyfish.Virtu
public sealed partial class Memory : MachineComponent
{
public Memory() { }
public Memory(Machine machine, byte[] appleIIe) :
base(machine)
{
@ -89,7 +90,7 @@ namespace Jellyfish.Virtu
_writeRomRegionD0FF = WriteRomRegionD0FF;
}
private readonly byte[] _appleIIe;
private byte[] _appleIIe;
public override void Initialize()
{
@ -138,55 +139,6 @@ namespace Jellyfish.Virtu
MapRegionD0FF();
}
public override void LoadState(BinaryReader reader, Version version)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
_state = reader.ReadInt32();
_slotRegionC8CF = reader.ReadInt32();
reader.Read(_ramMainRegion0001, 0, _ramMainRegion0001.Length);
reader.Read(_ramMainRegion02BF, 0, _ramMainRegion02BF.Length);
reader.Read(_ramMainBank1RegionD0DF, 0, _ramMainBank1RegionD0DF.Length);
reader.Read(_ramMainBank2RegionD0DF, 0, _ramMainBank2RegionD0DF.Length);
reader.Read(_ramMainRegionE0FF, 0, _ramMainRegionE0FF.Length);
reader.Read(_ramAuxRegion0001, 0, _ramAuxRegion0001.Length);
reader.Read(_ramAuxRegion02BF, 0, _ramAuxRegion02BF.Length);
reader.Read(_ramAuxBank1RegionD0DF, 0, _ramAuxBank1RegionD0DF.Length);
reader.Read(_ramAuxBank2RegionD0DF, 0, _ramAuxBank2RegionD0DF.Length);
reader.Read(_ramAuxRegionE0FF, 0, _ramAuxRegionE0FF.Length);
MapRegion0001();
MapRegion02BF();
MapRegionC0CF();
MapRegionD0FF();
}
public override void SaveState(BinaryWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
writer.Write(_state);
writer.Write(_slotRegionC8CF);
writer.Write(_ramMainRegion0001);
writer.Write(_ramMainRegion02BF);
writer.Write(_ramMainBank1RegionD0DF);
writer.Write(_ramMainBank2RegionD0DF);
writer.Write(_ramMainRegionE0FF);
writer.Write(_ramAuxRegion0001);
writer.Write(_ramAuxRegion02BF);
writer.Write(_ramAuxBank1RegionD0DF);
writer.Write(_ramAuxBank2RegionD0DF);
writer.Write(_ramAuxRegionE0FF);
}
public void LoadPrg(Stream stream)
{
if (stream == null)
@ -282,8 +234,6 @@ namespace Jellyfish.Virtu
return _video.ReadFloatingBus();
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
[SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")]
private int ReadIoRegionC0C0(int address)
{
Machine.Lagged = false;

View File

@ -101,6 +101,6 @@ namespace Jellyfish.Virtu
Video.ModeC, Video.ModeD, Video.Mode1, Video.Mode2, Video.ModeE, Video.ModeF, Video.Mode1, Video.Mode2
};
private readonly Action<int, byte>[][][] WriteRamModeBankRegion;
private Action<int, byte>[][][] WriteRamModeBankRegion;
}
}

View File

@ -5,6 +5,7 @@ namespace Jellyfish.Virtu
{
public sealed class NoSlotClock : MachineComponent
{
public NoSlotClock() { }
public NoSlotClock(Machine machine) :
base(machine)
{
@ -18,34 +19,6 @@ namespace Jellyfish.Virtu
_comparisonRegister = new RingRegister(ClockInitSequence, 0x1);
}
public override void LoadState(BinaryReader reader, Version version)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
_clockEnabled = reader.ReadBoolean();
_writeEnabled = reader.ReadBoolean();
_clockRegister = new RingRegister(reader.ReadUInt64(), reader.ReadUInt64());
_comparisonRegister = new RingRegister(reader.ReadUInt64(), reader.ReadUInt64());
}
public override void SaveState(BinaryWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
writer.Write(_clockEnabled);
writer.Write(_writeEnabled);
writer.Write(_clockRegister.Data);
writer.Write(_clockRegister.Mask);
writer.Write(_comparisonRegister.Data);
writer.Write(_comparisonRegister.Mask);
}
public int Read(int address, int data)
{
// this may read or write the clock

View File

@ -2,6 +2,7 @@
{
public class PeripheralCard : MachineComponent
{
public PeripheralCard() { }
public PeripheralCard(Machine machine) :
base(machine)
{

View File

@ -5,12 +5,12 @@ using Jellyfish.Library;
namespace Jellyfish.Virtu.Services
{
public abstract class AudioService : MachineService
/// <summary>
/// this isn't really a "service" anymore, just a helper for the speaker class
/// </summary>
public class AudioService
{
protected AudioService(Machine machine) :
base(machine)
{
}
public AudioService() { }
public void Output(int data) // machine thread
{
@ -25,21 +25,16 @@ namespace Jellyfish.Virtu.Services
private short[] buff = new short[4096];
private int pos = 0;
public void Reset()
{
public void Clear()
{
pos = 0;
}
public abstract void SetVolume(float volume);
}
public void GetSamples(out short[] samples, out int nsamp)
{
samples = buff;
nsamp = pos / 2;
pos = 0;
Console.WriteLine(nsamp);
}
}
}

View File

@ -7,24 +7,22 @@ using Jellyfish.Library;
namespace Jellyfish.Virtu.Services
{
public class DebugService : MachineService
/// <summary>
/// this isn't really a "service" anymore
/// </summary>
public static class DebugService
{
public DebugService(Machine machine) :
base(machine)
{
}
public void WriteMessage(string message)
public static void WriteMessage(string message)
{
OnWriteMessage(FormatMessage(message));
}
public void WriteMessage(string format, params object[] args)
public static void WriteMessage(string format, params object[] args)
{
OnWriteMessage(FormatMessage(format, args));
}
protected virtual void OnWriteMessage(string message)
private static void OnWriteMessage(string message)
{
#if SILVERLIGHT
Debug.WriteLine(message);
@ -33,7 +31,7 @@ namespace Jellyfish.Virtu.Services
#endif
}
private string FormatMessage(string format, params object[] args)
private static 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);
@ -55,9 +53,5 @@ namespace Jellyfish.Virtu.Services
return message.ToString();
}
public static DebugService Default { get { return _default.Value; } }
private static readonly Lazy<DebugService> _default = new Lazy<DebugService>(() => new DebugService(null));
}
}

View File

@ -1,333 +0,0 @@
using System;
namespace Jellyfish.Virtu.Services
{
public class GamePortService : MachineService
{
public GamePortService(Machine machine) :
base(machine)
{
Paddle0 = Paddle1 = Paddle2 = Paddle3 = 255; // not connected
}
public virtual void Update() // main thread
{
var keyboard = Machine.Keyboard;
if (keyboard.UseGamePort)
{
//UpdateKey(keyboard.Joystick0UpKey, IsJoystick0Up, ref _isJoystick0UpKeyDown, ref _wasJoystick0UpKeyDown);
//UpdateKey(keyboard.Joystick0LeftKey, IsJoystick0Left, ref _isJoystick0LeftKeyDown, ref _wasJoystick0LeftKeyDown);
//UpdateKey(keyboard.Joystick0RightKey, IsJoystick0Right, ref _isJoystick0RightKeyDown, ref _wasJoystick0RightKeyDown);
//UpdateKey(keyboard.Joystick0DownKey, IsJoystick0Down, ref _isJoystick0DownKeyDown, ref _wasJoystick0DownKeyDown);
//UpdateKey(keyboard.Joystick0UpLeftKey, IsJoystick0Up && IsJoystick0Left, ref _isJoystick0UpLeftKeyDown, ref _wasJoystick0UpLeftKeyDown);
//UpdateKey(keyboard.Joystick0UpRightKey, IsJoystick0Up && IsJoystick0Right, ref _isJoystick0UpRightKeyDown, ref _wasJoystick0UpRightKeyDown);
//UpdateKey(keyboard.Joystick0DownLeftKey, IsJoystick0Down && IsJoystick0Left, ref _isJoystick0DownLeftKeyDown, ref _wasJoystick0DownLeftKeyDown);
//UpdateKey(keyboard.Joystick0DownRightKey, IsJoystick0Down && IsJoystick0Right, ref _isJoystick0DownRightKeyDown, ref _wasJoystick0DownRightKeyDown);
//UpdateKey(keyboard.Joystick1UpKey, IsJoystick1Up, ref _isJoystick1UpKeyDown, ref _wasJoystick1UpKeyDown);
//UpdateKey(keyboard.Joystick1LeftKey, IsJoystick1Left, ref _isJoystick1LeftKeyDown, ref _wasJoystick1LeftKeyDown);
//UpdateKey(keyboard.Joystick1RightKey, IsJoystick1Right, ref _isJoystick1RightKeyDown, ref _wasJoystick1RightKeyDown);
//UpdateKey(keyboard.Joystick1DownKey, IsJoystick1Down, ref _isJoystick1DownKeyDown, ref _wasJoystick1DownKeyDown);
//UpdateKey(keyboard.Joystick1UpLeftKey, IsJoystick1Up && IsJoystick1Left, ref _isJoystick1UpLeftKeyDown, ref _wasJoystick1UpLeftKeyDown);
//UpdateKey(keyboard.Joystick1UpRightKey, IsJoystick1Up && IsJoystick1Right, ref _isJoystick1UpRightKeyDown, ref _wasJoystick1UpRightKeyDown);
//UpdateKey(keyboard.Joystick1DownLeftKey, IsJoystick1Down && IsJoystick1Left, ref _isJoystick1DownLeftKeyDown, ref _wasJoystick1DownLeftKeyDown);
//UpdateKey(keyboard.Joystick1DownRightKey, IsJoystick1Down && IsJoystick1Right, ref _isJoystick1DownRightKeyDown, ref _wasJoystick1DownRightKeyDown);
//all the keys are going through this one thing atm
UpdateKey(keyboard.Button0Key, IsButton0Down, ref _isButton0KeyDown, ref _wasButton0KeyDown);
//UpdateKey(keyboard.Button1Key, IsButton1Down, ref _isButton1KeyDown, ref _wasButton1KeyDown);
//UpdateKey(keyboard.Button2Key, IsButton2Down, ref _isButton2KeyDown, ref _wasButton2KeyDown);
/*
if (_lastKey > 0) // repeat last key
{
long time = DateTime.UtcNow.Ticks;
if (time - _lastTime >= _repeatTime)
{
_lastTime = time;
_repeatTime = RepeatSpeed;
keyboard.Latch = GetAsciiKey((Buttons)_lastKey, Machine.Keyboard.IsControlKeyDown, Machine.Keyboard.IsShiftKeyDown, false);
}
}
* */
}
}
//static int t = 0;
private void UpdateKey(ulong key, bool isActive, ref bool isKeyDown, ref bool wasKeyDown)
{
wasKeyDown = isKeyDown;
isKeyDown = (key > 0);// && isActive;
if (isKeyDown != wasKeyDown)
{
if (isKeyDown)
{
_lastKey = key;
_lastTime = DateTime.UtcNow.Ticks;
_repeatTime = RepeatDelay;
Machine.Keyboard.Latch = GetAsciiKey((Buttons)key, Machine.Keyboard.IsControlKeyDown, Machine.Keyboard.IsShiftKeyDown, false);
}
else if (key == _lastKey)
{
_lastKey = 0;
}
}
}
private static int GetAsciiKey(Buttons bizKey, bool bizCtrl, bool bizShift, bool bizCaps)
{
bool control = bizCtrl;
bool shift = bizShift;
bool capsLock = bizCaps;
switch (bizKey)
{
case 0:
return 0x00;
case Buttons.Left:
return 0x08;
case Buttons.Tab:
return 0x09;
case Buttons.Down:
return 0x0A;
case Buttons.Up:
return 0x0B;
case Buttons.Enter:
return 0x0D;
case Buttons.Right:
return 0x15;
case Buttons.Escape:
return 0x1B;
case Buttons.Back:
return control ? -1 : 0x7F;
case Buttons.Space:
return ' ';
case Buttons.Key1:
return shift ? '!' : '1';
case Buttons.Key2:
return control ? 0x00 : shift ? '@' : '2';
case Buttons.Key3:
return shift ? '#' : '3';
case Buttons.Key4:
return shift ? '$' : '4';
case Buttons.Key5:
return shift ? '%' : '5';
case Buttons.Key6:
return control ? 0x1E : shift ? '^' : '6';
case Buttons.Key7:
return shift ? '&' : '7';
case Buttons.Key8:
return shift ? '*' : '8';
case Buttons.Key9:
return shift ? '(' : '9';
case Buttons.Key0:
return shift ? ')' : '0';
case Buttons.KeyA:
return control ? 0x01 : capsLock ? 'A' : 'a';
case Buttons.KeyB:
return control ? 0x02 : capsLock ? 'B' : 'b';
case Buttons.KeyC:
return control ? 0x03 : capsLock ? 'C' : 'c';
case Buttons.KeyD:
return control ? 0x04 : capsLock ? 'D' : 'd';
case Buttons.KeyE:
return control ? 0x05 : capsLock ? 'E' : 'e';
case Buttons.KeyF:
return control ? 0x06 : capsLock ? 'F' : 'f';
case Buttons.KeyG:
return control ? 0x07 : capsLock ? 'G' : 'g';
case Buttons.KeyH:
return control ? 0x08 : capsLock ? 'H' : 'h';
case Buttons.KeyI:
return control ? 0x09 : capsLock ? 'I' : 'i';
case Buttons.KeyJ:
return control ? 0x0A : capsLock ? 'J' : 'j';
case Buttons.KeyK:
return control ? 0x0B : capsLock ? 'K' : 'k';
case Buttons.KeyL:
return control ? 0x0C : capsLock ? 'L' : 'l';
case Buttons.KeyM:
return control ? 0x0D : capsLock ? 'M' : 'm';
case Buttons.KeyN:
return control ? 0x0E : capsLock ? 'N' : 'n';
case Buttons.KeyO:
return control ? 0x0F : capsLock ? 'O' : 'o';
case Buttons.KeyP:
return control ? 0x10 : capsLock ? 'P' : 'p';
case Buttons.KeyQ:
return control ? 0x11 : capsLock ? 'Q' : 'q';
case Buttons.KeyR:
return control ? 0x12 : capsLock ? 'R' : 'r';
case Buttons.KeyS:
return control ? 0x13 : capsLock ? 'S' : 's';
case Buttons.KeyT:
return control ? 0x14 : capsLock ? 'T' : 't';
case Buttons.KeyU:
return control ? 0x15 : capsLock ? 'U' : 'u';
case Buttons.KeyV:
return control ? 0x16 : capsLock ? 'V' : 'v';
case Buttons.KeyW:
return control ? 0x17 : capsLock ? 'W' : 'w';
case Buttons.KeyX:
return control ? 0x18 : capsLock ? 'X' : 'x';
case Buttons.KeyY:
return control ? 0x19 : capsLock ? 'Y' : 'y';
case Buttons.KeyZ:
return control ? 0x1A : capsLock ? 'Z' : 'z';
//TODO: Get around to supporting those keys too
/*
case Key.Oem1:
return shift ? ':' : ';';
case Key.Oem2:
return shift ? '?' : '/';
case Key.Oem3:
return shift ? '~' : '`';
case Key.Oem4:
return shift ? '{' : '[';
case Key.Oem5:
return control ? 0x1C : shift ? '|' : '\\';
case Key.Oem6:
return control ? 0x1D : shift ? '}' : ']';
case Key.Oem7:
return shift ? '"' : '\'';
case Key.OemMinus:
return control ? 0x1F : shift ? '_' : '-';
case Key.OemPlus:
return shift ? '+' : '=';
case Key.OemComma:
return shift ? '<' : ',';
case Key.OemPeriod:
return shift ? '>' : '.';
* */
}
return 0;
}
public int Paddle0 { get; protected set; }
public int Paddle1 { get; protected set; }
public int Paddle2 { get; protected set; }
public int Paddle3 { get; protected set; }
public bool IsJoystick0Up { get; protected set; }
public bool IsJoystick0Left { get; protected set; }
public bool IsJoystick0Right { get; protected set; }
public bool IsJoystick0Down { get; protected set; }
public bool IsJoystick1Up { get; protected set; }
public bool IsJoystick1Left { get; protected set; }
public bool IsJoystick1Right { get; protected set; }
public bool IsJoystick1Down { get; protected set; }
public bool IsButton0Down { get; protected set; }
public bool IsButton1Down { get; protected set; }
public bool IsButton2Down { get; protected set; }
private static readonly long RepeatDelay = TimeSpan.FromMilliseconds(500).Ticks;
private static readonly long RepeatSpeed = TimeSpan.FromMilliseconds(32).Ticks;
//private bool _isJoystick0UpLeftKeyDown;
//private bool _isJoystick0UpKeyDown;
//private bool _isJoystick0UpRightKeyDown;
//private bool _isJoystick0LeftKeyDown;
//private bool _isJoystick0RightKeyDown;
//private bool _isJoystick0DownLeftKeyDown;
//private bool _isJoystick0DownKeyDown;
//private bool _isJoystick0DownRightKeyDown;
//private bool _isJoystick1UpLeftKeyDown;
//private bool _isJoystick1UpKeyDown;
//private bool _isJoystick1UpRightKeyDown;
//private bool _isJoystick1LeftKeyDown;
//private bool _isJoystick1RightKeyDown;
//private bool _isJoystick1DownLeftKeyDown;
//private bool _isJoystick1DownKeyDown;
//private bool _isJoystick1DownRightKeyDown;
private bool _isButton0KeyDown;
//private bool _isButton1KeyDown;
//private bool _isButton2KeyDown;
//private bool _wasJoystick0UpLeftKeyDown;
//private bool _wasJoystick0UpKeyDown;
//private bool _wasJoystick0UpRightKeyDown;
//private bool _wasJoystick0LeftKeyDown;
//private bool _wasJoystick0RightKeyDown;
//private bool _wasJoystick0DownLeftKeyDown;
//private bool _wasJoystick0DownKeyDown;
//private bool _wasJoystick0DownRightKeyDown;
//private bool _wasJoystick1UpLeftKeyDown;
//private bool _wasJoystick1UpKeyDown;
//private bool _wasJoystick1UpRightKeyDown;
//private bool _wasJoystick1LeftKeyDown;
//private bool _wasJoystick1RightKeyDown;
//private bool _wasJoystick1DownLeftKeyDown;
//private bool _wasJoystick1DownKeyDown;
//private bool _wasJoystick1DownRightKeyDown;
private bool _wasButton0KeyDown;
//private bool _wasButton1KeyDown;
//private bool _wasButton2KeyDown;
private ulong _lastKey;
private long _lastTime;
private long _repeatTime;
}
}

View File

@ -1,53 +0,0 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.IsolatedStorage;
namespace Jellyfish.Virtu.Services
{
public class IsolatedStorageService : StorageService
{
public IsolatedStorageService(Machine machine) :
base(machine)
{
}
protected override void OnLoad(string fileName, Action<Stream> reader)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
using (var store = GetStore())
{
using (var stream = store.OpenFile(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
reader(stream);
}
}
}
protected override void OnSave(string fileName, Action<Stream> writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
using (var store = GetStore())
{
using (var stream = store.OpenFile(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
writer(stream);
}
}
}
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
protected virtual IsolatedStorageFile GetStore()
{
return IsolatedStorageFile.GetUserStoreForApplication();
}
}
}

View File

@ -1,48 +0,0 @@
namespace Jellyfish.Virtu.Services
{
public abstract class KeyboardService : MachineService
{
protected KeyboardService(Machine machine) :
base(machine)
{
}
public abstract bool IsKeyDown(int key);
public virtual void Update() // main thread
{
var keyboard = Machine.Keyboard;
var buttons = Machine.Buttons;
keyboard.Button0Key = (ulong)buttons;
if (IsResetKeyDown && !keyboard.DisableResetKey)
{
if (!_resetKeyDown)
{
_resetKeyDown = true; // entering reset; pause until key released
//TODO ADELIKAT : HANDLE RESET DIFFERENTLY
//Machine.Pause();
//Machine.Reset();
}
}
else if (_resetKeyDown)
{
_resetKeyDown = false; // leaving reset
//TODO ADELIKAT : HANDLE RESET DIFFERENTLY
//Machine.Unpause();
}
}
public bool IsAnyKeyDown { get { return Machine.Buttons > 0; }}
public bool IsControlKeyDown { get { return Machine.Buttons.HasFlag(Buttons.Ctrl); }}
public bool IsShiftKeyDown { get { return Machine.Buttons.HasFlag(Buttons.Shift); }}
public bool IsOpenAppleKeyDown { get; protected set; }
public bool IsCloseAppleKeyDown { get; protected set; }
protected bool IsResetKeyDown { get; set; }
private bool _resetKeyDown;
}
}

View File

@ -1,20 +0,0 @@
using System;
using Jellyfish.Library;
namespace Jellyfish.Virtu.Services
{
public abstract class MachineService : DisposableBase
{
protected MachineService(Machine machine)
{
Machine = machine;
_debugService = new Lazy<DebugService>(() => Machine.Services.GetService<DebugService>());
}
protected Machine Machine { get; private set; }
protected DebugService DebugService { get { return _debugService.Value; } }
private Lazy<DebugService> _debugService;
}
}

View File

@ -1,51 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Jellyfish.Virtu.Properties;
namespace Jellyfish.Virtu.Services
{
public sealed class MachineServices : IServiceProvider
{
public void AddService(Type serviceType, MachineService serviceProvider)
{
if (serviceType == null)
{
throw new ArgumentNullException("serviceType");
}
if (_serviceProviders.ContainsKey(serviceType))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, Strings.ServiceAlreadyPresent, serviceType.FullName), "serviceType");
}
if (serviceProvider == null)
{
throw new ArgumentNullException("serviceProvider");
}
if (!serviceType.IsAssignableFrom(serviceProvider.GetType()))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, Strings.ServiceMustBeAssignable, serviceType.FullName, serviceProvider.GetType().FullName));
}
_serviceProviders.Add(serviceType, serviceProvider);
}
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
public T GetService<T>()
{
return (T)((IServiceProvider)this).GetService(typeof(T));
}
public void RemoveService(Type serviceType)
{
_serviceProviders.Remove(serviceType);
}
object IServiceProvider.GetService(Type serviceType)
{
return _serviceProviders.ContainsKey(serviceType) ? _serviceProviders[serviceType] : null;
}
private Dictionary<Type, MachineService> _serviceProviders = new Dictionary<Type, MachineService>();
}
}

View File

@ -1,214 +0,0 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Security;
using Jellyfish.Virtu.Properties;
namespace Jellyfish.Virtu.Services
{
public abstract class StorageService : MachineService
{
protected StorageService(Machine machine) :
base(machine)
{
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
public bool Load(string fileName, Action<Stream> reader)
{
try
{
DebugService.WriteMessage("Loading file '{0}'", fileName);
OnLoad(fileName, reader);
}
catch (Exception ex)
{
DebugService.WriteMessage(ex.ToString());
return false;
}
return true;
}
#if !WINDOWS
[SecuritySafeCritical]
#endif
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
public static bool LoadFile(Stream stream, Action<Stream> reader)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
try
{
DebugService.Default.WriteMessage("Loading file '{0}'", "STREAM");
{
reader(stream);
}
}
catch (Exception ex)
{
DebugService.Default.WriteMessage(ex.ToString());
return false;
}
return true;
}
#if !WINDOWS
[SecuritySafeCritical]
#endif
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
public static bool LoadFile(FileInfo fileInfo, Action<Stream> reader)
{
if (fileInfo == null)
{
throw new ArgumentNullException("fileInfo");
}
if (reader == null)
{
throw new ArgumentNullException("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 ex)
{
DebugService.Default.WriteMessage(ex.ToString());
return false;
}
return true;
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
public static bool LoadResource(string resourceName, Action<Stream> reader)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
try
{
DebugService.Default.WriteMessage("Loading resource '{0}'", resourceName);
using (var stream = File.OpenRead(resourceName))
reader(stream);
//using (var stream = GetResourceStream(resourceName))
//{
// reader(stream);
//}
}
catch (Exception ex)
{
DebugService.Default.WriteMessage(ex.ToString());
return false;
}
return true;
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
public bool Save(string fileName, Action<Stream> writer)
{
try
{
DebugService.WriteMessage("Saving file '{0}'", fileName);
OnSave(fileName, writer);
}
catch (Exception ex)
{
DebugService.WriteMessage(ex.ToString());
return false;
}
return true;
}
#if !WINDOWS
[SecuritySafeCritical]
#endif
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
public static bool SaveFile(string fileName, Action<Stream> writer)
{
if (writer == null)
{
throw new ArgumentNullException("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 ex)
{
DebugService.Default.WriteMessage(ex.ToString());
return false;
}
return true;
}
#if !WINDOWS
[SecuritySafeCritical]
#endif
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
public static bool SaveFile(FileInfo fileInfo, Action<Stream> writer)
{
if (fileInfo == null)
{
throw new ArgumentNullException("fileInfo");
}
if (writer == null)
{
throw new ArgumentNullException("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 ex)
{
DebugService.Default.WriteMessage(ex.ToString());
return false;
}
return true;
}
protected abstract void OnLoad(string fileName, Action<Stream> reader);
protected abstract void OnSave(string fileName, Action<Stream> writer);
private static Stream GetResourceStream(string resourceName)
{
resourceName = "Jellyfish.Virtu." + resourceName.Replace('/', '.');
var resourceStream = typeof(StorageService).Assembly.GetManifestResourceStream(resourceName);
if (resourceStream == null)
{
throw new FileNotFoundException(string.Format(CultureInfo.CurrentUICulture, Strings.ResourceNotFound, resourceName));
}
return resourceStream;
}
}
}

View File

@ -1,17 +1,25 @@
namespace Jellyfish.Virtu.Services
{
public abstract class VideoService : MachineService
/// <summary>
/// this isn't really a "service" anymore, just a helper for the video class
/// </summary>
public class VideoService
{
protected VideoService(Machine machine) :
base(machine)
{
}
public VideoService()
{
fb = new int[560 * 384];
}
public VideoService(int[] fb)
{
this.fb = fb;
}
public virtual void SetFullScreen(bool isFullScreen)
{
}
public int[] fb;
public abstract void SetPixel(int x, int y, uint color);
public abstract void Update(); // main thread
public void SetPixel(int x, int y, uint color)
{
int i = 560 * y + x;
fb[i] = fb[i + 560] = (int)color;
}
}
}

View File

@ -6,6 +6,7 @@ namespace Jellyfish.Virtu
{
public sealed class Speaker : MachineComponent
{
public Speaker() { }
public Speaker(Machine machine) :
base(machine)
{
@ -14,39 +15,17 @@ namespace Jellyfish.Virtu
public override void Initialize()
{
_audioService = Machine.Services.GetService<AudioService>();
AudioService = new Services.AudioService();
Volume = 0.5f;
Machine.Events.AddEvent(CyclesPerFlush * Machine.Cpu.Multiplier, _flushOutputEvent);
}
public override void Reset()
{
_audioService.Reset();
_isHigh = false;
_highCycles = _totalCycles = 0;
}
public override void LoadState(BinaryReader reader, Version version)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
Volume = reader.ReadSingle();
}
public override void SaveState(BinaryWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
writer.Write(Volume);
}
public void ToggleOutput()
{
UpdateCycles();
@ -56,7 +35,8 @@ namespace Jellyfish.Virtu
private void FlushOutputEvent()
{
UpdateCycles();
_audioService.Output(_highCycles * short.MaxValue / _totalCycles); // quick and dirty decimation
// TODO: better than simple decimation here!!
AudioService.Output(_highCycles * short.MaxValue / _totalCycles);
_highCycles = _totalCycles = 0;
Machine.Events.AddEvent(CyclesPerFlush * Machine.Cpu.Multiplier, _flushOutputEvent);
@ -73,20 +53,15 @@ namespace Jellyfish.Virtu
_lastCycles = Machine.Cpu.Cycles;
}
public float Volume { get { return _volume; } set { _volume = value; _audioService.SetVolume(_volume); } }
private const int CyclesPerFlush = 23;
private Action _flushOutputEvent;
private AudioService _audioService;
private bool _isHigh;
private int _highCycles;
private int _totalCycles;
private long _lastCycles;
private float _volume;
public AudioService AudioService { get { return _audioService; } }
public AudioService AudioService { get; private set; }
}
}

View File

@ -9,6 +9,7 @@ namespace Jellyfish.Virtu
public sealed partial class Video : MachineComponent
{
public Video() { }
public Video(Machine machine) :
base(machine)
{
@ -27,7 +28,7 @@ namespace Jellyfish.Virtu
public override void Initialize()
{
_memory = Machine.Memory;
_videoService = Machine.Services.GetService<VideoService>();
VideoService = new Services.VideoService();
//#if SILVERLIGHT || WPF
_colorBlack = 0xFF000000; // BGRA
@ -68,7 +69,6 @@ namespace Jellyfish.Virtu
//#endif
SetPalette();
IsFullScreen = false;
IsMonochrome = false;
ScannerOptions = ScannerOptions.None;
@ -86,71 +86,6 @@ namespace Jellyfish.Virtu
FlushScreen();
}
public override void LoadState(BinaryReader reader, Version version)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
_colorBlack = reader.ReadUInt32();
_colorDarkBlue = reader.ReadUInt32();
_colorDarkGreen = reader.ReadUInt32();
_colorMediumBlue = reader.ReadUInt32();
_colorBrown = reader.ReadUInt32();
_colorLightGrey = reader.ReadUInt32();
_colorGreen = reader.ReadUInt32();
_colorAquamarine = reader.ReadUInt32();
_colorDeepRed = reader.ReadUInt32();
_colorPurple = reader.ReadUInt32();
_colorDarkGrey = reader.ReadUInt32();
_colorLightBlue = reader.ReadUInt32();
_colorOrange = reader.ReadUInt32();
_colorPink = reader.ReadUInt32();
_colorYellow = reader.ReadUInt32();
_colorWhite = reader.ReadUInt32();
_colorMonochrome = reader.ReadUInt32();
SetPalette();
IsFullScreen = reader.ReadBoolean();
IsMonochrome = reader.ReadBoolean();
ScannerOptions = (ScannerOptions)reader.ReadInt32();
SetCharSet();
DirtyScreen();
FlushScreen();
}
public override void SaveState(BinaryWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
writer.Write(_colorBlack);
writer.Write(_colorDarkBlue);
writer.Write(_colorDarkGreen);
writer.Write(_colorMediumBlue);
writer.Write(_colorBrown);
writer.Write(_colorLightGrey);
writer.Write(_colorGreen);
writer.Write(_colorAquamarine);
writer.Write(_colorDeepRed);
writer.Write(_colorPurple);
writer.Write(_colorDarkGrey);
writer.Write(_colorLightBlue);
writer.Write(_colorOrange);
writer.Write(_colorPink);
writer.Write(_colorYellow);
writer.Write(_colorWhite);
writer.Write(_colorMonochrome);
writer.Write(IsFullScreen);
writer.Write(IsMonochrome);
writer.Write((int)ScannerOptions);
}
public void DirtyCell(int addressOffset)
{
_isCellDirty[CellIndex[addressOffset]] = true;
@ -1061,7 +996,7 @@ namespace Jellyfish.Virtu
private void SetPixel(int x, int y, int color)
{
_videoService.SetPixel(x, 2 * y, _colorPalette[color]);
VideoService.SetPixel(x, 2 * y, _colorPalette[color]);
}
private void SetScanner()
@ -1083,25 +1018,41 @@ namespace Jellyfish.Virtu
_cyclesPerFlash = VSyncsPerFlash * _cyclesPerVSync;
}
public uint ColorBlack { get { return _colorBlack; } set { _colorBlack = value; SetPalette(); } }
public uint ColorDarkBlue { get { return _colorDarkBlue; } set { _colorDarkBlue = value; SetPalette(); } }
public uint ColorDarkGreen { get { return _colorDarkGreen; } set { _colorDarkGreen = value; SetPalette(); } }
public uint ColorMediumBlue { get { return _colorMediumBlue; } set { _colorMediumBlue = value; SetPalette(); } }
public uint ColorBrown { get { return _colorBrown; } set { _colorBrown = value; SetPalette(); } }
public uint ColorLightGrey { get { return _colorLightGrey; } set { _colorLightGrey = value; SetPalette(); } }
public uint ColorGreen { get { return _colorGreen; } set { _colorGreen = value; SetPalette(); } }
public uint ColorAquamarine { get { return _colorAquamarine; } set { _colorAquamarine = value; SetPalette(); } }
public uint ColorDeepRed { get { return _colorDeepRed; } set { _colorDeepRed = value; SetPalette(); } }
public uint ColorPurple { get { return _colorPurple; } set { _colorPurple = value; SetPalette(); } }
public uint ColorDarkGrey { get { return _colorDarkGrey; } set { _colorDarkGrey = value; SetPalette(); } }
public uint ColorLightBlue { get { return _colorLightBlue; } set { _colorLightBlue = value; SetPalette(); } }
public uint ColorOrange { get { return _colorOrange; } set { _colorOrange = value; SetPalette(); } }
public uint ColorPink { get { return _colorPink; } set { _colorPink = value; SetPalette(); } }
public uint ColorYellow { get { return _colorYellow; } set { _colorYellow = value; SetPalette(); } }
public uint ColorWhite { get { return _colorWhite; } set { _colorWhite = value; SetPalette(); } }
public uint ColorMonochrome { get { return _colorMonochrome; } set { _colorMonochrome = value; SetPalette(); } }
public bool IsFullScreen { get { return _isFullScreen; } set { _isFullScreen = value; _videoService.SetFullScreen(_isFullScreen); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorBlack { get { return _colorBlack; } set { _colorBlack = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorDarkBlue { get { return _colorDarkBlue; } set { _colorDarkBlue = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorDarkGreen { get { return _colorDarkGreen; } set { _colorDarkGreen = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorMediumBlue { get { return _colorMediumBlue; } set { _colorMediumBlue = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorBrown { get { return _colorBrown; } set { _colorBrown = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorLightGrey { get { return _colorLightGrey; } set { _colorLightGrey = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorGreen { get { return _colorGreen; } set { _colorGreen = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorAquamarine { get { return _colorAquamarine; } set { _colorAquamarine = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorDeepRed { get { return _colorDeepRed; } set { _colorDeepRed = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorPurple { get { return _colorPurple; } set { _colorPurple = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorDarkGrey { get { return _colorDarkGrey; } set { _colorDarkGrey = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorLightBlue { get { return _colorLightBlue; } set { _colorLightBlue = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorOrange { get { return _colorOrange; } set { _colorOrange = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorPink { get { return _colorPink; } set { _colorPink = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorYellow { get { return _colorYellow; } set { _colorYellow = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorWhite { get { return _colorWhite; } set { _colorWhite = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public uint ColorMonochrome { get { return _colorMonochrome; } set { _colorMonochrome = value; SetPalette(); } }
[Newtonsoft.Json.JsonIgnore]
public bool IsMonochrome { get { return _isMonochrome; } set { _isMonochrome = value; DirtyScreen(); } }
public ScannerOptions ScannerOptions { get { return _scannerOptions; } set { _scannerOptions = value; SetScanner(); } }
@ -1113,7 +1064,7 @@ namespace Jellyfish.Virtu
private Action _resetVSyncEvent;
private Memory _memory;
private VideoService _videoService;
public VideoService VideoService { get; private set; }
private uint _colorBlack;
private uint _colorDarkBlue;
@ -1132,7 +1083,6 @@ namespace Jellyfish.Virtu
private uint _colorYellow;
private uint _colorWhite;
private uint _colorMonochrome;
private bool _isFullScreen;
private bool _isMonochrome;
private bool _isTextInversed;
private ScannerOptions _scannerOptions;

View File

@ -1627,7 +1627,7 @@ namespace Jellyfish.Virtu
public const int ModeE = 0xE;
public const int ModeF = 0xF;
private readonly Action<int>[] FlushRowMode;
private Action<int>[] FlushRowMode;
private const int Width = 560;
private const int Height = VLineEnterVBlank;

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -31,6 +31,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\..\References\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@ -43,6 +46,7 @@
<Compile Include="Cassette.cs" />
<Compile Include="Cpu.cs" />
<Compile Include="CpuData.cs" />
<Compile Include="ExtraConverters.cs" />
<Compile Include="Disk525.cs" />
<Compile Include="DiskDsk.cs" />
<Compile Include="DiskIIController.cs" />
@ -64,12 +68,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\AudioService.cs" />
<Compile Include="Services\DebugService.cs" />
<Compile Include="Services\GamePortService.cs" />
<Compile Include="Services\IsolatedStorageService.cs" />
<Compile Include="Services\KeyboardService.cs" />
<Compile Include="Services\MachineService.cs" />
<Compile Include="Services\MachineServices.cs" />
<Compile Include="Services\StorageService.cs" />
<Compile Include="Services\VideoService.cs" />
<Compile Include="Speaker.cs" />
<Compile Include="Video.cs" />

Binary file not shown.