Convert spaces to tabs in ZX Spectrum and AmstradCPC cores

This commit is contained in:
adelikat 2019-12-06 17:47:59 -06:00
parent fef746dffa
commit 85be6af3d3
112 changed files with 26295 additions and 26291 deletions

View File

@ -3,50 +3,50 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPCHawk: Core Class
/// * Controllers *
/// </summary>
public partial class AmstradCPC
{
/// <summary>
/// The one CPCHawk ControllerDefinition
/// </summary>
public ControllerDefinition AmstradCPCControllerDefinition
{
get
{
ControllerDefinition definition = new ControllerDefinition();
definition.Name = "AmstradCPC Controller";
/// <summary>
/// CPCHawk: Core Class
/// * Controllers *
/// </summary>
public partial class AmstradCPC
{
/// <summary>
/// The one CPCHawk ControllerDefinition
/// </summary>
public ControllerDefinition AmstradCPCControllerDefinition
{
get
{
ControllerDefinition definition = new ControllerDefinition();
definition.Name = "AmstradCPC Controller";
// joysticks
List<string> joys1 = new List<string>
{
// joysticks
List<string> joys1 = new List<string>
{
// P1 Joystick
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Fire1", "P1 Fire2", "P1 Fire3"
};
};
foreach (var s in joys1)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "J1";
}
foreach (var s in joys1)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "J1";
}
List<string> joys2 = new List<string>
{
List<string> joys2 = new List<string>
{
// P2 Joystick
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Fire",
};
};
foreach (var s in joys2)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "J2";
}
foreach (var s in joys2)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "J2";
}
// keyboard
List<string> keys = new List<string>
{
// keyboard
List<string> keys = new List<string>
{
// http://www.cpcwiki.eu/index.php/Programming:Keyboard_scanning
// http://www.cpcwiki.eu/index.php/File:Grimware_cpc464_version3_case_top.jpg
@ -64,66 +64,66 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
"Key CURUP", "Key CURDOWN", "Key CURLEFT", "Key CURRIGHT", "Key COPY",
// Keyboard - Numpad
"Key NUM0", "Key NUM1", "Key NUM2", "Key NUM3", "Key NUM4", "Key NUM5", "Key NUM6", "Key NUM7", "Key NUM8", "Key NUM9", "Key NUMPERIOD", "KEY ENTER"
};
};
foreach (var s in keys)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "Keyboard";
}
foreach (var s in keys)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "Keyboard";
}
// Power functions
List<string> power = new List<string>
{
// Power functions
List<string> power = new List<string>
{
// Power functions
"Reset", "Power"
};
};
foreach (var s in power)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "Power";
}
foreach (var s in power)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "Power";
}
// Datacorder (tape device)
List<string> tape = new List<string>
{
// Datacorder (tape device)
List<string> tape = new List<string>
{
// Tape functions
"Play Tape", "Stop Tape", "RTZ Tape", "Record Tape", "Insert Next Tape",
"Insert Previous Tape", "Next Tape Block", "Prev Tape Block", "Get Tape Status"
};
"Insert Previous Tape", "Next Tape Block", "Prev Tape Block", "Get Tape Status"
};
foreach (var s in tape)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "Datacorder";
}
foreach (var s in tape)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "Datacorder";
}
// Datacorder (tape device)
List<string> disk = new List<string>
{
// Datacorder (tape device)
List<string> disk = new List<string>
{
// Tape functions
"Insert Next Disk", "Insert Previous Disk", /*"Eject Current Disk",*/ "Get Disk Status"
};
};
foreach (var s in disk)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "Amstrad Disk Drive";
}
foreach (var s in disk)
{
definition.BoolButtons.Add(s);
definition.CategoryLabels[s] = "Amstrad Disk Drive";
}
return definition;
}
}
}
return definition;
}
}
}
/// <summary>
/// The possible joystick types
/// </summary>
public enum JoystickType
{
NULL,
Joystick1,
Joystick2
}
/// <summary>
/// The possible joystick types
/// </summary>
public enum JoystickType
{
NULL,
Joystick1,
Joystick2
}
}

View File

@ -4,146 +4,146 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPCHawk: Core Class
/// * IDebugggable *
/// </summary>
public partial class AmstradCPC : IDebuggable
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
return new Dictionary<string, RegisterValue>
{
["A"] = _cpu.Regs[_cpu.A],
["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8),
["B"] = _cpu.Regs[_cpu.B],
["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8),
["C"] = _cpu.Regs[_cpu.C],
["D"] = _cpu.Regs[_cpu.D],
["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8),
["E"] = _cpu.Regs[_cpu.E],
["F"] = _cpu.Regs[_cpu.F],
["H"] = _cpu.Regs[_cpu.H],
["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8),
["I"] = _cpu.Regs[_cpu.I],
["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8),
["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8),
["L"] = _cpu.Regs[_cpu.L],
["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8),
["R"] = _cpu.Regs[_cpu.R],
["Shadow AF"] = _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8),
["Shadow BC"] = _cpu.Regs[_cpu.C_s] + (_cpu.Regs[_cpu.B_s] << 8),
["Shadow DE"] = _cpu.Regs[_cpu.E_s] + (_cpu.Regs[_cpu.D_s] << 8),
["Shadow HL"] = _cpu.Regs[_cpu.L_s] + (_cpu.Regs[_cpu.H_s] << 8),
["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8),
["Flag C"] = _cpu.FlagC,
["Flag N"] = _cpu.FlagN,
["Flag P/V"] = _cpu.FlagP,
["Flag 3rd"] = _cpu.Flag3,
["Flag H"] = _cpu.FlagH,
["Flag 5th"] = _cpu.Flag5,
["Flag Z"] = _cpu.FlagZ,
["Flag S"] = _cpu.FlagS
};
}
/// <summary>
/// CPCHawk: Core Class
/// * IDebugggable *
/// </summary>
public partial class AmstradCPC : IDebuggable
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
return new Dictionary<string, RegisterValue>
{
["A"] = _cpu.Regs[_cpu.A],
["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8),
["B"] = _cpu.Regs[_cpu.B],
["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8),
["C"] = _cpu.Regs[_cpu.C],
["D"] = _cpu.Regs[_cpu.D],
["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8),
["E"] = _cpu.Regs[_cpu.E],
["F"] = _cpu.Regs[_cpu.F],
["H"] = _cpu.Regs[_cpu.H],
["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8),
["I"] = _cpu.Regs[_cpu.I],
["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8),
["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8),
["L"] = _cpu.Regs[_cpu.L],
["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8),
["R"] = _cpu.Regs[_cpu.R],
["Shadow AF"] = _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8),
["Shadow BC"] = _cpu.Regs[_cpu.C_s] + (_cpu.Regs[_cpu.B_s] << 8),
["Shadow DE"] = _cpu.Regs[_cpu.E_s] + (_cpu.Regs[_cpu.D_s] << 8),
["Shadow HL"] = _cpu.Regs[_cpu.L_s] + (_cpu.Regs[_cpu.H_s] << 8),
["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8),
["Flag C"] = _cpu.FlagC,
["Flag N"] = _cpu.FlagN,
["Flag P/V"] = _cpu.FlagP,
["Flag 3rd"] = _cpu.Flag3,
["Flag H"] = _cpu.FlagH,
["Flag 5th"] = _cpu.Flag5,
["Flag Z"] = _cpu.FlagZ,
["Flag S"] = _cpu.FlagS
};
}
public void SetCpuRegister(string register, int value)
{
switch (register)
{
default:
throw new InvalidOperationException();
case "A":
_cpu.Regs[_cpu.A] = (ushort)value;
break;
case "AF":
_cpu.Regs[_cpu.F] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00);
break;
case "B":
_cpu.Regs[_cpu.B] = (ushort)value;
break;
case "BC":
_cpu.Regs[_cpu.C] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00);
break;
case "C":
_cpu.Regs[_cpu.C] = (ushort)value;
break;
case "D":
_cpu.Regs[_cpu.D] = (ushort)value;
break;
case "DE":
_cpu.Regs[_cpu.E] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00);
break;
case "E":
_cpu.Regs[_cpu.E] = (ushort)value;
break;
case "F":
_cpu.Regs[_cpu.F] = (ushort)value;
break;
case "H":
_cpu.Regs[_cpu.H] = (ushort)value;
break;
case "HL":
_cpu.Regs[_cpu.L] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00);
break;
case "I":
_cpu.Regs[_cpu.I] = (ushort)value;
break;
case "IX":
_cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00);
break;
case "IY":
_cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00);
break;
case "L":
_cpu.Regs[_cpu.L] = (ushort)value;
break;
case "PC":
_cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00);
break;
case "R":
_cpu.Regs[_cpu.R] = (ushort)value;
break;
case "Shadow AF":
_cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00);
break;
case "Shadow BC":
_cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00);
break;
case "Shadow DE":
_cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00);
break;
case "Shadow HL":
_cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00);
break;
case "SP":
_cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00);
break;
}
}
public void SetCpuRegister(string register, int value)
{
switch (register)
{
default:
throw new InvalidOperationException();
case "A":
_cpu.Regs[_cpu.A] = (ushort)value;
break;
case "AF":
_cpu.Regs[_cpu.F] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00);
break;
case "B":
_cpu.Regs[_cpu.B] = (ushort)value;
break;
case "BC":
_cpu.Regs[_cpu.C] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00);
break;
case "C":
_cpu.Regs[_cpu.C] = (ushort)value;
break;
case "D":
_cpu.Regs[_cpu.D] = (ushort)value;
break;
case "DE":
_cpu.Regs[_cpu.E] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00);
break;
case "E":
_cpu.Regs[_cpu.E] = (ushort)value;
break;
case "F":
_cpu.Regs[_cpu.F] = (ushort)value;
break;
case "H":
_cpu.Regs[_cpu.H] = (ushort)value;
break;
case "HL":
_cpu.Regs[_cpu.L] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00);
break;
case "I":
_cpu.Regs[_cpu.I] = (ushort)value;
break;
case "IX":
_cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00);
break;
case "IY":
_cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00);
break;
case "L":
_cpu.Regs[_cpu.L] = (ushort)value;
break;
case "PC":
_cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00);
break;
case "R":
_cpu.Regs[_cpu.R] = (ushort)value;
break;
case "Shadow AF":
_cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00);
break;
case "Shadow BC":
_cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00);
break;
case "Shadow DE":
_cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00);
break;
case "Shadow HL":
_cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00);
break;
case "SP":
_cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00);
break;
}
}
public IMemoryCallbackSystem MemoryCallbacks { get; }
public IMemoryCallbackSystem MemoryCallbacks { get; }
public bool CanStep(StepType type) => false;
public bool CanStep(StepType type) => false;
[FeatureNotImplemented]
public void Step(StepType type)
{
throw new NotImplementedException();
}
[FeatureNotImplemented]
public void Step(StepType type)
{
throw new NotImplementedException();
}
public long TotalExecutedCycles => _cpu.TotalExecutedCycles;
}
public long TotalExecutedCycles => _cpu.TotalExecutedCycles;
}
}

View File

@ -2,84 +2,84 @@
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPCHawk: Core Class
/// * IEmulator *
/// </summary>
public partial class AmstradCPC : IEmulator
{
public IEmulatorServiceProvider ServiceProvider { get; }
/// <summary>
/// CPCHawk: Core Class
/// * IEmulator *
/// </summary>
public partial class AmstradCPC : IEmulator
{
public IEmulatorServiceProvider ServiceProvider { get; }
public ControllerDefinition ControllerDefinition { get; set; }
public ControllerDefinition ControllerDefinition { get; set; }
public bool FrameAdvance(IController controller, bool render, bool renderSound)
{
_controller = controller;
{
_controller = controller;
bool ren = render;
bool renSound = renderSound;
bool ren = render;
bool renSound = renderSound;
if (DeterministicEmulation)
{
ren = true;
renSound = true;
}
if (DeterministicEmulation)
{
ren = true;
renSound = true;
}
_isLag = true;
_isLag = true;
if (_tracer.Enabled)
{
_cpu.TraceCallback = s => _tracer.Put(s);
}
else
{
_cpu.TraceCallback = null;
}
if (_tracer.Enabled)
{
_cpu.TraceCallback = s => _tracer.Put(s);
}
else
{
_cpu.TraceCallback = null;
}
_machine.ExecuteFrame(ren, renSound);
_machine.ExecuteFrame(ren, renSound);
if (_isLag)
{
_lagCount++;
}
if (_isLag)
{
_lagCount++;
}
return true;
}
}
public int Frame
{
get
{
if (_machine == null)
return 0;
else
return _machine.FrameCount;
}
}
public int Frame
{
get
{
if (_machine == null)
return 0;
else
return _machine.FrameCount;
}
}
public string SystemId => "AmstradCPC";
public string SystemId => "AmstradCPC";
private bool deterministicEmulation;
public bool DeterministicEmulation
{
get { return deterministicEmulation; }
}
private bool deterministicEmulation;
public bool DeterministicEmulation
{
get { return deterministicEmulation; }
}
public void ResetCounters()
{
_machine.FrameCount = 0;
_lagCount = 0;
_isLag = false;
}
public void ResetCounters()
{
_machine.FrameCount = 0;
_lagCount = 0;
_isLag = false;
}
public CoreComm CoreComm { get; }
public CoreComm CoreComm { get; }
public void Dispose()
{
if (_machine != null)
{
_machine = null;
}
}
}
public void Dispose()
{
if (_machine != null)
{
_machine = null;
}
}
}
}

View File

@ -5,74 +5,74 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPCHawk: Core Class
/// * Memory Domains *
/// </summary>
public partial class AmstradCPC
{
internal IMemoryDomains memoryDomains;
private readonly Dictionary<string, MemoryDomainByteArray> _byteArrayDomains = new Dictionary<string, MemoryDomainByteArray>();
private bool _memoryDomainsInit = false;
/// <summary>
/// CPCHawk: Core Class
/// * Memory Domains *
/// </summary>
public partial class AmstradCPC
{
internal IMemoryDomains memoryDomains;
private readonly Dictionary<string, MemoryDomainByteArray> _byteArrayDomains = new Dictionary<string, MemoryDomainByteArray>();
private bool _memoryDomainsInit = false;
private void SetupMemoryDomains()
{
var domains = new List<MemoryDomain>
{
new MemoryDomainDelegate("System Bus", 0x10000, MemoryDomain.Endian.Little,
(addr) =>
{
if (addr < 0 || addr >= 65536)
{
throw new ArgumentOutOfRangeException();
}
return _machine.ReadBus((ushort)addr);
},
(addr, value) =>
{
if (addr < 0 || addr >= 65536)
{
throw new ArgumentOutOfRangeException();
}
_machine.WriteBus((ushort)addr, value);
}, 1)
};
private void SetupMemoryDomains()
{
var domains = new List<MemoryDomain>
{
new MemoryDomainDelegate("System Bus", 0x10000, MemoryDomain.Endian.Little,
(addr) =>
{
if (addr < 0 || addr >= 65536)
{
throw new ArgumentOutOfRangeException();
}
return _machine.ReadBus((ushort)addr);
},
(addr, value) =>
{
if (addr < 0 || addr >= 65536)
{
throw new ArgumentOutOfRangeException();
}
_machine.WriteBus((ushort)addr, value);
}, 1)
};
SyncAllByteArrayDomains();
SyncAllByteArrayDomains();
memoryDomains = new MemoryDomainList(_byteArrayDomains.Values.Concat(domains).ToList());
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(memoryDomains);
memoryDomains = new MemoryDomainList(_byteArrayDomains.Values.Concat(domains).ToList());
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(memoryDomains);
_memoryDomainsInit = true;
}
_memoryDomainsInit = true;
}
private void SyncAllByteArrayDomains()
{
SyncByteArrayDomain("ROMLower", _machine.ROMLower);
SyncByteArrayDomain("ROM0", _machine.ROM0);
SyncByteArrayDomain("ROM7", _machine.ROM7);
SyncByteArrayDomain("RAM0", _machine.RAM0);
SyncByteArrayDomain("RAM1", _machine.RAM1);
SyncByteArrayDomain("RAM2", _machine.RAM2);
SyncByteArrayDomain("RAM3", _machine.RAM3);
SyncByteArrayDomain("RAM4", _machine.RAM4);
SyncByteArrayDomain("RAM5", _machine.RAM5);
SyncByteArrayDomain("RAM6", _machine.RAM6);
SyncByteArrayDomain("RAM7", _machine.RAM7);
}
private void SyncAllByteArrayDomains()
{
SyncByteArrayDomain("ROMLower", _machine.ROMLower);
SyncByteArrayDomain("ROM0", _machine.ROM0);
SyncByteArrayDomain("ROM7", _machine.ROM7);
SyncByteArrayDomain("RAM0", _machine.RAM0);
SyncByteArrayDomain("RAM1", _machine.RAM1);
SyncByteArrayDomain("RAM2", _machine.RAM2);
SyncByteArrayDomain("RAM3", _machine.RAM3);
SyncByteArrayDomain("RAM4", _machine.RAM4);
SyncByteArrayDomain("RAM5", _machine.RAM5);
SyncByteArrayDomain("RAM6", _machine.RAM6);
SyncByteArrayDomain("RAM7", _machine.RAM7);
}
private void SyncByteArrayDomain(string name, byte[] data)
{
if (_memoryDomainsInit || _byteArrayDomains.ContainsKey(name))
{
var m = _byteArrayDomains[name];
m.Data = data;
}
else
{
var m = new MemoryDomainByteArray(name, MemoryDomain.Endian.Little, data, true, 1);
_byteArrayDomains.Add(name, m);
}
}
}
private void SyncByteArrayDomain(string name, byte[] data)
{
if (_memoryDomainsInit || _byteArrayDomains.ContainsKey(name))
{
var m = _byteArrayDomains[name];
m.Data = data;
}
else
{
var m = new MemoryDomainByteArray(name, MemoryDomain.Endian.Little, data, true, 1);
_byteArrayDomains.Add(name, m);
}
}
}
}

View File

@ -6,190 +6,190 @@ using System.Text;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPCHawk: Core Class
/// * ISettable *
/// </summary>
public partial class AmstradCPC : ISettable<AmstradCPC.AmstradCPCSettings, AmstradCPC.AmstradCPCSyncSettings>
{
internal AmstradCPCSettings Settings = new AmstradCPCSettings();
internal AmstradCPCSyncSettings SyncSettings = new AmstradCPCSyncSettings();
/// <summary>
/// CPCHawk: Core Class
/// * ISettable *
/// </summary>
public partial class AmstradCPC : ISettable<AmstradCPC.AmstradCPCSettings, AmstradCPC.AmstradCPCSyncSettings>
{
internal AmstradCPCSettings Settings = new AmstradCPCSettings();
internal AmstradCPCSyncSettings SyncSettings = new AmstradCPCSyncSettings();
public AmstradCPCSettings GetSettings()
{
return Settings.Clone();
}
public AmstradCPCSettings GetSettings()
{
return Settings.Clone();
}
public AmstradCPCSyncSettings GetSyncSettings()
{
return SyncSettings.Clone();
}
public AmstradCPCSyncSettings GetSyncSettings()
{
return SyncSettings.Clone();
}
public bool PutSettings(AmstradCPCSettings o)
{
// restore user settings to devices
if (_machine != null && _machine.AYDevice != null)
{
((AY38912)_machine.AYDevice as AY38912).PanningConfiguration = o.AYPanConfig;
_machine.AYDevice.Volume = o.AYVolume;
}
if (_machine != null && _machine.TapeBuzzer != null)
{
((Beeper)_machine.TapeBuzzer as Beeper).Volume = o.TapeVolume;
}
public bool PutSettings(AmstradCPCSettings o)
{
Settings = o;
// restore user settings to devices
if (_machine != null && _machine.AYDevice != null)
{
((AY38912)_machine.AYDevice as AY38912).PanningConfiguration = o.AYPanConfig;
_machine.AYDevice.Volume = o.AYVolume;
}
if (_machine != null && _machine.TapeBuzzer != null)
{
((Beeper)_machine.TapeBuzzer as Beeper).Volume = o.TapeVolume;
}
return false;
}
public bool PutSyncSettings(AmstradCPCSyncSettings o)
{
bool ret = AmstradCPCSyncSettings.NeedsReboot(SyncSettings, o);
SyncSettings = o;
return ret;
}
Settings = o;
public class AmstradCPCSettings
{
[DisplayName("AY-3-8912 Panning Config")]
[Description("Set the PSG panning configuration.\nThe chip has 3 audio channels that can be outputed in different configurations")]
[DefaultValue(AY38912.AYPanConfig.ABC)]
public AY38912.AYPanConfig AYPanConfig { get; set; }
return false;
}
[DisplayName("AY-3-8912 Volume")]
[Description("The AY chip volume")]
[DefaultValue(75)]
public int AYVolume { get; set; }
public bool PutSyncSettings(AmstradCPCSyncSettings o)
{
bool ret = AmstradCPCSyncSettings.NeedsReboot(SyncSettings, o);
SyncSettings = o;
return ret;
}
[DisplayName("Core OSD Message Verbosity")]
[Description("Full: Display all GUI messages\nMedium: Display only emulator/device generated messages\nNone: Show no messages")]
[DefaultValue(OSDVerbosity.Medium)]
public OSDVerbosity OSDMessageVerbosity { get; set; }
public class AmstradCPCSettings
{
[DisplayName("AY-3-8912 Panning Config")]
[Description("Set the PSG panning configuration.\nThe chip has 3 audio channels that can be outputed in different configurations")]
[DefaultValue(AY38912.AYPanConfig.ABC)]
public AY38912.AYPanConfig AYPanConfig { get; set; }
[DisplayName("Tape Loading Volume")]
[Description("The buzzer volume when the tape is playing")]
[DefaultValue(50)]
public int TapeVolume { get; set; }
[DisplayName("AY-3-8912 Volume")]
[Description("The AY chip volume")]
[DefaultValue(75)]
public int AYVolume { get; set; }
public AmstradCPCSettings Clone()
{
return (AmstradCPCSettings)MemberwiseClone();
}
[DisplayName("Core OSD Message Verbosity")]
[Description("Full: Display all GUI messages\nMedium: Display only emulator/device generated messages\nNone: Show no messages")]
[DefaultValue(OSDVerbosity.Medium)]
public OSDVerbosity OSDMessageVerbosity { get; set; }
public AmstradCPCSettings()
{
BizHawk.Common.SettingsUtil.SetDefaultValues(this);
}
}
[DisplayName("Tape Loading Volume")]
[Description("The buzzer volume when the tape is playing")]
[DefaultValue(50)]
public int TapeVolume { get; set; }
public class AmstradCPCSyncSettings
{
[DisplayName("Deterministic Emulation")]
[Description("If true, the core agrees to behave in a completely deterministic manner")]
[DefaultValue(true)]
public bool DeterministicEmulation { get; set; }
public AmstradCPCSettings Clone()
{
return (AmstradCPCSettings)MemberwiseClone();
}
[DisplayName("CPC Model")]
[Description("The model of Amstrad CPC machine to be emulated")]
[DefaultValue(MachineType.CPC464)]
public MachineType MachineType { get; set; }
public AmstradCPCSettings()
{
BizHawk.Common.SettingsUtil.SetDefaultValues(this);
}
}
[DisplayName("Auto Start/Stop Tape")]
[Description("If true, CPCHawk will automatically start and stop the tape when the tape motor is triggered")]
[DefaultValue(true)]
public bool AutoStartStopTape { get; set; }
public class AmstradCPCSyncSettings
{
[DisplayName("Deterministic Emulation")]
[Description("If true, the core agrees to behave in a completely deterministic manner")]
[DefaultValue(true)]
public bool DeterministicEmulation { get; set; }
[DisplayName("Border type")]
[Description("Select how to show the border area")]
[DefaultValue(BorderType.Uniform)]
public BorderType BorderType { get; set; }
[DisplayName("CPC Model")]
[Description("The model of Amstrad CPC machine to be emulated")]
[DefaultValue(MachineType.CPC464)]
public MachineType MachineType { get; set; }
public AmstradCPCSyncSettings Clone()
{
return (AmstradCPCSyncSettings)MemberwiseClone();
}
[DisplayName("Auto Start/Stop Tape")]
[Description("If true, CPCHawk will automatically start and stop the tape when the tape motor is triggered")]
[DefaultValue(true)]
public bool AutoStartStopTape { get; set; }
public AmstradCPCSyncSettings()
{
BizHawk.Common.SettingsUtil.SetDefaultValues(this);
}
[DisplayName("Border type")]
[Description("Select how to show the border area")]
[DefaultValue(BorderType.Uniform)]
public BorderType BorderType { get; set; }
public static bool NeedsReboot(AmstradCPCSyncSettings x, AmstradCPCSyncSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
}
public AmstradCPCSyncSettings Clone()
{
return (AmstradCPCSyncSettings)MemberwiseClone();
}
/// <summary>
/// Verbosity of the CPCHawk generated OSD messages
/// </summary>
public enum OSDVerbosity
{
/// <summary>
/// Show all OSD messages
/// </summary>
Full,
/// <summary>
/// Only show machine/device generated messages
/// </summary>
Medium,
/// <summary>
/// No core-driven OSD messages
/// </summary>
None
}
public AmstradCPCSyncSettings()
{
BizHawk.Common.SettingsUtil.SetDefaultValues(this);
}
/// <summary>
/// Provides information on each emulated machine
/// </summary>
public class CPCMachineMetaData
{
public MachineType MachineType { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Released { get; set; }
public string CPU { get; set; }
public string Memory { get; set; }
public string Video { get; set; }
public string Audio { get; set; }
public string Media { get; set; }
public string OtherMisc { get; set; }
public static bool NeedsReboot(AmstradCPCSyncSettings x, AmstradCPCSyncSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
}
/// <summary>
/// Verbosity of the CPCHawk generated OSD messages
/// </summary>
public enum OSDVerbosity
{
/// <summary>
/// Show all OSD messages
/// </summary>
Full,
/// <summary>
/// Only show machine/device generated messages
/// </summary>
Medium,
/// <summary>
/// No core-driven OSD messages
/// </summary>
None
}
/// <summary>
/// Provides information on each emulated machine
/// </summary>
public class CPCMachineMetaData
{
public MachineType MachineType { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Released { get; set; }
public string CPU { get; set; }
public string Memory { get; set; }
public string Video { get; set; }
public string Audio { get; set; }
public string Media { get; set; }
public string OtherMisc { get; set; }
Dictionary<string, string> Data = new Dictionary<string, string>();
public static CPCMachineMetaData GetMetaObject(MachineType type)
{
CPCMachineMetaData m = new CPCMachineMetaData();
m.MachineType = type;
{
CPCMachineMetaData m = new CPCMachineMetaData();
m.MachineType = type;
switch (type)
{
case MachineType.CPC464:
m.Name = "Amstrad CPC 464";
m.Description = "The CPC 464 was the first personal home computer built by Amstrad in 1984. ";
m.Description += "The 464 was popular with consumers for various reasons. Aside from the joystick port, the computer, keyboard, and tape deck were all combined into one unit.";
m.Released = "1984";
m.CPU = "Zilog Z80A @ 4MHz";
m.Memory = "64KB RAM / 32KB ROM";
m.Video = "Amstrad Gate Array @ 16Mhz & CRCT @ 1Mhz";
m.Audio = "General Instruments AY-3-8912 PSG (3ch)";
m.Media = "Cassette Tape (via built-in Datacorder)";
break;
case MachineType.CPC6128:
m.Name = "Amstrad CPC 6128";
m.Description = "The CPC6128 features 128 KB RAM and an internal 3-inch floppy disk drive. ";
m.Description += "Aside from various hardware and firmware improvements, one of the CPC6128's most prominent features is the compatibility with the CP/M+ operating system that rendered it attractive for business uses.";
m.Released = "1985";
m.CPU = "Zilog Z80A @ 4MHz";
m.Memory = "64KB RAM / 32KB ROM";
m.Video = "Amstrad Gate Array @ 16Mhz & CRCT @ 1Mhz";
m.Audio = "General Instruments AY-3-8912 PSG (3ch)";
m.Media = "3\" Floppy Disk (via built-in Floppy Drive) & Cassette Tape (via external cassette player)";
break;
}
switch (type)
{
case MachineType.CPC464:
m.Name = "Amstrad CPC 464";
m.Description = "The CPC 464 was the first personal home computer built by Amstrad in 1984. ";
m.Description += "The 464 was popular with consumers for various reasons. Aside from the joystick port, the computer, keyboard, and tape deck were all combined into one unit.";
m.Released = "1984";
m.CPU = "Zilog Z80A @ 4MHz";
m.Memory = "64KB RAM / 32KB ROM";
m.Video = "Amstrad Gate Array @ 16Mhz & CRCT @ 1Mhz";
m.Audio = "General Instruments AY-3-8912 PSG (3ch)";
m.Media = "Cassette Tape (via built-in Datacorder)";
break;
case MachineType.CPC6128:
m.Name = "Amstrad CPC 6128";
m.Description = "The CPC6128 features 128 KB RAM and an internal 3-inch floppy disk drive. ";
m.Description += "Aside from various hardware and firmware improvements, one of the CPC6128's most prominent features is the compatibility with the CP/M+ operating system that rendered it attractive for business uses.";
m.Released = "1985";
m.CPU = "Zilog Z80A @ 4MHz";
m.Memory = "64KB RAM / 32KB ROM";
m.Video = "Amstrad Gate Array @ 16Mhz & CRCT @ 1Mhz";
m.Audio = "General Instruments AY-3-8912 PSG (3ch)";
m.Media = "3\" Floppy Disk (via built-in Floppy Drive) & Cassette Tape (via external cassette player)";
break;
}
m.Data.Add(AmstradCPC.GetMemberName((CPCMachineMetaData c) => c.Name), m.Name.Trim());
m.Data.Add(AmstradCPC.GetMemberName((CPCMachineMetaData c) => c.Description), m.Description.Trim());
@ -201,7 +201,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
m.Data.Add(AmstradCPC.GetMemberName((CPCMachineMetaData c) => c.Media), m.Media.Trim());
return m;
}
}
public static string GetMetaString(MachineType type)
{
@ -275,75 +275,75 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
public static string GetMetaStringOld(MachineType type)
{
var m = GetMetaObject(type);
{
var m = GetMetaObject(type);
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder();
sb.Append(m.Name);
sb.Append("\n");
sb.Append("-----------------------------------------------------------------\n");
// Release
sb.Append("Released:");
sb.Append(" ");
sb.Append(m.Released);
sb.Append("\n");
// CPU
sb.Append("CPU:");
sb.Append(" ");
sb.Append(m.CPU);
sb.Append("\n");
// Memory
sb.Append("Memory:");
sb.Append(" ");
sb.Append(m.Memory);
sb.Append("\n");
// Video
sb.Append("Video:");
sb.Append(" ");
sb.Append(m.Video);
sb.Append("\n");
// Audio
sb.Append("Audio:");
sb.Append(" ");
sb.Append(m.Audio);
sb.Append("\n");
// Audio
sb.Append("Media:");
sb.Append(" ");
sb.Append(m.Media);
sb.Append("\n");
sb.Append(m.Name);
sb.Append("\n");
sb.Append("-----------------------------------------------------------------\n");
// Release
sb.Append("Released:");
sb.Append(" ");
sb.Append(m.Released);
sb.Append("\n");
// CPU
sb.Append("CPU:");
sb.Append(" ");
sb.Append(m.CPU);
sb.Append("\n");
// Memory
sb.Append("Memory:");
sb.Append(" ");
sb.Append(m.Memory);
sb.Append("\n");
// Video
sb.Append("Video:");
sb.Append(" ");
sb.Append(m.Video);
sb.Append("\n");
// Audio
sb.Append("Audio:");
sb.Append(" ");
sb.Append(m.Audio);
sb.Append("\n");
// Audio
sb.Append("Media:");
sb.Append(" ");
sb.Append(m.Media);
sb.Append("\n");
sb.Append("-----------------------------------------------------------------\n");
// description
sb.Append(m.Description);
if (m.OtherMisc != null)
sb.Append("\n" + m.OtherMisc);
sb.Append("-----------------------------------------------------------------\n");
// description
sb.Append(m.Description);
if (m.OtherMisc != null)
sb.Append("\n" + m.OtherMisc);
return sb.ToString();
return sb.ToString();
}
}
}
}
/// <summary>
/// The size of the Spectrum border
/// </summary>
public enum BorderType
{
/// <summary>
/// Attempts to equalise the border areas
/// </summary>
Uniform,
/// <summary>
/// The size of the Spectrum border
/// </summary>
public enum BorderType
{
/// <summary>
/// Attempts to equalise the border areas
/// </summary>
Uniform,
/// <summary>
/// Pretty much the signal the gate array is generating (looks shit)
/// </summary>
Uncropped,
/// <summary>
/// Pretty much the signal the gate array is generating (looks shit)
/// </summary>
Uncropped,
/// <summary>
/// Top and bottom border removed so that the result is *almost* 16:9
/// </summary>
Widescreen,
}
}
/// <summary>
/// Top and bottom border removed so that the result is *almost* 16:9
/// </summary>
Widescreen,
}
}
}

View File

@ -4,94 +4,94 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPCHawk: Core Class
/// * IStatable *
/// </summary>
public partial class AmstradCPC : IStatable
{
public bool BinarySaveStatesPreferred => true;
/// <summary>
/// CPCHawk: Core Class
/// * IStatable *
/// </summary>
public partial class AmstradCPC : IStatable
{
public bool BinarySaveStatesPreferred => true;
public void SaveStateText(TextWriter writer)
{
SyncState(new Serializer(writer));
}
{
SyncState(new Serializer(writer));
}
public void LoadStateText(TextReader reader)
{
SyncState(new Serializer(reader));
}
public void LoadStateText(TextReader reader)
{
SyncState(new Serializer(reader));
}
public void SaveStateBinary(BinaryWriter bw)
{
SyncState(new Serializer(bw));
}
public void SaveStateBinary(BinaryWriter bw)
{
SyncState(new Serializer(bw));
}
public void LoadStateBinary(BinaryReader br)
{
SyncState(new Serializer(br));
}
public void LoadStateBinary(BinaryReader br)
{
SyncState(new Serializer(br));
}
public byte[] SaveStateBinary()
{
using var ms = new MemoryStream();
public byte[] SaveStateBinary()
{
using var ms = new MemoryStream();
using var bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
}
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
}
private void SyncState(Serializer ser)
{
byte[] core = null;
if (ser.IsWriter)
{
var ms = new MemoryStream();
ms.Close();
core = ms.ToArray();
}
private void SyncState(Serializer ser)
{
byte[] core = null;
if (ser.IsWriter)
{
var ms = new MemoryStream();
ms.Close();
core = ms.ToArray();
}
if (ser.IsWriter)
{
ser.SyncEnum(nameof(_machineType), ref _machineType);
if (ser.IsWriter)
{
ser.SyncEnum(nameof(_machineType), ref _machineType);
_cpu.SyncState(ser);
ser.BeginSection(nameof(AmstradCPC));
_machine.SyncState(ser);
ser.Sync("Frame", ref _machine.FrameCount);
ser.Sync("LagCount", ref _lagCount);
ser.Sync("IsLag", ref _isLag);
ser.EndSection();
}
_cpu.SyncState(ser);
ser.BeginSection(nameof(AmstradCPC));
_machine.SyncState(ser);
ser.Sync("Frame", ref _machine.FrameCount);
ser.Sync("LagCount", ref _lagCount);
ser.Sync("IsLag", ref _isLag);
ser.EndSection();
}
if (ser.IsReader)
{
var tmpM = _machineType;
ser.SyncEnum(nameof(_machineType), ref _machineType);
if (tmpM != _machineType && _machineType.ToString() != "72")
{
string msg = "SAVESTATE FAILED TO LOAD!!\n\n";
msg += "Current Configuration: " + tmpM.ToString();
msg += "\n";
msg += "Saved Configuration: " + _machineType.ToString();
msg += "\n\n";
msg += "If you wish to load this SaveState ensure that you have the correct machine configuration selected, reboot the core, then try again.";
CoreComm.ShowMessage(msg);
_machineType = tmpM;
}
else
{
_cpu.SyncState(ser);
ser.BeginSection(nameof(AmstradCPC));
_machine.SyncState(ser);
ser.Sync("Frame", ref _machine.FrameCount);
ser.Sync("LagCount", ref _lagCount);
ser.Sync("IsLag", ref _isLag);
ser.EndSection();
if (ser.IsReader)
{
var tmpM = _machineType;
ser.SyncEnum(nameof(_machineType), ref _machineType);
if (tmpM != _machineType && _machineType.ToString() != "72")
{
string msg = "SAVESTATE FAILED TO LOAD!!\n\n";
msg += "Current Configuration: " + tmpM.ToString();
msg += "\n";
msg += "Saved Configuration: " + _machineType.ToString();
msg += "\n\n";
msg += "If you wish to load this SaveState ensure that you have the correct machine configuration selected, reboot the core, then try again.";
CoreComm.ShowMessage(msg);
_machineType = tmpM;
}
else
{
_cpu.SyncState(ser);
ser.BeginSection(nameof(AmstradCPC));
_machine.SyncState(ser);
ser.Sync("Frame", ref _machine.FrameCount);
ser.Sync("LagCount", ref _lagCount);
ser.Sync("IsLag", ref _isLag);
ser.EndSection();
SyncAllByteArrayDomains();
}
}
}
}
SyncAllByteArrayDomains();
}
}
}
}
}

View File

@ -3,27 +3,27 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPCHawk: Core Class
/// * IInputPollable *
/// </summary>
public partial class AmstradCPC : IInputPollable
{
public int LagCount
{
get { return _lagCount; }
set { _lagCount = value; }
}
/// <summary>
/// CPCHawk: Core Class
/// * IInputPollable *
/// </summary>
public partial class AmstradCPC : IInputPollable
{
public int LagCount
{
get { return _lagCount; }
set { _lagCount = value; }
}
public bool IsLagFrame
{
get { return _isLag; }
set { _isLag = value; }
}
public bool IsLagFrame
{
get { return _isLag; }
set { _isLag = value; }
}
public IInputCallbackSystem InputCallbacks { get; }
public IInputCallbackSystem InputCallbacks { get; }
private int _lagCount = 0;
private bool _isLag = false;
}
private int _lagCount = 0;
private bool _isLag = false;
}
}

View File

@ -4,127 +4,127 @@ using System.Text;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPCHawk: Core Class
/// * Handles all messaging (OSD) operations *
/// </summary>
public partial class AmstradCPC
{
/// <summary>
/// Writes a message to the OSD
/// </summary>
public void SendMessage(string message, MessageCategory category)
{
if (!CheckMessageSettings(category))
return;
/// <summary>
/// CPCHawk: Core Class
/// * Handles all messaging (OSD) operations *
/// </summary>
public partial class AmstradCPC
{
/// <summary>
/// Writes a message to the OSD
/// </summary>
public void SendMessage(string message, MessageCategory category)
{
if (!CheckMessageSettings(category))
return;
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder();
switch (category)
{
case MessageCategory.Tape:
sb.Append("DATACORDER: ");
sb.Append(message);
break;
case MessageCategory.Input:
sb.Append("INPUT DETECTED: ");
sb.Append(message);
break;
case MessageCategory.Disk:
sb.Append("DISK DRIVE: ");
sb.Append(message);
break;
case MessageCategory.Emulator:
case MessageCategory.Misc:
sb.Append("CPCHAWK: ");
sb.Append(message);
break;
}
switch (category)
{
case MessageCategory.Tape:
sb.Append("DATACORDER: ");
sb.Append(message);
break;
case MessageCategory.Input:
sb.Append("INPUT DETECTED: ");
sb.Append(message);
break;
case MessageCategory.Disk:
sb.Append("DISK DRIVE: ");
sb.Append(message);
break;
case MessageCategory.Emulator:
case MessageCategory.Misc:
sb.Append("CPCHAWK: ");
sb.Append(message);
break;
}
CoreComm.Notify(sb.ToString());
}
CoreComm.Notify(sb.ToString());
}
#region Input Message Methods
#region Input Message Methods
/// <summary>
/// Called when certain input presses are detected
/// </summary>
public void OSD_FireInputMessage(string input)
{
StringBuilder sb = new StringBuilder();
sb.Append(input);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Input);
}
/// <summary>
/// Called when certain input presses are detected
/// </summary>
public void OSD_FireInputMessage(string input)
{
StringBuilder sb = new StringBuilder();
sb.Append(input);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Input);
}
#endregion
#endregion
#region DiskDevice Message Methods
#region DiskDevice Message Methods
/// <summary>
/// Disk message that is fired on core init
/// </summary>
public void OSD_DiskInit()
{
StringBuilder sb = new StringBuilder();
if (_machine.diskImages != null && _machine.UPDDiskDevice != null)
{
sb.Append("Disk Media Imported (count: " + _machine.diskImages.Count() + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Emulator);
}
}
/// <summary>
/// Disk message that is fired on core init
/// </summary>
public void OSD_DiskInit()
{
StringBuilder sb = new StringBuilder();
if (_machine.diskImages != null && _machine.UPDDiskDevice != null)
{
sb.Append("Disk Media Imported (count: " + _machine.diskImages.Count() + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Emulator);
}
}
/// <summary>
/// Disk message that is fired when a new disk is inserted into the drive
/// </summary>
public void OSD_DiskInserted()
{
StringBuilder sb = new StringBuilder();
/// <summary>
/// Disk message that is fired when a new disk is inserted into the drive
/// </summary>
public void OSD_DiskInserted()
{
StringBuilder sb = new StringBuilder();
if (_machine.UPDDiskDevice == null)
{
sb.Append("No Drive Present");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
return;
}
if (_machine.UPDDiskDevice == null)
{
sb.Append("No Drive Present");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
return;
}
sb.Append("DISK INSERTED (" + _machine.DiskMediaIndex + ": " + _diskInfo[_machine.DiskMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
}
sb.Append("DISK INSERTED (" + _machine.DiskMediaIndex + ": " + _diskInfo[_machine.DiskMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
}
/// <summary>
/// Tape message that prints the current status of the tape device
/// </summary>
public void OSD_ShowDiskStatus()
{
StringBuilder sb = new StringBuilder();
/// <summary>
/// Tape message that prints the current status of the tape device
/// </summary>
public void OSD_ShowDiskStatus()
{
StringBuilder sb = new StringBuilder();
if (_machine.UPDDiskDevice == null)
{
sb.Append("No Drive Present");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
return;
}
if (_machine.UPDDiskDevice == null)
{
sb.Append("No Drive Present");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
return;
}
if (_diskInfo.Count == 0)
{
sb.Append("No Disk Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
return;
}
if (_diskInfo.Count == 0)
{
sb.Append("No Disk Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
return;
}
if (_machine.UPDDiskDevice != null)
{
if (_machine.UPDDiskDevice.DiskPointer == null)
{
sb.Append("No Disk Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
return;
}
if (_machine.UPDDiskDevice != null)
{
if (_machine.UPDDiskDevice.DiskPointer == null)
{
sb.Append("No Disk Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
return;
}
sb.Append("Disk: " + _machine.DiskMediaIndex + ": " + _diskInfo[_machine.DiskMediaIndex].Name);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
sb.Clear();
/*
sb.Append("Disk: " + _machine.DiskMediaIndex + ": " + _diskInfo[_machine.DiskMediaIndex].Name);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
sb.Clear();
/*
string protection = "None";
protection = Enum.GetName(typeof(ProtectionType), _machine.UPDDiskDevice.DiskPointer.Protection);
if (protection == "None")
@ -135,384 +135,384 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
sb.Clear();
*/
sb.Append("Status: ");
if (_machine.UPDDiskDevice.DriveLight)
sb.Append("READING/WRITING DATA");
else
sb.Append("UNKNOWN");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
sb.Clear();
}
}
#endregion
#region TapeDevice Message Methods
/// <summary>
/// Tape message that is fired on core init
/// </summary>
public void OSD_TapeInit()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("Tape Media Imported (count: " + _tapeInfo.Count() + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Emulator);
}
/// <summary>
/// Tape message that is fired when tape is playing
/// </summary>
public void OSD_TapeMotorActive()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("MOTOR ON (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when tape is playing
/// </summary>
public void OSD_TapeMotorInactive()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("MOTOR OFF (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when tape is playing
/// </summary>
public void OSD_TapePlaying()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("PLAYING MANUAL (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when tape is stopped
/// </summary>
public void OSD_TapeStopped()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("STOPPED MANUAL (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when tape is rewound
/// </summary>
public void OSD_TapeRTZ()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("REWOUND (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when a new tape is inserted into the datacorder
/// </summary>
public void OSD_TapeInserted()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("TAPE INSERTED (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when a tape is stopped automatically
/// </summary>
public void OSD_TapeStoppedAuto()
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("STOPPED (Auto Tape Trap Detected)");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when a tape is started automatically
/// </summary>
public void OSD_TapePlayingAuto()
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("PLAYING (Auto Tape Trap Detected)");
sb.Append("Status: ");
if (_machine.UPDDiskDevice.DriveLight)
sb.Append("READING/WRITING DATA");
else
sb.Append("UNKNOWN");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk);
sb.Clear();
}
}
#endregion
#region TapeDevice Message Methods
/// <summary>
/// Tape message that is fired on core init
/// </summary>
public void OSD_TapeInit()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("Tape Media Imported (count: " + _tapeInfo.Count() + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Emulator);
}
/// <summary>
/// Tape message that is fired when tape is playing
/// </summary>
public void OSD_TapeMotorActive()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("MOTOR ON (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when tape is playing
/// </summary>
public void OSD_TapeMotorInactive()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("MOTOR OFF (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when tape is playing
/// </summary>
public void OSD_TapePlaying()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("PLAYING MANUAL (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when tape is stopped
/// </summary>
public void OSD_TapeStopped()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("STOPPED MANUAL (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when tape is rewound
/// </summary>
public void OSD_TapeRTZ()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("REWOUND (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when a new tape is inserted into the datacorder
/// </summary>
public void OSD_TapeInserted()
{
if (_tapeInfo.Count == 0)
return;
StringBuilder sb = new StringBuilder();
sb.Append("TAPE INSERTED (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when a tape is stopped automatically
/// </summary>
public void OSD_TapeStoppedAuto()
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("STOPPED (Auto Tape Trap Detected)");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when a tape is started automatically
/// </summary>
public void OSD_TapePlayingAuto()
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("PLAYING (Auto Tape Trap Detected)");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when a new block starts playing
/// </summary>
public void OSD_TapePlayingBlockInfo(string blockinfo)
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("...Starting Block " + blockinfo);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when a tape block is skipped (because it is empty)
/// </summary>
public void OSD_TapePlayingSkipBlockInfo(string blockinfo)
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("...Skipping Empty Block " + blockinfo);
/// <summary>
/// Tape message that is fired when a new block starts playing
/// </summary>
public void OSD_TapePlayingBlockInfo(string blockinfo)
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("...Starting Block " + blockinfo);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when a tape block is skipped (because it is empty)
/// </summary>
public void OSD_TapePlayingSkipBlockInfo(string blockinfo)
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("...Skipping Empty Block " + blockinfo);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when a tape is started automatically
/// </summary>
public void OSD_TapeEndDetected(string blockinfo)
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("...Skipping Empty Block " + blockinfo);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when user has manually skipped to the next block
/// </summary>
public void OSD_TapeNextBlock(string blockinfo)
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("Manual Skip Next " + blockinfo);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when user has manually skipped to the next block
/// </summary>
public void OSD_TapePrevBlock(string blockinfo)
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("Manual Skip Prev " + blockinfo);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that prints the current status of the tape device
/// </summary>
public void OSD_ShowTapeStatus()
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("Status: ");
if (_machine.TapeDevice.TapeIsPlaying)
sb.Append("PLAYING");
else
sb.Append("STOPPED");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
sb.Clear();
sb.Append("Tape: " + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
sb.Clear();
sb.Append("Block: ");
sb.Append("(" + (_machine.TapeDevice.CurrentDataBlockIndex + 1) +
" of " + _machine.TapeDevice.DataBlocks.Count() + ") " +
_machine.TapeDevice.DataBlocks[_machine.TapeDevice.CurrentDataBlockIndex].BlockDescription);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
sb.Clear();
sb.Append("Block Pos: ");
int pos = _machine.TapeDevice.Position;
int end = _machine.TapeDevice.DataBlocks[_machine.TapeDevice.CurrentDataBlockIndex].DataPeriods.Count;
double p = 0;
if (end != 0)
p = ((double)pos / (double)end) * (double)100;
sb.Append(p.ToString("N0") + "%");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
sb.Clear();
// get position within the tape itself
sb.Append("Tape Pos: ");
var ind = _machine.TapeDevice.CurrentDataBlockIndex;
int cnt = 0;
for (int i = 0; i < ind; i++)
{
cnt += _machine.TapeDevice.DataBlocks[i].DataPeriods.Count;
}
// now we are at our current block
int ourPos = cnt + pos;
cnt += end;
// count periods in the remaining blocks
for (int i = ind + 1; i < _machine.TapeDevice.DataBlocks.Count; i++)
{
cnt += _machine.TapeDevice.DataBlocks[i].DataPeriods.Count;
}
// work out overall position within the tape
p = 0;
p = ((double)ourPos / (double)cnt) * (double)100;
sb.Append(p.ToString("N0") + "%");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
#endregion
/// <summary>
/// Checks whether message category is allowed to be sent
/// </summary>
public bool CheckMessageSettings(MessageCategory category)
{
switch (Settings.OSDMessageVerbosity)
{
case OSDVerbosity.Full:
return true;
case OSDVerbosity.None:
return false;
case OSDVerbosity.Medium:
switch (category)
{
case MessageCategory.Disk:
case MessageCategory.Emulator:
case MessageCategory.Tape:
case MessageCategory.Misc:
return true;
default:
return false;
}
default:
return true;
}
}
/// <summary>
/// Defines the different message categories
/// </summary>
public enum MessageCategory
{
/// <summary>
/// No defined category as such
/// </summary>
Misc,
/// <summary>
/// User generated input messages (at the moment only tape/disk controls)
/// </summary>
Input,
/// <summary>
/// Tape device generated messages
/// </summary>
Tape,
/// <summary>
/// Disk device generated messages
/// </summary>
Disk,
/// <summary>
/// Emulator generated messages
/// </summary>
Emulator
}
}
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when a tape is started automatically
/// </summary>
public void OSD_TapeEndDetected(string blockinfo)
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("...Skipping Empty Block " + blockinfo);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when user has manually skipped to the next block
/// </summary>
public void OSD_TapeNextBlock(string blockinfo)
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("Manual Skip Next " + blockinfo);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that is fired when user has manually skipped to the next block
/// </summary>
public void OSD_TapePrevBlock(string blockinfo)
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("Manual Skip Prev " + blockinfo);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
/// <summary>
/// Tape message that prints the current status of the tape device
/// </summary>
public void OSD_ShowTapeStatus()
{
StringBuilder sb = new StringBuilder();
if (_tapeInfo.Count == 0)
{
sb.Append("No Tape Loaded");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
return;
}
sb.Append("Status: ");
if (_machine.TapeDevice.TapeIsPlaying)
sb.Append("PLAYING");
else
sb.Append("STOPPED");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
sb.Clear();
sb.Append("Tape: " + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
sb.Clear();
sb.Append("Block: ");
sb.Append("(" + (_machine.TapeDevice.CurrentDataBlockIndex + 1) +
" of " + _machine.TapeDevice.DataBlocks.Count() + ") " +
_machine.TapeDevice.DataBlocks[_machine.TapeDevice.CurrentDataBlockIndex].BlockDescription);
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
sb.Clear();
sb.Append("Block Pos: ");
int pos = _machine.TapeDevice.Position;
int end = _machine.TapeDevice.DataBlocks[_machine.TapeDevice.CurrentDataBlockIndex].DataPeriods.Count;
double p = 0;
if (end != 0)
p = ((double)pos / (double)end) * (double)100;
sb.Append(p.ToString("N0") + "%");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
sb.Clear();
// get position within the tape itself
sb.Append("Tape Pos: ");
var ind = _machine.TapeDevice.CurrentDataBlockIndex;
int cnt = 0;
for (int i = 0; i < ind; i++)
{
cnt += _machine.TapeDevice.DataBlocks[i].DataPeriods.Count;
}
// now we are at our current block
int ourPos = cnt + pos;
cnt += end;
// count periods in the remaining blocks
for (int i = ind + 1; i < _machine.TapeDevice.DataBlocks.Count; i++)
{
cnt += _machine.TapeDevice.DataBlocks[i].DataPeriods.Count;
}
// work out overall position within the tape
p = 0;
p = ((double)ourPos / (double)cnt) * (double)100;
sb.Append(p.ToString("N0") + "%");
SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape);
}
#endregion
/// <summary>
/// Checks whether message category is allowed to be sent
/// </summary>
public bool CheckMessageSettings(MessageCategory category)
{
switch (Settings.OSDMessageVerbosity)
{
case OSDVerbosity.Full:
return true;
case OSDVerbosity.None:
return false;
case OSDVerbosity.Medium:
switch (category)
{
case MessageCategory.Disk:
case MessageCategory.Emulator:
case MessageCategory.Tape:
case MessageCategory.Misc:
return true;
default:
return false;
}
default:
return true;
}
}
/// <summary>
/// Defines the different message categories
/// </summary>
public enum MessageCategory
{
/// <summary>
/// No defined category as such
/// </summary>
Misc,
/// <summary>
/// User generated input messages (at the moment only tape/disk controls)
/// </summary>
Input,
/// <summary>
/// Tape device generated messages
/// </summary>
Tape,
/// <summary>
/// Disk device generated messages
/// </summary>
Disk,
/// <summary>
/// Emulator generated messages
/// </summary>
Emulator
}
}
}

View File

@ -4,48 +4,48 @@ using System.Linq.Expressions;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPCHawk: Core Class
/// * Misc Utilities *
/// </summary>
public partial class AmstradCPC
{
/// <summary>
/// Helper method that returns a single INT32 from a BitArray
/// </summary>
public static int GetIntFromBitArray(BitArray bitArray)
{
if (bitArray.Length > 32)
throw new ArgumentException("Argument length shall be at most 32 bits.");
/// <summary>
/// CPCHawk: Core Class
/// * Misc Utilities *
/// </summary>
public partial class AmstradCPC
{
/// <summary>
/// Helper method that returns a single INT32 from a BitArray
/// </summary>
public static int GetIntFromBitArray(BitArray bitArray)
{
if (bitArray.Length > 32)
throw new ArgumentException("Argument length shall be at most 32 bits.");
int[] array = new int[1];
bitArray.CopyTo(array, 0);
return array[0];
}
int[] array = new int[1];
bitArray.CopyTo(array, 0);
return array[0];
}
/// <summary>
/// POKEs a memory bus address
/// </summary>
public void PokeMemory(ushort addr, byte value)
{
_machine.WriteBus(addr, value);
}
/// <summary>
/// POKEs a memory bus address
/// </summary>
public void PokeMemory(ushort addr, byte value)
{
_machine.WriteBus(addr, value);
}
public string GetMachineType()
{
string m = "";
switch (SyncSettings.MachineType)
{
case MachineType.CPC464:
m = "(Amstrad) CPC 464 (64K)";
break;
case MachineType.CPC6128:
m = "(Amstrad) CPC 6464 (128K)";
break;
}
public string GetMachineType()
{
string m = "";
switch (SyncSettings.MachineType)
{
case MachineType.CPC464:
m = "(Amstrad) CPC 464 (64K)";
break;
case MachineType.CPC6128:
m = "(Amstrad) CPC 6464 (128K)";
break;
}
return m;
}
return m;
}
public static string GetMemberName<T, TValue>(Expression<Func<T, TValue>> memberAccess)
{

View File

@ -9,212 +9,212 @@ using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPCHawk: Core Class
/// * Main Initialization *
/// </summary>
[Core(
"CPCHawk",
"Asnivor",
isPorted: false,
isReleased: false)]
public partial class AmstradCPC : IRegionable, IDriveLight
{
public AmstradCPC(CoreComm comm, IEnumerable<byte[]> files, List<GameInfo> game, object settings, object syncSettings)
{
var ser = new BasicServiceProvider(this);
ServiceProvider = ser;
InputCallbacks = new InputCallbackSystem();
MemoryCallbacks = new MemoryCallbackSystem(new[] { "System Bus" });
CoreComm = comm;
_gameInfo = game;
_cpu = new Z80A();
_tracer = new TraceBuffer { Header = _cpu.TraceHeader };
_files = files?.ToList() ?? new List<byte[]>();
/// <summary>
/// CPCHawk: Core Class
/// * Main Initialization *
/// </summary>
[Core(
"CPCHawk",
"Asnivor",
isPorted: false,
isReleased: false)]
public partial class AmstradCPC : IRegionable, IDriveLight
{
public AmstradCPC(CoreComm comm, IEnumerable<byte[]> files, List<GameInfo> game, object settings, object syncSettings)
{
var ser = new BasicServiceProvider(this);
ServiceProvider = ser;
InputCallbacks = new InputCallbackSystem();
MemoryCallbacks = new MemoryCallbackSystem(new[] { "System Bus" });
CoreComm = comm;
_gameInfo = game;
_cpu = new Z80A();
_tracer = new TraceBuffer { Header = _cpu.TraceHeader };
_files = files?.ToList() ?? new List<byte[]>();
if (settings == null)
settings = new AmstradCPCSettings();
if (syncSettings == null)
syncSettings = new AmstradCPCSyncSettings();
if (settings == null)
settings = new AmstradCPCSettings();
if (syncSettings == null)
syncSettings = new AmstradCPCSyncSettings();
PutSyncSettings((AmstradCPCSyncSettings)syncSettings ?? new AmstradCPCSyncSettings());
PutSettings((AmstradCPCSettings)settings ?? new AmstradCPCSettings());
PutSyncSettings((AmstradCPCSyncSettings)syncSettings ?? new AmstradCPCSyncSettings());
PutSettings((AmstradCPCSettings)settings ?? new AmstradCPCSettings());
deterministicEmulation = ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).DeterministicEmulation;
deterministicEmulation = ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).DeterministicEmulation;
switch (SyncSettings.MachineType)
{
case MachineType.CPC464:
ControllerDefinition = AmstradCPCControllerDefinition;
Init(MachineType.CPC464, _files, ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).AutoStartStopTape,
((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).BorderType);
break;
case MachineType.CPC6128:
ControllerDefinition = AmstradCPCControllerDefinition;
Init(MachineType.CPC6128, _files, ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).AutoStartStopTape, ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).BorderType);
break;
default:
throw new InvalidOperationException("Machine not yet emulated");
}
switch (SyncSettings.MachineType)
{
case MachineType.CPC464:
ControllerDefinition = AmstradCPCControllerDefinition;
Init(MachineType.CPC464, _files, ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).AutoStartStopTape,
((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).BorderType);
break;
case MachineType.CPC6128:
ControllerDefinition = AmstradCPCControllerDefinition;
Init(MachineType.CPC6128, _files, ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).AutoStartStopTape, ((AmstradCPCSyncSettings)syncSettings as AmstradCPCSyncSettings).BorderType);
break;
default:
throw new InvalidOperationException("Machine not yet emulated");
}
_cpu.MemoryCallbacks = MemoryCallbacks;
_cpu.MemoryCallbacks = MemoryCallbacks;
HardReset = _machine.HardReset;
SoftReset = _machine.SoftReset;
HardReset = _machine.HardReset;
SoftReset = _machine.SoftReset;
_cpu.FetchMemory = _machine.ReadMemory;
_cpu.ReadMemory = _machine.ReadMemory;
_cpu.WriteMemory = _machine.WriteMemory;
_cpu.ReadHardware = _machine.ReadPort;
_cpu.WriteHardware = _machine.WritePort;
_cpu.FetchDB = _machine.PushBus;
_cpu.IRQACKCallback = _machine.GateArray.IORQA;
//_cpu.OnExecFetch = _machine.CPUMon.OnExecFetch;
_cpu.FetchMemory = _machine.ReadMemory;
_cpu.ReadMemory = _machine.ReadMemory;
_cpu.WriteMemory = _machine.WriteMemory;
_cpu.ReadHardware = _machine.ReadPort;
_cpu.WriteHardware = _machine.WritePort;
_cpu.FetchDB = _machine.PushBus;
_cpu.IRQACKCallback = _machine.GateArray.IORQA;
//_cpu.OnExecFetch = _machine.CPUMon.OnExecFetch;
ser.Register<ITraceable>(_tracer);
ser.Register<IDisassemblable>(_cpu);
ser.Register<IVideoProvider>(_machine.GateArray);
ser.Register<ITraceable>(_tracer);
ser.Register<IDisassemblable>(_cpu);
ser.Register<IVideoProvider>(_machine.GateArray);
// initialize sound mixer and attach the various ISoundProvider devices
SoundMixer = new SoundProviderMixer((int)(32767 / 10), "Tape Audio", (ISoundProvider)_machine.TapeBuzzer);
if (_machine.AYDevice != null)
SoundMixer.AddSource(_machine.AYDevice, "AY-3-3912");
// initialize sound mixer and attach the various ISoundProvider devices
SoundMixer = new SoundProviderMixer((int)(32767 / 10), "Tape Audio", (ISoundProvider)_machine.TapeBuzzer);
if (_machine.AYDevice != null)
SoundMixer.AddSource(_machine.AYDevice, "AY-3-3912");
// set audio device settings
if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AY38912))
{
((AY38912)_machine.AYDevice as AY38912).PanningConfiguration = ((AmstradCPCSettings)settings as AmstradCPCSettings).AYPanConfig;
_machine.AYDevice.Volume = ((AmstradCPCSettings)settings as AmstradCPCSettings).AYVolume;
}
// set audio device settings
if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AY38912))
{
((AY38912)_machine.AYDevice as AY38912).PanningConfiguration = ((AmstradCPCSettings)settings as AmstradCPCSettings).AYPanConfig;
_machine.AYDevice.Volume = ((AmstradCPCSettings)settings as AmstradCPCSettings).AYVolume;
}
if (_machine.TapeBuzzer != null)
{
((Beeper)_machine.TapeBuzzer as Beeper).Volume = ((AmstradCPCSettings)settings as AmstradCPCSettings).TapeVolume;
}
if (_machine.TapeBuzzer != null)
{
((Beeper)_machine.TapeBuzzer as Beeper).Volume = ((AmstradCPCSettings)settings as AmstradCPCSettings).TapeVolume;
}
ser.Register<ISoundProvider>(SoundMixer);
ser.Register<ISoundProvider>(SoundMixer);
HardReset();
SetupMemoryDomains();
}
HardReset();
SetupMemoryDomains();
}
public Action HardReset;
public Action SoftReset;
public Action HardReset;
public Action SoftReset;
private readonly Z80A _cpu;
private readonly TraceBuffer _tracer;
public IController _controller;
public CPCBase _machine;
private readonly Z80A _cpu;
private readonly TraceBuffer _tracer;
public IController _controller;
public CPCBase _machine;
public List<GameInfo> _gameInfo;
public List<GameInfo> _tapeInfo = new List<GameInfo>();
public List<GameInfo> _diskInfo = new List<GameInfo>();
public List<GameInfo> _gameInfo;
public List<GameInfo> _tapeInfo = new List<GameInfo>();
public List<GameInfo> _diskInfo = new List<GameInfo>();
private SoundProviderMixer SoundMixer;
private SoundProviderMixer SoundMixer;
private readonly List<byte[]> _files;
private readonly List<byte[]> _files;
private byte[] GetFirmware(int length, params string[] names)
{
// Amstrad licensed ROMs are free to distribute and shipped with BizHawk
byte[] embeddedRom = new byte[length];
bool embeddedFound = true;
switch (names.FirstOrDefault())
{
// CPC 464 ROMS
case "OS464ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.OS_464_ROM));
break;
case "BASIC1-0ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.CPC_BASIC_1_0_ROM));
break;
private byte[] GetFirmware(int length, params string[] names)
{
// Amstrad licensed ROMs are free to distribute and shipped with BizHawk
byte[] embeddedRom = new byte[length];
bool embeddedFound = true;
switch (names.FirstOrDefault())
{
// CPC 464 ROMS
case "OS464ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.OS_464_ROM));
break;
case "BASIC1-0ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.CPC_BASIC_1_0_ROM));
break;
// CPC 6128 ROMS
case "OS6128ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.CPC_OS_6128_ROM));
break;
case "BASIC1-1ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.CPC_BASIC_1_1_ROM));
break;
case "AMSDOS0-5ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.CPC_AMSDOS_0_5_ROM));
break;
default:
embeddedFound = false;
break;
}
// CPC 6128 ROMS
case "OS6128ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.CPC_OS_6128_ROM));
break;
case "BASIC1-1ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.CPC_BASIC_1_1_ROM));
break;
case "AMSDOS0-5ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.CPC_AMSDOS_0_5_ROM));
break;
default:
embeddedFound = false;
break;
}
if (embeddedFound)
return embeddedRom;
if (embeddedFound)
return embeddedRom;
// Embedded ROM not found, maybe this is a peripheral ROM?
var result = names.Select(n => CoreComm.CoreFileProvider.GetFirmware("AmstradCPC", n, false)).FirstOrDefault(b => b != null && b.Length == length);
if (result == null)
{
throw new MissingFirmwareException($"At least one of these firmwares is required: {string.Join(", ", names)}");
}
// Embedded ROM not found, maybe this is a peripheral ROM?
var result = names.Select(n => CoreComm.CoreFileProvider.GetFirmware("AmstradCPC", n, false)).FirstOrDefault(b => b != null && b.Length == length);
if (result == null)
{
throw new MissingFirmwareException($"At least one of these firmwares is required: {string.Join(", ", names)}");
}
return result;
}
return result;
}
private MachineType _machineType;
private MachineType _machineType;
private void Init(MachineType machineType, List<byte[]> files, bool autoTape, BorderType bType)
{
_machineType = machineType;
private void Init(MachineType machineType, List<byte[]> files, bool autoTape, BorderType bType)
{
_machineType = machineType;
// setup the emulated model based on the MachineType
switch (machineType)
{
case MachineType.CPC464:
_machine = new CPC464(this, _cpu, files, autoTape, bType);
List<RomData> roms64 = new List<RomData>();
roms64.Add(RomData.InitROM(MachineType.CPC464, GetFirmware(0x4000, "OS464ROM"), RomData.ROMChipType.Lower));
roms64.Add(RomData.InitROM(MachineType.CPC464, GetFirmware(0x4000, "BASIC1-0ROM"), RomData.ROMChipType.Upper, 0));
_machine.InitROM(roms64.ToArray());
break;
// setup the emulated model based on the MachineType
switch (machineType)
{
case MachineType.CPC464:
_machine = new CPC464(this, _cpu, files, autoTape, bType);
List<RomData> roms64 = new List<RomData>();
roms64.Add(RomData.InitROM(MachineType.CPC464, GetFirmware(0x4000, "OS464ROM"), RomData.ROMChipType.Lower));
roms64.Add(RomData.InitROM(MachineType.CPC464, GetFirmware(0x4000, "BASIC1-0ROM"), RomData.ROMChipType.Upper, 0));
_machine.InitROM(roms64.ToArray());
break;
case MachineType.CPC6128:
_machine = new CPC6128(this, _cpu, files, autoTape, bType);
List<RomData> roms128 = new List<RomData>();
roms128.Add(RomData.InitROM(MachineType.CPC6128, GetFirmware(0x4000, "OS6128ROM"), RomData.ROMChipType.Lower));
roms128.Add(RomData.InitROM(MachineType.CPC6128, GetFirmware(0x4000, "BASIC1-1ROM"), RomData.ROMChipType.Upper, 0));
roms128.Add(RomData.InitROM(MachineType.CPC6128, GetFirmware(0x4000, "AMSDOS0-5ROM"), RomData.ROMChipType.Upper, 7));
_machine.InitROM(roms128.ToArray());
break;
}
}
case MachineType.CPC6128:
_machine = new CPC6128(this, _cpu, files, autoTape, bType);
List<RomData> roms128 = new List<RomData>();
roms128.Add(RomData.InitROM(MachineType.CPC6128, GetFirmware(0x4000, "OS6128ROM"), RomData.ROMChipType.Lower));
roms128.Add(RomData.InitROM(MachineType.CPC6128, GetFirmware(0x4000, "BASIC1-1ROM"), RomData.ROMChipType.Upper, 0));
roms128.Add(RomData.InitROM(MachineType.CPC6128, GetFirmware(0x4000, "AMSDOS0-5ROM"), RomData.ROMChipType.Upper, 7));
_machine.InitROM(roms128.ToArray());
break;
}
}
#region IRegionable
#region IRegionable
public DisplayType Region => DisplayType.PAL;
public DisplayType Region => DisplayType.PAL;
#endregion
#endregion
#region IDriveLight
#region IDriveLight
public bool DriveLightEnabled
{
get
{
return true;
}
}
public bool DriveLightEnabled
{
get
{
return true;
}
}
public bool DriveLightOn
{
get
{
if (_machine != null &&
(_machine.TapeDevice != null && _machine.TapeDevice.TapeIsPlaying) ||
(_machine.UPDDiskDevice != null && _machine.UPDDiskDevice.DriveLight))
return true;
public bool DriveLightOn
{
get
{
if (_machine != null &&
(_machine.TapeDevice != null && _machine.TapeDevice.TapeIsPlaying) ||
(_machine.UPDDiskDevice != null && _machine.UPDDiskDevice.DriveLight))
return true;
return false;
}
}
return false;
}
}
#endregion
}
#endregion
}
}

View File

@ -2,24 +2,24 @@
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Represents a beeper/buzzer device
/// </summary>
public interface IBeeperDevice
{
/// <summary>
/// Initialisation
/// </summary>
void Init(int sampleRate, int tStatesPerFrame);
/// <summary>
/// Represents a beeper/buzzer device
/// </summary>
public interface IBeeperDevice
{
/// <summary>
/// Initialisation
/// </summary>
void Init(int sampleRate, int tStatesPerFrame);
/// <summary>
/// Processes an incoming pulse value and adds it to the blipbuffer
/// </summary>
void ProcessPulseValue(bool pulse);
/// <summary>
/// Processes an incoming pulse value and adds it to the blipbuffer
/// </summary>
void ProcessPulseValue(bool pulse);
/// <summary>
/// State serialization
/// </summary>
void SyncState(Serializer ser);
}
/// <summary>
/// State serialization
/// </summary>
void SyncState(Serializer ser);
}
}

View File

@ -1,29 +1,29 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Defines an object that can load a floppy disk image
/// </summary>
public interface IFDDHost
{
/// <summary>
/// The currently inserted diskimage
/// </summary>
FloppyDisk Disk { get; set; }
/// <summary>
/// Defines an object that can load a floppy disk image
/// </summary>
public interface IFDDHost
{
/// <summary>
/// The currently inserted diskimage
/// </summary>
FloppyDisk Disk { get; set; }
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
void FDD_LoadDisk(byte[] diskData);
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
void FDD_LoadDisk(byte[] diskData);
/// <summary>
/// Ejects the current disk
/// </summary>
void FDD_EjectDisk();
/// <summary>
/// Ejects the current disk
/// </summary>
void FDD_EjectDisk();
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
bool FDD_IsDiskLoaded { get; }
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
bool FDD_IsDiskLoaded { get; }
}
}

View File

@ -1,34 +1,34 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Represents a spectrum joystick
/// </summary>
public interface IJoystick
{
/// <summary>
/// The type of joystick
/// </summary>
JoystickType JoyType { get; }
/// <summary>
/// Represents a spectrum joystick
/// </summary>
public interface IJoystick
{
/// <summary>
/// The type of joystick
/// </summary>
JoystickType JoyType { get; }
/// <summary>
/// Array of all the possibly button press names
/// </summary>
string[] ButtonCollection { get; set; }
/// <summary>
/// Array of all the possibly button press names
/// </summary>
string[] ButtonCollection { get; set; }
/// <summary>
/// The player number that this controller is currently assigned to
/// </summary>
int PlayerNumber { get; set; }
/// <summary>
/// The player number that this controller is currently assigned to
/// </summary>
int PlayerNumber { get; set; }
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
void SetJoyInput(string key, bool isPressed);
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
void SetJoyInput(string key, bool isPressed);
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
bool GetJoyInput(string key);
}
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
bool GetJoyInput(string key);
}
}

View File

@ -2,52 +2,52 @@
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Represents a spectrum keyboard
/// </summary>
public interface IKeyboard
{
/// <summary>
/// The calling spectrumbase class
/// </summary>
CPCBase _machine { get; }
/// <summary>
/// Represents a spectrum keyboard
/// </summary>
public interface IKeyboard
{
/// <summary>
/// The calling spectrumbase class
/// </summary>
CPCBase _machine { get; }
/// <summary>
/// The keyboard matrix for a particular CPC model
/// </summary>
string[] KeyboardMatrix { get; set; }
/// <summary>
/// The keyboard matrix for a particular CPC model
/// </summary>
string[] KeyboardMatrix { get; set; }
/// <summary>
/// Other keyboard keys that are not in the matrix
/// (usually keys derived from key combos)
/// </summary>
string[] NonMatrixKeys { get; set; }
/// <summary>
/// Other keyboard keys that are not in the matrix
/// (usually keys derived from key combos)
/// </summary>
string[] NonMatrixKeys { get; set; }
/// <summary>
/// Represents the spectrum key state
/// </summary>
bool[] KeyStatus { get; set; }
/// <summary>
/// Represents the spectrum key state
/// </summary>
bool[] KeyStatus { get; set; }
/// <summary>
/// The currently selected line
/// </summary>
int CurrentLine { get; set; }
/// <summary>
/// The currently selected line
/// </summary>
int CurrentLine { get; set; }
/// <summary>
/// Reads the current line status
/// </summary>
byte ReadCurrentLine();
/// <summary>
/// Reads the current line status
/// </summary>
byte ReadCurrentLine();
/// <summary>
/// Sets the CPC key status
/// </summary>
void SetKeyStatus(string key, bool isPressed);
/// <summary>
/// Sets the CPC key status
/// </summary>
void SetKeyStatus(string key, bool isPressed);
/// <summary>
/// Gets the status of a CPC key
/// </summary>
bool GetKeyStatus(string key);
/// <summary>
/// Gets the status of a CPC key
/// </summary>
bool GetKeyStatus(string key);
void SyncState(Serializer ser);
}
void SyncState(Serializer ser);
}
}

View File

@ -3,64 +3,64 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Represents a PSG device (in this case an AY-3-891x)
/// </summary>
public interface IPSG : ISoundProvider
{
/// <summary>
/// Initlization routine
/// </summary>
void Init(int sampleRate, int tStatesPerFrame);
/// <summary>
/// Represents a PSG device (in this case an AY-3-891x)
/// </summary>
public interface IPSG : ISoundProvider
{
/// <summary>
/// Initlization routine
/// </summary>
void Init(int sampleRate, int tStatesPerFrame);
void SetFunction(int data);
void SetFunction(int data);
//void ClockCycle();
//void ClockCycle();
/// <summary>
/// Activates a register
/// </summary>
int SelectedRegister { get; set; }
/// <summary>
/// Writes to the PSG
/// </summary>
void PortWrite(int value);
/// <summary>
/// Activates a register
/// </summary>
int SelectedRegister { get; set; }
/// <summary>
/// Reads from the PSG
/// </summary>
int PortRead();
/// <summary>
/// Writes to the PSG
/// </summary>
void PortWrite(int value);
/// <summary>
/// Resets the PSG
/// </summary>
void Reset();
/// <summary>
/// Reads from the PSG
/// </summary>
int PortRead();
/// <summary>
/// The volume of the AY chip
/// </summary>
int Volume { get; set; }
/// <summary>
/// Called at the start of a frame
/// </summary>
void StartFrame();
/// <summary>
/// Resets the PSG
/// </summary>
void Reset();
/// <summary>
/// called at the end of a frame
/// </summary>
void EndFrame();
/// <summary>
/// The volume of the AY chip
/// </summary>
int Volume { get; set; }
/// <summary>
/// Updates the sound based on number of frame cycles
/// </summary>
void UpdateSound(int frameCycle);
/// <summary>
/// Called at the start of a frame
/// </summary>
void StartFrame();
/// <summary>
/// IStatable serialization
/// </summary>
void SyncState(Serializer ser);
}
/// <summary>
/// called at the end of a frame
/// </summary>
void EndFrame();
/// <summary>
/// Updates the sound based on number of frame cycles
/// </summary>
void UpdateSound(int frameCycle);
/// <summary>
/// IStatable serialization
/// </summary>
void SyncState(Serializer ser);
}
}

View File

@ -1,19 +1,19 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Represents a device that utilizes port IN & OUT
/// </summary>
public interface IPortIODevice
{
/// <summary>
/// Device responds to an IN instruction
/// </summary>
bool ReadPort(ushort port, ref int result);
/// <summary>
/// Represents a device that utilizes port IN & OUT
/// </summary>
public interface IPortIODevice
{
/// <summary>
/// Device responds to an IN instruction
/// </summary>
bool ReadPort(ushort port, ref int result);
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
bool WritePort(ushort port, int result);
}
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
bool WritePort(ushort port, int result);
}
}

View File

@ -1,180 +1,180 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Used for the sector CHRN structure
/// </summary>
public class CHRN
{
/// <summary>
/// Track
/// </summary>
public byte C { get; set; }
/// <summary>
/// Used for the sector CHRN structure
/// </summary>
public class CHRN
{
/// <summary>
/// Track
/// </summary>
public byte C { get; set; }
/// <summary>
/// Side
/// </summary>
public byte H { get; set; }
/// <summary>
/// Side
/// </summary>
public byte H { get; set; }
/// <summary>
/// Sector ID
/// </summary>
public byte R { get; set; }
/// <summary>
/// Sector ID
/// </summary>
public byte R { get; set; }
/// <summary>
/// Sector Size
/// </summary>
public byte N { get; set; }
/// <summary>
/// Sector Size
/// </summary>
public byte N { get; set; }
/// <summary>
/// Status register 1
/// </summary>
private byte _flag1;
public byte Flag1
{
get { return _flag1; }
set { _flag1 = value; }
}
/// <summary>
/// Status register 1
/// </summary>
private byte _flag1;
public byte Flag1
{
get { return _flag1; }
set { _flag1 = value; }
}
/// <summary>
/// Status register 2
/// </summary>
private byte _flag2;
public byte Flag2
{
get { return _flag2; }
set { _flag2 = value; }
}
/// <summary>
/// Status register 2
/// </summary>
private byte _flag2;
public byte Flag2
{
get { return _flag2; }
set { _flag2 = value; }
}
/// <summary>
/// Used to store the last transmitted/received data bytes
/// </summary>
public byte[] DataBytes { get; set; }
/// <summary>
/// Used to store the last transmitted/received data bytes
/// </summary>
public byte[] DataBytes { get; set; }
/// <summary>
/// ID for the read/write data command
/// </summary>
public int DataID { get; set; }
/// <summary>
/// ID for the read/write data command
/// </summary>
public int DataID { get; set; }
#region Helper Methods
#region Helper Methods
/// <summary>
/// Missing Address Mark (Sector_ID or DAM not found)
/// </summary>
public bool ST1MA
{
get { return NECUPD765.GetBit(0, _flag1); }
set
{
if (value) { NECUPD765.SetBit(0, ref _flag1); }
else { NECUPD765.UnSetBit(0, ref _flag1); }
}
}
/// <summary>
/// Missing Address Mark (Sector_ID or DAM not found)
/// </summary>
public bool ST1MA
{
get { return NECUPD765.GetBit(0, _flag1); }
set
{
if (value) { NECUPD765.SetBit(0, ref _flag1); }
else { NECUPD765.UnSetBit(0, ref _flag1); }
}
}
/// <summary>
/// No Data (Sector_ID not found, CRC fail in ID_field)
/// </summary>
public bool ST1ND
{
get { return NECUPD765.GetBit(2, _flag1); }
set
{
if (value) { NECUPD765.SetBit(2, ref _flag1); }
else { NECUPD765.UnSetBit(2, ref _flag1); }
}
}
/// <summary>
/// No Data (Sector_ID not found, CRC fail in ID_field)
/// </summary>
public bool ST1ND
{
get { return NECUPD765.GetBit(2, _flag1); }
set
{
if (value) { NECUPD765.SetBit(2, ref _flag1); }
else { NECUPD765.UnSetBit(2, ref _flag1); }
}
}
/// <summary>
/// Data Error (CRC-fail in ID- or Data-Field)
/// </summary>
public bool ST1DE
{
get { return NECUPD765.GetBit(5, _flag1); }
set
{
if (value) { NECUPD765.SetBit(5, ref _flag1); }
else { NECUPD765.UnSetBit(5, ref _flag1); }
}
}
/// <summary>
/// Data Error (CRC-fail in ID- or Data-Field)
/// </summary>
public bool ST1DE
{
get { return NECUPD765.GetBit(5, _flag1); }
set
{
if (value) { NECUPD765.SetBit(5, ref _flag1); }
else { NECUPD765.UnSetBit(5, ref _flag1); }
}
}
/// <summary>
/// End of Track (set past most read/write commands) (see IC)
/// </summary>
public bool ST1EN
{
get { return NECUPD765.GetBit(7, _flag1); }
set
{
if (value) { NECUPD765.SetBit(7, ref _flag1); }
else { NECUPD765.UnSetBit(7, ref _flag1); }
}
}
/// <summary>
/// End of Track (set past most read/write commands) (see IC)
/// </summary>
public bool ST1EN
{
get { return NECUPD765.GetBit(7, _flag1); }
set
{
if (value) { NECUPD765.SetBit(7, ref _flag1); }
else { NECUPD765.UnSetBit(7, ref _flag1); }
}
}
/// <summary>
/// Missing Address Mark in Data Field (DAM not found)
/// </summary>
public bool ST2MD
{
get { return NECUPD765.GetBit(0, _flag2); }
set
{
if (value) { NECUPD765.SetBit(0, ref _flag2); }
else { NECUPD765.UnSetBit(0, ref _flag2); }
}
}
/// <summary>
/// Missing Address Mark in Data Field (DAM not found)
/// </summary>
public bool ST2MD
{
get { return NECUPD765.GetBit(0, _flag2); }
set
{
if (value) { NECUPD765.SetBit(0, ref _flag2); }
else { NECUPD765.UnSetBit(0, ref _flag2); }
}
}
/// <summary>
/// Bad Cylinder (read/programmed track-ID different and read-ID = FF)
/// </summary>
public bool ST2BC
{
get { return NECUPD765.GetBit(1, _flag2); }
set
{
if (value) { NECUPD765.SetBit(1, ref _flag2); }
else { NECUPD765.UnSetBit(1, ref _flag2); }
}
}
/// <summary>
/// Bad Cylinder (read/programmed track-ID different and read-ID = FF)
/// </summary>
public bool ST2BC
{
get { return NECUPD765.GetBit(1, _flag2); }
set
{
if (value) { NECUPD765.SetBit(1, ref _flag2); }
else { NECUPD765.UnSetBit(1, ref _flag2); }
}
}
/// <summary>
/// Wrong Cylinder (read/programmed track-ID different) (see b1)
/// </summary>
public bool ST2WC
{
get { return NECUPD765.GetBit(4, _flag2); }
set
{
if (value) { NECUPD765.SetBit(4, ref _flag2); }
else { NECUPD765.UnSetBit(4, ref _flag2); }
}
}
/// <summary>
/// Wrong Cylinder (read/programmed track-ID different) (see b1)
/// </summary>
public bool ST2WC
{
get { return NECUPD765.GetBit(4, _flag2); }
set
{
if (value) { NECUPD765.SetBit(4, ref _flag2); }
else { NECUPD765.UnSetBit(4, ref _flag2); }
}
}
/// <summary>
/// Data Error in Data Field (CRC-fail in data-field)
/// </summary>
public bool ST2DD
{
get { return NECUPD765.GetBit(5, _flag2); }
set
{
if (value) { NECUPD765.SetBit(5, ref _flag2); }
else { NECUPD765.UnSetBit(5, ref _flag2); }
}
}
/// <summary>
/// Data Error in Data Field (CRC-fail in data-field)
/// </summary>
public bool ST2DD
{
get { return NECUPD765.GetBit(5, _flag2); }
set
{
if (value) { NECUPD765.SetBit(5, ref _flag2); }
else { NECUPD765.UnSetBit(5, ref _flag2); }
}
}
/// <summary>
/// Control Mark (read/scan command found sector with deleted DAM)
/// </summary>
public bool ST2CM
{
get { return NECUPD765.GetBit(6, _flag2); }
set
{
if (value) { NECUPD765.SetBit(6, ref _flag2); }
else { NECUPD765.UnSetBit(6, ref _flag2); }
}
}
/// <summary>
/// Control Mark (read/scan command found sector with deleted DAM)
/// </summary>
public bool ST2CM
{
get { return NECUPD765.GetBit(6, _flag2); }
set
{
if (value) { NECUPD765.SetBit(6, ref _flag2); }
else { NECUPD765.UnSetBit(6, ref _flag2); }
}
}
#endregion
}
#endregion
}
}

View File

@ -5,331 +5,331 @@ using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Floppy drive related stuff
/// </summary>
#region Attribution
/*
/// <summary>
/// Floppy drive related stuff
/// </summary>
#region Attribution
/*
Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC
and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/
#endregion
public partial class NECUPD765 : IFDDHost
{
#region Drive State
#endregion
public partial class NECUPD765 : IFDDHost
{
#region Drive State
/// <summary>
/// FDD Flag - motor on/off
/// </summary>
public bool FDD_FLAG_MOTOR;
/// <summary>
/// FDD Flag - motor on/off
/// </summary>
public bool FDD_FLAG_MOTOR;
/// <summary>
/// The index of the currently active disk drive
/// </summary>
public int DiskDriveIndex
{
get { return _diskDriveIndex; }
set
{
// when index is changed update the ActiveDrive
_diskDriveIndex = value;
ActiveDrive = DriveStates[_diskDriveIndex];
}
}
private int _diskDriveIndex = 0;
/// <summary>
/// The index of the currently active disk drive
/// </summary>
public int DiskDriveIndex
{
get { return _diskDriveIndex; }
set
{
// when index is changed update the ActiveDrive
_diskDriveIndex = value;
ActiveDrive = DriveStates[_diskDriveIndex];
}
}
private int _diskDriveIndex = 0;
/// <summary>
/// The currently active drive
/// </summary>
private DriveState ActiveDrive;
/// <summary>
/// The currently active drive
/// </summary>
private DriveState ActiveDrive;
/// <summary>
/// Array that holds state information for each possible drive
/// </summary>
private DriveState[] DriveStates = new DriveState[4];
/// <summary>
/// Array that holds state information for each possible drive
/// </summary>
private DriveState[] DriveStates = new DriveState[4];
#endregion
#endregion
#region FDD Methods
#region FDD Methods
/// <summary>
/// Initialization / reset of the floppy drive subsystem
/// </summary>
private void FDD_Init()
{
for (int i = 0; i < 4; i++)
{
DriveState ds = new DriveState(i, this);
DriveStates[i] = ds;
}
}
/// <summary>
/// Initialization / reset of the floppy drive subsystem
/// </summary>
private void FDD_Init()
{
for (int i = 0; i < 4; i++)
{
DriveState ds = new DriveState(i, this);
DriveStates[i] = ds;
}
}
/// <summary>
/// Searches for the requested sector
/// </summary>
private FloppyDisk.Sector GetSector()
{
FloppyDisk.Sector sector = null;
/// <summary>
/// Searches for the requested sector
/// </summary>
private FloppyDisk.Sector GetSector()
{
FloppyDisk.Sector sector = null;
// get the current track
var trk = ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex];
// get the current track
var trk = ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex];
// get the current sector index
int index = ActiveDrive.SectorIndex;
// get the current sector index
int index = ActiveDrive.SectorIndex;
// make sure this index exists
if (index > trk.Sectors.Length)
{
index = 0;
}
// make sure this index exists
if (index > trk.Sectors.Length)
{
index = 0;
}
// index hole count
int iHole = 0;
// index hole count
int iHole = 0;
// loop through the sectors in a track
// the loop ends with either the sector being found
// or the index hole being passed twice
while (iHole <= 2)
{
// does the requested sector match the current sector
if (trk.Sectors[index].SectorIDInfo.C == ActiveCommandParams.Cylinder &&
trk.Sectors[index].SectorIDInfo.H == ActiveCommandParams.Head &&
trk.Sectors[index].SectorIDInfo.R == ActiveCommandParams.Sector &&
trk.Sectors[index].SectorIDInfo.N == ActiveCommandParams.SectorSize)
{
// sector has been found
sector = trk.Sectors[index];
// loop through the sectors in a track
// the loop ends with either the sector being found
// or the index hole being passed twice
while (iHole <= 2)
{
// does the requested sector match the current sector
if (trk.Sectors[index].SectorIDInfo.C == ActiveCommandParams.Cylinder &&
trk.Sectors[index].SectorIDInfo.H == ActiveCommandParams.Head &&
trk.Sectors[index].SectorIDInfo.R == ActiveCommandParams.Sector &&
trk.Sectors[index].SectorIDInfo.N == ActiveCommandParams.SectorSize)
{
// sector has been found
sector = trk.Sectors[index];
UnSetBit(SR2_BC, ref Status2);
UnSetBit(SR2_WC, ref Status2);
break;
}
UnSetBit(SR2_BC, ref Status2);
UnSetBit(SR2_WC, ref Status2);
break;
}
// check for bad cylinder
if (trk.Sectors[index].SectorIDInfo.C == 255)
{
SetBit(SR2_BC, ref Status2);
}
// check for no cylinder
else if (trk.Sectors[index].SectorIDInfo.C != ActiveCommandParams.Cylinder)
{
SetBit(SR2_WC, ref Status2);
}
// check for bad cylinder
if (trk.Sectors[index].SectorIDInfo.C == 255)
{
SetBit(SR2_BC, ref Status2);
}
// check for no cylinder
else if (trk.Sectors[index].SectorIDInfo.C != ActiveCommandParams.Cylinder)
{
SetBit(SR2_WC, ref Status2);
}
// incrememnt sector index
index++;
// incrememnt sector index
index++;
// have we reached the index hole?
if (trk.Sectors.Length <= index)
{
// wrap around
index = 0;
iHole++;
}
}
// have we reached the index hole?
if (trk.Sectors.Length <= index)
{
// wrap around
index = 0;
iHole++;
}
}
// search loop has completed and the sector may or may not have been found
// search loop has completed and the sector may or may not have been found
// bad cylinder detected?
if (Status2.Bit(SR2_BC))
{
// remove WC
UnSetBit(SR2_WC, ref Status2);
}
// bad cylinder detected?
if (Status2.Bit(SR2_BC))
{
// remove WC
UnSetBit(SR2_WC, ref Status2);
}
// update sectorindex on drive
ActiveDrive.SectorIndex = index;
// update sectorindex on drive
ActiveDrive.SectorIndex = index;
return sector;
}
return sector;
}
#endregion
#endregion
#region IFDDHost
#region IFDDHost
// IFDDHost methods that fall through to the currently active drive
// IFDDHost methods that fall through to the currently active drive
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
public void FDD_LoadDisk(byte[] diskData)
{
// we are only going to load into the first drive
DriveStates[0].FDD_LoadDisk(diskData);
}
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
public void FDD_LoadDisk(byte[] diskData)
{
// we are only going to load into the first drive
DriveStates[0].FDD_LoadDisk(diskData);
}
/// <summary>
/// Ejects the current disk
/// </summary>
public void FDD_EjectDisk()
{
DriveStates[0].FDD_EjectDisk();
}
/// <summary>
/// Ejects the current disk
/// </summary>
public void FDD_EjectDisk()
{
DriveStates[0].FDD_EjectDisk();
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
public bool FDD_IsDiskLoaded
{
get { return DriveStates[DiskDriveIndex].FDD_IsDiskLoaded; }
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
public bool FDD_IsDiskLoaded
{
get { return DriveStates[DiskDriveIndex].FDD_IsDiskLoaded; }
}
/// <summary>
/// Returns the disk object from drive 0
/// </summary>
public FloppyDisk DiskPointer
{
get { return DriveStates[0].Disk; }
}
public FloppyDisk Disk { get; set; }
/// <summary>
/// Returns the disk object from drive 0
/// </summary>
public FloppyDisk DiskPointer
{
get { return DriveStates[0].Disk; }
}
#endregion
public FloppyDisk Disk { get; set; }
#region Drive Status Class
#endregion
/// <summary>
/// Holds specfic state information about a drive
/// </summary>
private class DriveState : IFDDHost
{
#region State
#region Drive Status Class
/// <summary>
/// The drive ID from an FDC perspective
/// </summary>
public int ID;
/// <summary>
/// Holds specfic state information about a drive
/// </summary>
private class DriveState : IFDDHost
{
#region State
/// <summary>
/// Signs whether this drive ready
/// TRUE if both drive exists and has a disk inserted
/// </summary>
public bool FLAG_READY
{
get
{
if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR)
return false;
else
return true;
}
}
/// <summary>
/// The drive ID from an FDC perspective
/// </summary>
public int ID;
/// <summary>
/// Disk is write protected (TRUE BY DEFAULT)
/// </summary>
public bool FLAG_WRITEPROTECT = false;
/// <summary>
/// Signs whether this drive ready
/// TRUE if both drive exists and has a disk inserted
/// </summary>
public bool FLAG_READY
{
get
{
if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR)
return false;
else
return true;
}
}
/// <summary>
/// Storage for seek steps
/// One step for each indexpulse (track index) until seeked track
/// </summary>
public int SeekCounter;
/// <summary>
/// Disk is write protected (TRUE BY DEFAULT)
/// </summary>
public bool FLAG_WRITEPROTECT = false;
/// <summary>
/// Seek status
/// </summary>
public int SeekStatus;
/// <summary>
/// Storage for seek steps
/// One step for each indexpulse (track index) until seeked track
/// </summary>
public int SeekCounter;
/// <summary>
/// Age counter
/// </summary>
public int SeekAge;
/// <summary>
/// Seek status
/// </summary>
public int SeekStatus;
/// <summary>
/// The current side
/// </summary>
public byte CurrentSide;
/// <summary>
/// Age counter
/// </summary>
public int SeekAge;
/// <summary>
/// The current track index in the DiskTracks array
/// </summary>
public byte TrackIndex;
/// <summary>
/// The current side
/// </summary>
public byte CurrentSide;
/// <summary>
/// The track ID of the current cylinder
/// </summary>
public byte CurrentTrackID
{
get
{
// default invalid track
int id = 0xff;
/// <summary>
/// The current track index in the DiskTracks array
/// </summary>
public byte TrackIndex;
if (Disk == null)
return (byte)id;
/// <summary>
/// The track ID of the current cylinder
/// </summary>
public byte CurrentTrackID
{
get
{
// default invalid track
int id = 0xff;
if (Disk.DiskTracks.Count() == 0)
return (byte)id;
if (Disk == null)
return (byte)id;
if (TrackIndex >= Disk.GetTrackCount())
TrackIndex = 0;
else if (TrackIndex < 0)
TrackIndex = 0;
if (Disk.DiskTracks.Count() == 0)
return (byte)id;
var track = Disk.DiskTracks[TrackIndex];
if (TrackIndex >= Disk.GetTrackCount())
TrackIndex = 0;
else if (TrackIndex < 0)
TrackIndex = 0;
id = track.TrackNumber;
var track = Disk.DiskTracks[TrackIndex];
return (byte)id;
}
set
{
for (int i = 0; i < Disk.GetTrackCount(); i++)
{
if (Disk.DiskTracks[i].TrackNumber == value)
{
TrackIndex = (byte)i;
break;
}
}
}
}
id = track.TrackNumber;
return (byte)id;
}
set
{
for (int i = 0; i < Disk.GetTrackCount(); i++)
{
if (Disk.DiskTracks[i].TrackNumber == value)
{
TrackIndex = (byte)i;
break;
}
}
}
}
/// <summary>
/// The new track that the drive is seeking to
/// (used in seek operations)
/// </summary>
public int SeekingTrack;
/// <summary>
/// The new track that the drive is seeking to
/// (used in seek operations)
/// </summary>
public int SeekingTrack;
/// <summary>
/// The current sector index in the Sectors array
/// </summary>
public int SectorIndex;
/// <summary>
/// The current sector index in the Sectors array
/// </summary>
public int SectorIndex;
/// <summary>
/// The currently loaded floppy disk
/// </summary>
public FloppyDisk Disk { get; set; }
/// <summary>
/// The currently loaded floppy disk
/// </summary>
public FloppyDisk Disk { get; set; }
/// <summary>
/// The parent controller
/// </summary>
private NECUPD765 FDC;
/// <summary>
/// The parent controller
/// </summary>
private NECUPD765 FDC;
#endregion
#endregion
#region Lookups
#region Lookups
/// <summary>
/// TRUE if we are on track 0
/// </summary>
public bool FLAG_TRACK0
{
get
{
if (TrackIndex == 0) { return true; }
else { return false; }
}
}
/// <summary>
/// TRUE if we are on track 0
/// </summary>
public bool FLAG_TRACK0
{
get
{
if (TrackIndex == 0) { return true; }
else { return false; }
}
}
#endregion
#endregion
#region Public Methods
/*
#region Public Methods
/*
/// <summary>
/// Moves the head across the disk cylinders
/// </summary>
@ -369,7 +369,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
*/
/*
/*
/// <summary>
/// Finds a supplied sector
@ -524,7 +524,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
*/
/*
/*
/// <summary>
/// The drive performs a seek operation if necessary
@ -751,131 +751,131 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
SetBit(SR0_EC, ref IntStatus);
}
*/
/*
// UnitSelect
SetUnitSelect(ID, ref IntStatus);
/*
// UnitSelect
SetUnitSelect(ID, ref IntStatus);
// move to none state
//CurrentState = DriveMainState.None;
// move to none state
//CurrentState = DriveMainState.None;
//SeekState = SeekSubState.SeekCompleted;
//SeekState = SeekSubState.SeekCompleted;
// set the seek interrupt flag for this drive
// this will be cleared at the next successful senseint
FLAG_SEEK_INTERRUPT = true;
// set the seek interrupt flag for this drive
// this will be cleared at the next successful senseint
FLAG_SEEK_INTERRUPT = true;
//CurrentState = DriveMainState.None;
//CurrentState = DriveMainState.None;
}
*/
}
*/
#endregion
#endregion
#region Construction
#region Construction
public DriveState(int driveID, NECUPD765 fdc)
{
ID = driveID;
FDC = fdc;
}
public DriveState(int driveID, NECUPD765 fdc)
{
ID = driveID;
FDC = fdc;
}
#endregion
#endregion
#region IFDDHost
#region IFDDHost
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
public void FDD_LoadDisk(byte[] diskData)
{
// try dsk first
FloppyDisk fdd = null;
bool found = false;
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
public void FDD_LoadDisk(byte[] diskData)
{
// try dsk first
FloppyDisk fdd = null;
bool found = false;
foreach (DiskType type in Enum.GetValues(typeof(DiskType)))
{
switch (type)
{
case DiskType.CPCExtended:
fdd = new CPCExtendedFloppyDisk();
found = fdd.ParseDisk(diskData);
break;
case DiskType.CPC:
fdd = new CPCFloppyDisk();
found = fdd.ParseDisk(diskData);
break;
}
foreach (DiskType type in Enum.GetValues(typeof(DiskType)))
{
switch (type)
{
case DiskType.CPCExtended:
fdd = new CPCExtendedFloppyDisk();
found = fdd.ParseDisk(diskData);
break;
case DiskType.CPC:
fdd = new CPCFloppyDisk();
found = fdd.ParseDisk(diskData);
break;
}
if (found)
{
Disk = fdd;
break;
}
}
if (found)
{
Disk = fdd;
break;
}
}
if (!found)
{
throw new Exception(this.GetType().ToString() +
"\n\nDisk image file could not be parsed. Potentially an unknown format.");
}
}
if (!found)
{
throw new Exception(this.GetType().ToString() +
"\n\nDisk image file could not be parsed. Potentially an unknown format.");
}
}
/// <summary>
/// Ejects the current disk
/// </summary>
public void FDD_EjectDisk()
{
Disk = null;
//FLAG_READY = false;
}
/// <summary>
/// Ejects the current disk
/// </summary>
public void FDD_EjectDisk()
{
Disk = null;
//FLAG_READY = false;
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
public bool FDD_IsDiskLoaded
{
get
{
if (Disk != null)
return true;
else
return false;
}
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
public bool FDD_IsDiskLoaded
{
get
{
if (Disk != null)
return true;
else
return false;
}
}
#endregion
#endregion
#region StateSerialization
#region StateSerialization
public void SyncState(Serializer ser)
{
ser.Sync(nameof(ID), ref ID);
ser.Sync(nameof(FLAG_WRITEPROTECT), ref FLAG_WRITEPROTECT);
//ser.Sync(nameof(FLAG_DISKCHANGED), ref FLAG_DISKCHANGED);
//ser.Sync(nameof(FLAG_RECALIBRATING), ref FLAG_RECALIBRATING);
//ser.Sync(nameof(FLAG_SEEK_INTERRUPT), ref FLAG_SEEK_INTERRUPT);
//ser.Sync(nameof(IntStatus), ref IntStatus);
//ser.Sync(nameof(ST0), ref ST0);
//ser.Sync(nameof(RecalibrationCounter), ref RecalibrationCounter);
ser.Sync(nameof(SeekCounter), ref SeekCounter);
ser.Sync(nameof(SeekStatus), ref SeekStatus);
ser.Sync(nameof(SeekAge), ref SeekAge);
ser.Sync(nameof(CurrentSide), ref CurrentSide);
//ser.Sync(nameof(CurrentTrack), ref CurrentTrack);
ser.Sync(nameof(TrackIndex), ref TrackIndex);
ser.Sync(nameof(SeekingTrack), ref SeekingTrack);
//ser.Sync(nameof(CurrentSector), ref CurrentSector);
ser.Sync(nameof(SectorIndex), ref SectorIndex);
//ser.Sync(nameof(RAngles), ref RAngles);
//ser.Sync(nameof(DataPointer), ref DataPointer);
//ser.SyncEnum(nameof(CurrentState), ref CurrentState);
//ser.SyncEnum(nameof(SeekState), ref SeekState);
//ser.SyncEnum(nameof(SeekIntState), ref SeekIntState);
}
public void SyncState(Serializer ser)
{
ser.Sync(nameof(ID), ref ID);
ser.Sync(nameof(FLAG_WRITEPROTECT), ref FLAG_WRITEPROTECT);
//ser.Sync(nameof(FLAG_DISKCHANGED), ref FLAG_DISKCHANGED);
//ser.Sync(nameof(FLAG_RECALIBRATING), ref FLAG_RECALIBRATING);
//ser.Sync(nameof(FLAG_SEEK_INTERRUPT), ref FLAG_SEEK_INTERRUPT);
//ser.Sync(nameof(IntStatus), ref IntStatus);
//ser.Sync(nameof(ST0), ref ST0);
//ser.Sync(nameof(RecalibrationCounter), ref RecalibrationCounter);
ser.Sync(nameof(SeekCounter), ref SeekCounter);
ser.Sync(nameof(SeekStatus), ref SeekStatus);
ser.Sync(nameof(SeekAge), ref SeekAge);
ser.Sync(nameof(CurrentSide), ref CurrentSide);
//ser.Sync(nameof(CurrentTrack), ref CurrentTrack);
ser.Sync(nameof(TrackIndex), ref TrackIndex);
ser.Sync(nameof(SeekingTrack), ref SeekingTrack);
//ser.Sync(nameof(CurrentSector), ref CurrentSector);
ser.Sync(nameof(SectorIndex), ref SectorIndex);
//ser.Sync(nameof(RAngles), ref RAngles);
//ser.Sync(nameof(DataPointer), ref DataPointer);
//ser.SyncEnum(nameof(CurrentState), ref CurrentState);
//ser.SyncEnum(nameof(SeekState), ref SeekState);
//ser.SyncEnum(nameof(SeekIntState), ref SeekIntState);
}
#endregion
}
#endregion
}
#endregion
}
#endregion
}
}

View File

@ -5,32 +5,32 @@ using System.Text;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// IPortIODevice
/// </summary>
#region Attribution
/*
/// <summary>
/// IPortIODevice
/// </summary>
#region Attribution
/*
Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC
and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/
#endregion
public partial class NECUPD765 : IPortIODevice
{
#region Dev Logging
#endregion
public partial class NECUPD765 : IPortIODevice
{
#region Dev Logging
public string outputfile = @"D:\Dropbox\Dropbox\_Programming\TASVideos\BizHawk\output\zxhawkio-" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv";
public string outputString = "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN\r\n";
public bool writeDebug = false;
public string outputfile = @"D:\Dropbox\Dropbox\_Programming\TASVideos\BizHawk\output\zxhawkio-" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv";
public string outputString = "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN\r\n";
public bool writeDebug = false;
public List<string> dLog = new List<string>
{
"STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN"
};
public List<string> dLog = new List<string>
{
"STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN"
};
/*
/*
* Status read
* Data write
* Data read
@ -40,154 +40,154 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
* MK flag
* SK flag
* */
private string[] workingArr = new string[3];
private string[] workingArr = new string[3];
private void BuildCSVLine()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 3; i++)
{
sb.Append(workingArr[i]);
sb.Append(",");
workingArr[i] = "";
}
private void BuildCSVLine()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 3; i++)
{
sb.Append(workingArr[i]);
sb.Append(",");
workingArr[i] = "";
}
sb.Append(ActiveCommand.CommandCode).Append(",");
sb.Append(ActiveCommand.CommandCode).Append(",");
sb.Append(CMD_FLAG_MT).Append(",");
sb.Append(CMD_FLAG_MF).Append(",");
sb.Append(CMD_FLAG_SK).Append(",");
sb.Append(CMD_FLAG_MT).Append(",");
sb.Append(CMD_FLAG_MF).Append(",");
sb.Append(CMD_FLAG_SK).Append(",");
sb.Append(CommCounter).Append(",");
sb.Append(ResCounter).Append(",");
sb.Append(ExecCounter).Append(",");
sb.Append(ExecLength);
sb.Append(CommCounter).Append(",");
sb.Append(ResCounter).Append(",");
sb.Append(ExecCounter).Append(",");
sb.Append(ExecLength);
//sb.Append("\r\n");
//sb.Append("\r\n");
//outputString += sb.ToString();
dLog.Add(sb.ToString());
}
//outputString += sb.ToString();
dLog.Add(sb.ToString());
}
#endregion
#endregion
public void ReadStatus(ref int data)
{
// read main status register
// this can happen at any time
data = ReadMainStatus();
if (writeDebug)
{
//outputString += data + ",,," + ActiveCommand.CommandCode + "\r\n";
workingArr[0] = data.ToString();
BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString);
}
}
public void ReadStatus(ref int data)
{
// read main status register
// this can happen at any time
data = ReadMainStatus();
if (writeDebug)
{
//outputString += data + ",,," + ActiveCommand.CommandCode + "\r\n";
workingArr[0] = data.ToString();
BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString);
}
}
public void ReadData(ref int data)
{
// Z80 is trying to read from the data register
data = ReadDataRegister();
if (writeDebug)
{
workingArr[2] = data.ToString();
//outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n";
BuildCSVLine();
}
}
public void ReadData(ref int data)
{
// Z80 is trying to read from the data register
data = ReadDataRegister();
if (writeDebug)
{
workingArr[2] = data.ToString();
//outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n";
BuildCSVLine();
}
}
public void WriteData(int data)
{
// Z80 is attempting to write to the data register
WriteDataRegister((byte)data);
if (writeDebug)
{
//outputString += "," + data + ",," + ActiveCommand.CommandCode + "\r\n";
workingArr[1] = data.ToString();
BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString);
}
}
public void WriteData(int data)
{
// Z80 is attempting to write to the data register
WriteDataRegister((byte)data);
if (writeDebug)
{
//outputString += "," + data + ",," + ActiveCommand.CommandCode + "\r\n";
workingArr[1] = data.ToString();
BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString);
}
}
public void Motor(int data)
{
// set disk motor on/off
if (data > 0)
FDD_FLAG_MOTOR = true;
else
FDD_FLAG_MOTOR = false;
}
public void Motor(int data)
{
// set disk motor on/off
if (data > 0)
FDD_FLAG_MOTOR = true;
else
FDD_FLAG_MOTOR = false;
}
/// <summary>
/// Device responds to an IN instruction
/// </summary>
public bool ReadPort(ushort port, ref int data)
{
BitArray bits = new BitArray(new byte[] { (byte)data });
/// <summary>
/// Device responds to an IN instruction
/// </summary>
public bool ReadPort(ushort port, ref int data)
{
BitArray bits = new BitArray(new byte[] { (byte)data });
if (port == 0x3ffd)
{
// Z80 is trying to read from the data register
data = ReadDataRegister();
if (writeDebug)
{
workingArr[2] = data.ToString();
//outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n";
BuildCSVLine();
}
return true;
}
if (port == 0x3ffd)
{
// Z80 is trying to read from the data register
data = ReadDataRegister();
if (writeDebug)
{
workingArr[2] = data.ToString();
//outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n";
BuildCSVLine();
}
if (port == 0x2ffd)
{
// read main status register
// this can happen at any time
data = ReadMainStatus();
if (writeDebug)
{
//outputString += data + ",,," + ActiveCommand.CommandCode + "\r\n";
workingArr[0] = data.ToString();
BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString);
}
return true;
}
return true;
}
return false;
}
if (port == 0x2ffd)
{
// read main status register
// this can happen at any time
data = ReadMainStatus();
if (writeDebug)
{
//outputString += data + ",,," + ActiveCommand.CommandCode + "\r\n";
workingArr[0] = data.ToString();
BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString);
}
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
public bool WritePort(ushort port, int data)
{
BitArray bits = new BitArray(new byte[] { (byte)data });
return true;
}
if (port == 0x3ffd)
{
// Z80 is attempting to write to the data register
WriteDataRegister((byte)data);
if (writeDebug)
{
//outputString += "," + data + ",," + ActiveCommand.CommandCode + "\r\n";
workingArr[1] = data.ToString();
BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString);
}
return true;
}
return false;
}
if (port == 0x1ffd)
{
// set disk motor on/off
FDD_FLAG_MOTOR = bits[3];
return true;
}
return false;
}
}
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
public bool WritePort(ushort port, int data)
{
BitArray bits = new BitArray(new byte[] { (byte)data });
if (port == 0x3ffd)
{
// Z80 is attempting to write to the data register
WriteDataRegister((byte)data);
if (writeDebug)
{
//outputString += "," + data + ",," + ActiveCommand.CommandCode + "\r\n";
workingArr[1] = data.ToString();
BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString);
}
return true;
}
if (port == 0x1ffd)
{
// set disk motor on/off
FDD_FLAG_MOTOR = bits[3];
return true;
}
return false;
}
}
}

View File

@ -1,120 +1,120 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Timimng
/// </summary>
#region Attribution
/*
/// <summary>
/// Timimng
/// </summary>
#region Attribution
/*
Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC
and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/
#endregion
public partial class NECUPD765
{
/// <summary>
/// The current Z80 cycle
/// </summary>
private long CurrentCPUCycle
{
get
{
if (_machine == null)
return 0;
else
return _machine.CPU.TotalExecutedCycles;
}
}
#endregion
public partial class NECUPD765
{
/// <summary>
/// The current Z80 cycle
/// </summary>
private long CurrentCPUCycle
{
get
{
if (_machine == null)
return 0;
else
return _machine.CPU.TotalExecutedCycles;
}
}
/// <summary>
/// The last CPU cycle when the FDC accepted an IO read/write
/// </summary>
private long LastCPUCycle;
/// <summary>
/// The last CPU cycle when the FDC accepted an IO read/write
/// </summary>
private long LastCPUCycle;
/// <summary>
/// The current delay figure (in Z80 t-states)
/// This implementation only introduces delay upon main status register reads
/// All timing calculations should be done during the other read/write operations
/// </summary>
private long StatusDelay;
/// <summary>
/// The current delay figure (in Z80 t-states)
/// This implementation only introduces delay upon main status register reads
/// All timing calculations should be done during the other read/write operations
/// </summary>
private long StatusDelay;
/// <summary>
/// Defines the numbers of Z80 cycles per MS
/// </summary>
private long CPUCyclesPerMs;
/// <summary>
/// Defines the numbers of Z80 cycles per MS
/// </summary>
private long CPUCyclesPerMs;
/// <summary>
/// The floppy drive emulated clock speed
/// </summary>
public const double DriveClock = 31250;
/// <summary>
/// The floppy drive emulated clock speed
/// </summary>
public const double DriveClock = 31250;
/// <summary>
/// The number of floppy drive cycles per MS
/// </summary>
public long DriveCyclesPerMs;
/// <summary>
/// The number of floppy drive cycles per MS
/// </summary>
public long DriveCyclesPerMs;
/// <summary>
/// The number of T-States in one floppy drive clock tick
/// </summary>
public long StatesPerDriveTick;
/// <summary>
/// The number of T-States in one floppy drive clock tick
/// </summary>
public long StatesPerDriveTick;
/// <summary>
/// Responsible for measuring when the floppy drive is ready to run a cycle
/// </summary>
private long TickCounter;
/// <summary>
/// Responsible for measuring when the floppy drive is ready to run a cycle
/// </summary>
private long TickCounter;
/// <summary>
/// Internal drive cycle counter
/// </summary>
private int DriveCycleCounter = 1;
/// <summary>
/// Internal drive cycle counter
/// </summary>
private int DriveCycleCounter = 1;
/// <summary>
/// Initializes the timing routines
/// </summary>
private void TimingInit()
{
// z80 timing
double frameSize = _machine.GateArray.FrameLength;
double rRate = _machine.GateArray.Z80ClockSpeed / frameSize;
long tPerSecond = (long)(frameSize * rRate);
CPUCyclesPerMs = tPerSecond / 1000;
/// <summary>
/// Initializes the timing routines
/// </summary>
private void TimingInit()
{
// z80 timing
double frameSize = _machine.GateArray.FrameLength;
double rRate = _machine.GateArray.Z80ClockSpeed / frameSize;
long tPerSecond = (long)(frameSize * rRate);
CPUCyclesPerMs = tPerSecond / 1000;
// drive timing
double dRate = DriveClock / frameSize;
long dPerSecond = (long)(frameSize * dRate);
DriveCyclesPerMs = dPerSecond / 1000;
// drive timing
double dRate = DriveClock / frameSize;
long dPerSecond = (long)(frameSize * dRate);
DriveCyclesPerMs = dPerSecond / 1000;
long TStatesPerDriveCycle = (long)((double)_machine.GateArray.Z80ClockSpeed / DriveClock);
StatesPerDriveTick = TStatesPerDriveCycle;
long TStatesPerDriveCycle = (long)((double)_machine.GateArray.Z80ClockSpeed / DriveClock);
StatesPerDriveTick = TStatesPerDriveCycle;
}
}
/// <summary>
/// Called by reads to the main status register
/// Returns true if there is no delay
/// Returns false if read is to be deferred
/// </summary>
private bool CheckTiming()
{
// get delta
long delta = CurrentCPUCycle - LastCPUCycle;
/// <summary>
/// Called by reads to the main status register
/// Returns true if there is no delay
/// Returns false if read is to be deferred
/// </summary>
private bool CheckTiming()
{
// get delta
long delta = CurrentCPUCycle - LastCPUCycle;
if (StatusDelay >= delta)
{
// there is still delay remaining
StatusDelay -= delta;
LastCPUCycle = CurrentCPUCycle;
return false;
}
else
{
// no delay remaining
StatusDelay = 0;
LastCPUCycle = CurrentCPUCycle;
return true;
}
}
}
if (StatusDelay >= delta)
{
// there is still delay remaining
StatusDelay -= delta;
LastCPUCycle = CurrentCPUCycle;
return false;
}
else
{
// no delay remaining
StatusDelay = 0;
LastCPUCycle = CurrentCPUCycle;
return true;
}
}
}
}

View File

@ -3,243 +3,243 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// The NEC floppy disk controller (and floppy drive) found in the +3
/// </summary>
#region Attribution
/*
/// <summary>
/// The NEC floppy disk controller (and floppy drive) found in the +3
/// </summary>
#region Attribution
/*
Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC
and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/
#endregion
public partial class NECUPD765
{
#region Devices
#endregion
public partial class NECUPD765
{
#region Devices
/// <summary>
/// The emulated spectrum machine
/// </summary>
private CPCBase _machine;
/// <summary>
/// The emulated spectrum machine
/// </summary>
private CPCBase _machine;
#endregion
#endregion
#region Construction & Initialization
#region Construction & Initialization
/// <summary>
/// Main constructor
/// </summary>
public NECUPD765()
{
InitCommandList();
}
/// <summary>
/// Main constructor
/// </summary>
public NECUPD765()
{
InitCommandList();
}
/// <summary>
/// Initialization routine
/// </summary>
public void Init(CPCBase machine)
{
_machine = machine;
FDD_Init();
TimingInit();
Reset();
}
/// <summary>
/// Resets the FDC
/// </summary>
public void Reset()
{
// setup main status
StatusMain = 0;
/// <summary>
/// Initialization routine
/// </summary>
public void Init(CPCBase machine)
{
_machine = machine;
FDD_Init();
TimingInit();
Reset();
}
Status0 = 0;
Status1 = 0;
Status2 = 0;
Status3 = 0;
/// <summary>
/// Resets the FDC
/// </summary>
public void Reset()
{
// setup main status
StatusMain = 0;
SetBit(MSR_RQM, ref StatusMain);
Status0 = 0;
Status1 = 0;
Status2 = 0;
Status3 = 0;
SetPhase_Idle();
SetBit(MSR_RQM, ref StatusMain);
//FDC_FLAG_RQM = true;
//ActiveDirection = CommandDirection.IN;
SRT = 6;
HUT = 16;
HLT = 2;
HLT_Counter = 0;
HUT_Counter = 0;
IndexPulseCounter = 0;
CMD_FLAG_MF = false;
SetPhase_Idle();
foreach (var d in DriveStates)
{
//d.SeekingTrack = d.CurrentTrack;
////d.SeekCounter = 0;
//d.FLAG_SEEK_INTERRUPT = false;
//d.IntStatus = 0;
//d.SeekState = SeekSubState.Idle;
//d.SeekIntState = SeekIntStatus.Normal;
//FDC_FLAG_RQM = true;
//ActiveDirection = CommandDirection.IN;
SRT = 6;
HUT = 16;
HLT = 2;
HLT_Counter = 0;
HUT_Counter = 0;
IndexPulseCounter = 0;
CMD_FLAG_MF = false;
}
}
foreach (var d in DriveStates)
{
//d.SeekingTrack = d.CurrentTrack;
////d.SeekCounter = 0;
//d.FLAG_SEEK_INTERRUPT = false;
//d.IntStatus = 0;
//d.SeekState = SeekSubState.Idle;
//d.SeekIntState = SeekIntStatus.Normal;
/// <summary>
/// Setup the command structure
/// Each command represents one of the internal UPD765 commands
/// </summary>
private void InitCommandList()
{
CommandList = new List<Command>
{
}
}
/// <summary>
/// Setup the command structure
/// Each command represents one of the internal UPD765 commands
/// </summary>
private void InitCommandList()
{
CommandList = new List<Command>
{
// read data
new Command { CommandDelegate = UPD_ReadData, CommandCode = 0x06, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
// read id
new Command { CommandDelegate = UPD_ReadID, CommandCode = 0x0a, MF = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 7 },
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 7 },
// specify
new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03,
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
// read diagnostic
new Command { CommandDelegate = UPD_ReadDiagnostic, CommandCode = 0x02, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
// scan equal
new Command { CommandDelegate = UPD_ScanEqual, CommandCode = 0x11, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// scan high or equal
new Command { CommandDelegate = UPD_ScanHighOrEqual, CommandCode = 0x1d, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// scan low or equal
new Command { CommandDelegate = UPD_ScanLowOrEqual, CommandCode = 0x19, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// read deleted data
new Command { CommandDelegate = UPD_ReadDeletedData, CommandCode = 0x0c, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
// write data
new Command { CommandDelegate = UPD_WriteData, CommandCode = 0x05, MT = true, MF = true, IsWrite = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// write id
new Command { CommandDelegate = UPD_WriteID, CommandCode = 0x0d, MF = true, IsWrite = true,
Direction = CommandDirection.IN, ParameterByteCount = 5, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 5, ResultByteCount = 7 },
// write deleted data
new Command { CommandDelegate = UPD_WriteDeletedData, CommandCode = 0x09, MT = true, MF = true, IsWrite = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// seek
new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f,
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
// recalibrate (seek track00)
new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 },
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 },
// sense interrupt status
new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 },
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 },
// sense drive status
new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 },
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 },
// version
new Command { CommandDelegate = UPD_Version, CommandCode = 0x10,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
// invalid
new Command { CommandDelegate = UPD_Invalid, CommandCode = 0x00,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
};
}
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
};
}
#endregion
#endregion
#region State Serialization
#region State Serialization
public void SyncState(Serializer ser)
{
ser.BeginSection("NEC-UPD765");
public void SyncState(Serializer ser)
{
ser.BeginSection("NEC-UPD765");
#region FDD
ser.Sync(nameof(FDD_FLAG_MOTOR), ref FDD_FLAG_MOTOR);
#region FDD
for (int i = 0; i < 4; i++)
{
ser.BeginSection("HITDrive_" + i);
DriveStates[i].SyncState(ser);
ser.EndSection();
}
ser.Sync(nameof(FDD_FLAG_MOTOR), ref FDD_FLAG_MOTOR);
ser.Sync(nameof(DiskDriveIndex), ref _diskDriveIndex);
// set active drive
DiskDriveIndex = _diskDriveIndex;
for (int i = 0; i < 4; i++)
{
ser.BeginSection("HITDrive_" + i);
DriveStates[i].SyncState(ser);
ser.EndSection();
}
#endregion
ser.Sync(nameof(DiskDriveIndex), ref _diskDriveIndex);
// set active drive
DiskDriveIndex = _diskDriveIndex;
#region Registers
#endregion
ser.Sync("_RegMain", ref StatusMain);
ser.Sync("_Reg0", ref Status0);
ser.Sync("_Reg1", ref Status1);
ser.Sync("_Reg2", ref Status2);
ser.Sync("_Reg3", ref Status3);
#region Registers
#endregion
ser.Sync("_RegMain", ref StatusMain);
ser.Sync("_Reg0", ref Status0);
ser.Sync("_Reg1", ref Status1);
ser.Sync("_Reg2", ref Status2);
ser.Sync("_Reg3", ref Status3);
#region Controller state
#endregion
ser.Sync(nameof(DriveLight), ref DriveLight);
ser.SyncEnum(nameof(ActivePhase), ref ActivePhase);
//ser.SyncEnum(nameof(ActiveDirection), ref ActiveDirection);
ser.SyncEnum(nameof(ActiveInterrupt), ref ActiveInterrupt);
ser.Sync(nameof(CommBuffer), ref CommBuffer, false);
ser.Sync(nameof(CommCounter), ref CommCounter);
ser.Sync(nameof(ResBuffer), ref ResBuffer, false);
ser.Sync(nameof(ExecBuffer), ref ExecBuffer, false);
ser.Sync(nameof(ExecCounter), ref ExecCounter);
ser.Sync(nameof(ExecLength), ref ExecLength);
ser.Sync(nameof(InterruptResultBuffer), ref InterruptResultBuffer, false);
ser.Sync(nameof(ResCounter), ref ResCounter);
ser.Sync(nameof(ResLength), ref ResLength);
ser.Sync(nameof(LastSectorDataWriteByte), ref LastSectorDataWriteByte);
ser.Sync(nameof(LastSectorDataReadByte), ref LastSectorDataReadByte);
ser.Sync(nameof(LastByteReceived), ref LastByteReceived);
ser.Sync(nameof(_cmdIndex), ref _cmdIndex);
// resync the ActiveCommand
CMDIndex = _cmdIndex;
#region Controller state
ActiveCommandParams.SyncState(ser);
ser.Sync(nameof(IndexPulseCounter), ref IndexPulseCounter);
//ser.SyncEnum(nameof(_activeStatus), ref _activeStatus);
//ser.SyncEnum(nameof(_statusRaised), ref _statusRaised);
ser.Sync(nameof(DriveLight), ref DriveLight);
ser.SyncEnum(nameof(ActivePhase), ref ActivePhase);
//ser.SyncEnum(nameof(ActiveDirection), ref ActiveDirection);
ser.SyncEnum(nameof(ActiveInterrupt), ref ActiveInterrupt);
ser.Sync(nameof(CommBuffer), ref CommBuffer, false);
ser.Sync(nameof(CommCounter), ref CommCounter);
ser.Sync(nameof(ResBuffer), ref ResBuffer, false);
ser.Sync(nameof(ExecBuffer), ref ExecBuffer, false);
ser.Sync(nameof(ExecCounter), ref ExecCounter);
ser.Sync(nameof(ExecLength), ref ExecLength);
ser.Sync(nameof(InterruptResultBuffer), ref InterruptResultBuffer, false);
ser.Sync(nameof(ResCounter), ref ResCounter);
ser.Sync(nameof(ResLength), ref ResLength);
ser.Sync(nameof(LastSectorDataWriteByte), ref LastSectorDataWriteByte);
ser.Sync(nameof(LastSectorDataReadByte), ref LastSectorDataReadByte);
ser.Sync(nameof(LastByteReceived), ref LastByteReceived);
ser.Sync(nameof(CMD_FLAG_MT), ref CMD_FLAG_MT);
ser.Sync(nameof(CMD_FLAG_MF), ref CMD_FLAG_MF);
ser.Sync(nameof(CMD_FLAG_SK), ref CMD_FLAG_SK);
ser.Sync(nameof(SRT), ref SRT);
ser.Sync(nameof(HUT), ref HUT);
ser.Sync(nameof(HLT), ref HLT);
ser.Sync(nameof(ND), ref ND);
ser.Sync(nameof(SRT_Counter), ref SRT_Counter);
ser.Sync(nameof(HUT_Counter), ref HUT_Counter);
ser.Sync(nameof(HLT_Counter), ref HLT_Counter);
ser.Sync(nameof(_cmdIndex), ref _cmdIndex);
// resync the ActiveCommand
CMDIndex = _cmdIndex;
ser.Sync(nameof(SectorDelayCounter), ref SectorDelayCounter);
ser.Sync(nameof(SectorID), ref SectorID);
ActiveCommandParams.SyncState(ser);
#endregion
ser.Sync(nameof(IndexPulseCounter), ref IndexPulseCounter);
//ser.SyncEnum(nameof(_activeStatus), ref _activeStatus);
//ser.SyncEnum(nameof(_statusRaised), ref _statusRaised);
#region Timing
ser.Sync(nameof(CMD_FLAG_MT), ref CMD_FLAG_MT);
ser.Sync(nameof(CMD_FLAG_MF), ref CMD_FLAG_MF);
ser.Sync(nameof(CMD_FLAG_SK), ref CMD_FLAG_SK);
ser.Sync(nameof(SRT), ref SRT);
ser.Sync(nameof(HUT), ref HUT);
ser.Sync(nameof(HLT), ref HLT);
ser.Sync(nameof(ND), ref ND);
ser.Sync(nameof(SRT_Counter), ref SRT_Counter);
ser.Sync(nameof(HUT_Counter), ref HUT_Counter);
ser.Sync(nameof(HLT_Counter), ref HLT_Counter);
ser.Sync(nameof(LastCPUCycle), ref LastCPUCycle);
ser.Sync(nameof(StatusDelay), ref StatusDelay);
ser.Sync(nameof(TickCounter), ref TickCounter);
ser.Sync(nameof(DriveCycleCounter), ref DriveCycleCounter);
ser.Sync(nameof(SectorDelayCounter), ref SectorDelayCounter);
ser.Sync(nameof(SectorID), ref SectorID);
#endregion
#endregion
ser.EndSection();
}
#region Timing
#endregion
}
ser.Sync(nameof(LastCPUCycle), ref LastCPUCycle);
ser.Sync(nameof(StatusDelay), ref StatusDelay);
ser.Sync(nameof(TickCounter), ref TickCounter);
ser.Sync(nameof(DriveCycleCounter), ref DriveCycleCounter);
#endregion
ser.EndSection();
}
#endregion
}
}

View File

@ -2,95 +2,95 @@
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Static helper methods
/// </summary>
#region Attribution
/*
/// <summary>
/// Static helper methods
/// </summary>
#region Attribution
/*
Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC
and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/
#endregion
public partial class NECUPD765
{
/// <summary>
/// Returns the specified bit value from supplied byte
/// </summary>
public static bool GetBit(int bitNumber, byte dataByte)
{
if (bitNumber < 0 || bitNumber > 7)
return false;
#endregion
public partial class NECUPD765
{
/// <summary>
/// Returns the specified bit value from supplied byte
/// </summary>
public static bool GetBit(int bitNumber, byte dataByte)
{
if (bitNumber < 0 || bitNumber > 7)
return false;
BitArray bi = new BitArray(new byte[] { dataByte });
BitArray bi = new BitArray(new byte[] { dataByte });
return bi[bitNumber];
}
return bi[bitNumber];
}
/// <summary>
/// Sets the specified bit of the supplied byte to 1
/// </summary>
public static void SetBit(int bitNumber, ref byte dataByte)
{
if (bitNumber < 0 || bitNumber > 7)
return;
/// <summary>
/// Sets the specified bit of the supplied byte to 1
/// </summary>
public static void SetBit(int bitNumber, ref byte dataByte)
{
if (bitNumber < 0 || bitNumber > 7)
return;
int db = (int)dataByte;
int db = (int)dataByte;
db |= 1 << bitNumber;
db |= 1 << bitNumber;
dataByte = (byte)db;
}
dataByte = (byte)db;
}
/// <summary>
/// Sets the specified bit of the supplied byte to 0
/// </summary>
public static void UnSetBit(int bitNumber, ref byte dataByte)
{
if (bitNumber < 0 || bitNumber > 7)
return;
/// <summary>
/// Sets the specified bit of the supplied byte to 0
/// </summary>
public static void UnSetBit(int bitNumber, ref byte dataByte)
{
if (bitNumber < 0 || bitNumber > 7)
return;
int db = (int)dataByte;
int db = (int)dataByte;
db &= ~(1 << bitNumber);
db &= ~(1 << bitNumber);
dataByte = (byte)db;
}
dataByte = (byte)db;
}
/// <summary>
/// Returns a drive number (0-3) based on the first two bits of the supplied byte
/// </summary>
public static int GetUnitSelect(byte dataByte)
{
int driveNumber = dataByte & 0x03;
return driveNumber;
}
/// <summary>
/// Returns a drive number (0-3) based on the first two bits of the supplied byte
/// </summary>
public static int GetUnitSelect(byte dataByte)
{
int driveNumber = dataByte & 0x03;
return driveNumber;
}
/// <summary>
/// Sets the first two bits of a byte based on the supplied drive number (0-3)
/// </summary>
public static void SetUnitSelect(int driveNumber, ref byte dataByte)
{
switch (driveNumber)
{
case 0:
UnSetBit(SR0_US0, ref dataByte);
UnSetBit(SR0_US1, ref dataByte);
break;
case 1:
SetBit(SR0_US0, ref dataByte);
UnSetBit(SR0_US1, ref dataByte);
break;
case 2:
SetBit(SR0_US1, ref dataByte);
UnSetBit(SR0_US0, ref dataByte);
break;
case 3:
SetBit(SR0_US0, ref dataByte);
SetBit(SR0_US1, ref dataByte);
break;
}
}
}
/// <summary>
/// Sets the first two bits of a byte based on the supplied drive number (0-3)
/// </summary>
public static void SetUnitSelect(int driveNumber, ref byte dataByte)
{
switch (driveNumber)
{
case 0:
UnSetBit(SR0_US0, ref dataByte);
UnSetBit(SR0_US1, ref dataByte);
break;
case 1:
SetBit(SR0_US0, ref dataByte);
UnSetBit(SR0_US1, ref dataByte);
break;
case 2:
SetBit(SR0_US1, ref dataByte);
UnSetBit(SR0_US0, ref dataByte);
break;
case 3:
SetBit(SR0_US0, ref dataByte);
SetBit(SR0_US1, ref dataByte);
break;
}
}
}
}

View File

@ -283,7 +283,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// The CRTC latches the Display Start H & L address at different times
/// (depending on the chip type)
/// </summary>
private int StartAddressLatch;
private int StartAddressLatch;
#endregion
@ -541,7 +541,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
case 1: return ReadStatus_Type1(ref data);
case 3:
case 4: return ReadStatus_Type3_4(ref data);
default: return false;
default: return false;
}
}
@ -561,7 +561,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
// Bits 5 and 4 determine the skew
res = (val & 0x30) >> 4;
if (res > 2)
return -1;
return -1;
break;
// UMR6845R
@ -600,7 +600,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
if (res > 2)
return -1;
break;
// UMR6845R
case 1:
return 0;
@ -1141,7 +1141,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
#region Clock Cycles
/* persistent switch signals */
/* persistent switch signals */
bool s_VS;
bool s_HDISP;
bool s_VDISP;
@ -1172,7 +1172,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// </summary>
private void ClockCycle_Generic()
{
}
/// <summary>
@ -1788,7 +1788,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
else
{
_DISPTMG = false;
}
}
/* Cursor Control */
if (s_HDISP && s_VDISP)

View File

@ -6,61 +6,61 @@ using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// The 48k keyboard device
/// </summary>
public class StandardKeyboard : IKeyboard
{
public CPCBase _machine { get; set; }
/// <summary>
/// The 48k keyboard device
/// </summary>
public class StandardKeyboard : IKeyboard
{
public CPCBase _machine { get; set; }
private int _currentLine;
public int CurrentLine
{
get { return _currentLine; }
set
{
// bits 0-3 contain the line
var line = value & 0x0f;
private int _currentLine;
public int CurrentLine
{
get { return _currentLine; }
set
{
// bits 0-3 contain the line
var line = value & 0x0f;
if (line > 0)
{
if (line > 0)
{
}
}
_currentLine = line;
}
}
_currentLine = line;
}
}
private bool[] _keyStatus;
public bool[] KeyStatus
{
get { return _keyStatus; }
set { _keyStatus = value; }
}
private bool[] _keyStatus;
public bool[] KeyStatus
{
get { return _keyStatus; }
set { _keyStatus = value; }
}
private string[] _keyboardMatrix;
public string[] KeyboardMatrix
{
get { return _keyboardMatrix; }
set { _keyboardMatrix = value; }
}
private string[] _keyboardMatrix;
public string[] KeyboardMatrix
{
get { return _keyboardMatrix; }
set { _keyboardMatrix = value; }
}
private string[] _nonMatrixKeys;
public string[] NonMatrixKeys
{
get { return _nonMatrixKeys; }
set { _nonMatrixKeys = value; }
}
private string[] _nonMatrixKeys;
public string[] NonMatrixKeys
{
get { return _nonMatrixKeys; }
set { _nonMatrixKeys = value; }
}
public StandardKeyboard(CPCBase machine)
{
_machine = machine;
//_machine.AYDevice.PortA_IN_CallBack = INCallback;
//_machine.AYDevice.PortA_OUT_CallBack = OUTCallback;
public StandardKeyboard(CPCBase machine)
{
_machine = machine;
//_machine.AYDevice.PortA_IN_CallBack = INCallback;
//_machine.AYDevice.PortA_OUT_CallBack = OUTCallback;
// scancode rows, ascending (Bit0 - Bit7)
KeyboardMatrix = new string[]
{
// scancode rows, ascending (Bit0 - Bit7)
KeyboardMatrix = new string[]
{
// 0x40
"Key CURUP", "Key CURRIGHT", "Key CURDOWN", "Key NUM9", "Key NUM6", "Key NUM3", "Key ENTER", "Key NUMPERIOD",
// 0x41
@ -82,72 +82,72 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
// 0x49
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Fire1", "P1 Fire2", "P1 Fire3", "Key DEL",
};
};
// keystatus array to match the matrix
KeyStatus = new bool[8 * 10];
// nonmatrix keys (anything that hasnt already been taken)
var nonMatrix = new List<string>();
foreach (var key in _machine.CPC.AmstradCPCControllerDefinition.BoolButtons)
{
if (!KeyboardMatrix.Any(s => s == key))
nonMatrix.Add(key);
}
NonMatrixKeys = nonMatrix.ToArray();
}
// keystatus array to match the matrix
KeyStatus = new bool[8 * 10];
/// <summary>
/// Reads the currently selected line
/// </summary>
public byte ReadCurrentLine()
{
var lin = _currentLine; // - 0x40;
var pos = lin * 8;
var l = KeyStatus.Skip(pos).Take(8).ToArray();
BitArray bi = new BitArray(l);
byte[] bytes = new byte[1];
bi.CopyTo(bytes, 0);
byte inv = (byte)(~bytes[0]);
return inv;
}
// nonmatrix keys (anything that hasnt already been taken)
var nonMatrix = new List<string>();
/// <summary>
/// Returns the index of the key within the matrix
/// </summary>
public int GetKeyIndexFromMatrix(string key)
{
int index = Array.IndexOf(KeyboardMatrix, key);
return index;
}
foreach (var key in _machine.CPC.AmstradCPCControllerDefinition.BoolButtons)
{
if (!KeyboardMatrix.Any(s => s == key))
nonMatrix.Add(key);
}
/// <summary>
/// Sets key status
/// </summary>
public void SetKeyStatus(string key, bool isPressed)
{
int index = GetKeyIndexFromMatrix(key);
KeyStatus[index] = isPressed;
}
NonMatrixKeys = nonMatrix.ToArray();
}
/// <summary>
/// Gets a key's status
/// </summary>
public bool GetKeyStatus(string key)
{
int index = GetKeyIndexFromMatrix(key);
return KeyStatus[index];
}
/// <summary>
/// Reads the currently selected line
/// </summary>
public byte ReadCurrentLine()
{
var lin = _currentLine; // - 0x40;
var pos = lin * 8;
var l = KeyStatus.Skip(pos).Take(8).ToArray();
BitArray bi = new BitArray(l);
byte[] bytes = new byte[1];
bi.CopyTo(bytes, 0);
byte inv = (byte)(~bytes[0]);
return inv;
}
/// <summary>
/// Returns the index of the key within the matrix
/// </summary>
public int GetKeyIndexFromMatrix(string key)
{
int index = Array.IndexOf(KeyboardMatrix, key);
return index;
}
/// <summary>
/// Sets key status
/// </summary>
public void SetKeyStatus(string key, bool isPressed)
{
int index = GetKeyIndexFromMatrix(key);
KeyStatus[index] = isPressed;
}
/// <summary>
/// Gets a key's status
/// </summary>
public bool GetKeyStatus(string key)
{
int index = GetKeyIndexFromMatrix(key);
return KeyStatus[index];
}
public void SyncState(Serializer ser)
{
ser.BeginSection("Keyboard");
ser.Sync("currentLine", ref _currentLine);
ser.Sync("keyStatus", ref _keyStatus, false);
ser.EndSection();
}
}
public void SyncState(Serializer ser)
{
ser.BeginSection("Keyboard");
ser.Sync("currentLine", ref _currentLine);
ser.Sync("keyStatus", ref _keyStatus, false);
ser.EndSection();
}
}
}

View File

@ -5,456 +5,456 @@ using System.Collections;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Emulates the PPI (8255) chip
/// http://www.cpcwiki.eu/imgs/d/df/PPI_M5L8255AP-5.pdf
/// http://www.cpcwiki.eu/index.php/8255
/// </summary>
public class PPI_8255 : IPortIODevice
{
#region Devices
/// <summary>
/// Emulates the PPI (8255) chip
/// http://www.cpcwiki.eu/imgs/d/df/PPI_M5L8255AP-5.pdf
/// http://www.cpcwiki.eu/index.php/8255
/// </summary>
public class PPI_8255 : IPortIODevice
{
#region Devices
private CPCBase _machine;
private CRCT_6845 CRTC => _machine.CRCT;
private AmstradGateArray GateArray => _machine.GateArray;
private IPSG PSG => _machine.AYDevice;
private DatacorderDevice Tape => _machine.TapeDevice;
private IKeyboard Keyboard => _machine.KeyboardDevice;
private CPCBase _machine;
private CRCT_6845 CRTC => _machine.CRCT;
private AmstradGateArray GateArray => _machine.GateArray;
private IPSG PSG => _machine.AYDevice;
private DatacorderDevice Tape => _machine.TapeDevice;
private IKeyboard Keyboard => _machine.KeyboardDevice;
#endregion
#endregion
#region Construction
#region Construction
public PPI_8255(CPCBase machine)
{
_machine = machine;
Reset();
}
public PPI_8255(CPCBase machine)
{
_machine = machine;
Reset();
}
#endregion
#endregion
#region Implementation
#region Implementation
/// <summary>
/// BDIR Line connected to PSG
/// </summary>
public bool BDIR
{
get { return Regs[PORT_C].Bit(7); }
}
/// <summary>
/// BDIR Line connected to PSG
/// </summary>
public bool BDIR
{
get { return Regs[PORT_C].Bit(7); }
}
/// <summary>
/// BC1 Line connected to PSG
/// </summary>
public bool BC1
{
get { return Regs[PORT_C].Bit(6); }
}
/// <summary>
/// BC1 Line connected to PSG
/// </summary>
public bool BC1
{
get { return Regs[PORT_C].Bit(6); }
}
/* Port Constants */
private const int PORT_A = 0;
private const int PORT_B = 1;
private const int PORT_C = 2;
private const int PORT_CONTROL = 3;
/* Port Constants */
private const int PORT_A = 0;
private const int PORT_B = 1;
private const int PORT_C = 2;
private const int PORT_CONTROL = 3;
/// <summary>
/// The i8255 internal data registers
/// </summary>
private byte[] Regs = new byte[4];
/// <summary>
/// The i8255 internal data registers
/// </summary>
private byte[] Regs = new byte[4];
/// <summary>
/// Returns the currently latched port direction for Port A
/// </summary>
private PortDirection DirPortA
{
get { return Regs[PORT_CONTROL].Bit(4) ? PortDirection.Input : PortDirection.Output; }
}
/// <summary>
/// Returns the currently latched port direction for Port A
/// </summary>
private PortDirection DirPortA
{
get { return Regs[PORT_CONTROL].Bit(4) ? PortDirection.Input : PortDirection.Output; }
}
/// <summary>
/// Returns the currently latched port direction for Port B
/// </summary>
private PortDirection DirPortB
{
get { return Regs[PORT_CONTROL].Bit(1) ? PortDirection.Input : PortDirection.Output; }
}
/// <summary>
/// Returns the currently latched port direction for Port B
/// </summary>
private PortDirection DirPortB
{
get { return Regs[PORT_CONTROL].Bit(1) ? PortDirection.Input : PortDirection.Output; }
}
/// <summary>
/// Returns the currently latched port direction for Port C (lower half)
/// </summary>
private PortDirection DirPortCL
{
get { return Regs[PORT_CONTROL].Bit(0) ? PortDirection.Input : PortDirection.Output; }
}
/// <summary>
/// Returns the currently latched port direction for Port C (lower half)
/// </summary>
private PortDirection DirPortCL
{
get { return Regs[PORT_CONTROL].Bit(0) ? PortDirection.Input : PortDirection.Output; }
}
/// <summary>
/// Returns the currently latched port direction for Port C (upper half)
/// </summary>
private PortDirection DirPortCU
{
get { return Regs[PORT_CONTROL].Bit(3) ? PortDirection.Input : PortDirection.Output; }
}
/// <summary>
/// Returns the currently latched port direction for Port C (upper half)
/// </summary>
private PortDirection DirPortCU
{
get { return Regs[PORT_CONTROL].Bit(3) ? PortDirection.Input : PortDirection.Output; }
}
#region OUT Methods
#region OUT Methods
/// <summary>
/// Writes to Port A
/// </summary>
private void OUTPortA(int data)
{
// latch the data
Regs[PORT_A] = (byte)data;
/// <summary>
/// Writes to Port A
/// </summary>
private void OUTPortA(int data)
{
// latch the data
Regs[PORT_A] = (byte)data;
if (DirPortA == PortDirection.Output)
{
// PSG write
PSG.PortWrite(data);
}
}
if (DirPortA == PortDirection.Output)
{
// PSG write
PSG.PortWrite(data);
}
}
/// <summary>
/// Writes to Port B
/// </summary>
private void OUTPortB(int data)
{
// PortB is read only
// just latch the data
Regs[PORT_B] = (byte)data;
}
/// <summary>
/// Writes to Port B
/// </summary>
private void OUTPortB(int data)
{
// PortB is read only
// just latch the data
Regs[PORT_B] = (byte)data;
}
/// <summary>
/// Writes to Port C
/// </summary>
private void OUTPortC(int data)
{
// latch the data
Regs[PORT_C] = (byte)data;
/// <summary>
/// Writes to Port C
/// </summary>
private void OUTPortC(int data)
{
// latch the data
Regs[PORT_C] = (byte)data;
if (DirPortCL == PortDirection.Output)
{
// lower Port C bits OUT
// keyboard line update
Keyboard.CurrentLine = Regs[PORT_C] & 0x0f;
}
if (DirPortCL == PortDirection.Output)
{
// lower Port C bits OUT
// keyboard line update
Keyboard.CurrentLine = Regs[PORT_C] & 0x0f;
}
if (DirPortCU == PortDirection.Output)
{
// upper Port C bits OUT
// write to PSG using latched data
PSG.SetFunction(data);
PSG.PortWrite(Regs[PORT_A]);
if (DirPortCU == PortDirection.Output)
{
// upper Port C bits OUT
// write to PSG using latched data
PSG.SetFunction(data);
PSG.PortWrite(Regs[PORT_A]);
// cassete write data
//not implemeted
// cassete write data
//not implemeted
// cas motor control
Tape.TapeMotor = Regs[PORT_C].Bit(4);
}
}
// cas motor control
Tape.TapeMotor = Regs[PORT_C].Bit(4);
}
}
/// <summary>
/// Writes to the control register
/// </summary>
private void OUTControl(int data)
{
if (data.Bit(7))
{
// update configuration
Regs[PORT_CONTROL] = (byte)data;
/// <summary>
/// Writes to the control register
/// </summary>
private void OUTControl(int data)
{
if (data.Bit(7))
{
// update configuration
Regs[PORT_CONTROL] = (byte)data;
// Writing to PIO Control Register (with Bit7 set), automatically resets PIO Ports A,B,C to 00h each
Regs[PORT_A] = 0;
Regs[PORT_B] = 0;
Regs[PORT_C] = 0;
}
else
{
// register is used to set/reset a single bit in Port C
bool isSet = data.Bit(0);
// Writing to PIO Control Register (with Bit7 set), automatically resets PIO Ports A,B,C to 00h each
Regs[PORT_A] = 0;
Regs[PORT_B] = 0;
Regs[PORT_C] = 0;
}
else
{
// register is used to set/reset a single bit in Port C
bool isSet = data.Bit(0);
// get the bit in PortC that we wish to change
var bit = (data >> 1) & 7;
// get the bit in PortC that we wish to change
var bit = (data >> 1) & 7;
// modify this bit
if (isSet)
{
Regs[PORT_C] = (byte)(Regs[PORT_C] | (bit * bit));
}
else
{
Regs[PORT_C] = (byte)(Regs[PORT_C] & ~(bit * bit));
}
// modify this bit
if (isSet)
{
Regs[PORT_C] = (byte)(Regs[PORT_C] | (bit * bit));
}
else
{
Regs[PORT_C] = (byte)(Regs[PORT_C] & ~(bit * bit));
}
// any other ouput business
if (DirPortCL == PortDirection.Output)
{
// update keyboard line
Keyboard.CurrentLine = Regs[PORT_C] & 0x0f;
}
// any other ouput business
if (DirPortCL == PortDirection.Output)
{
// update keyboard line
Keyboard.CurrentLine = Regs[PORT_C] & 0x0f;
}
if (DirPortCU == PortDirection.Output)
{
// write to PSG using latched data
PSG.SetFunction(data);
PSG.PortWrite(Regs[PORT_A]);
if (DirPortCU == PortDirection.Output)
{
// write to PSG using latched data
PSG.SetFunction(data);
PSG.PortWrite(Regs[PORT_A]);
// cassete write data
//not implemeted
// cassete write data
//not implemeted
// cas motor control
Tape.TapeMotor = Regs[PORT_C].Bit(4);
}
}
}
// cas motor control
Tape.TapeMotor = Regs[PORT_C].Bit(4);
}
}
}
#endregion
#endregion
#region IN Methods
#region IN Methods
/// <summary>
/// Reads from Port A
/// </summary>
private int INPortA()
{
if (DirPortA == PortDirection.Input)
{
// read from PSG
return PSG.PortRead();
}
else
{
// Port A is set to output
// return latched value
return Regs[PORT_A];
}
}
/// <summary>
/// Reads from Port A
/// </summary>
private int INPortA()
{
if (DirPortA == PortDirection.Input)
{
// read from PSG
return PSG.PortRead();
}
else
{
// Port A is set to output
// return latched value
return Regs[PORT_A];
}
}
/// <summary>
/// Reads from Port B
/// </summary>
private int INPortB()
{
if (DirPortB == PortDirection.Input)
{
// build the PortB output
// start with every bit reset
BitArray rBits = new BitArray(8);
/// <summary>
/// Reads from Port B
/// </summary>
private int INPortB()
{
if (DirPortB == PortDirection.Input)
{
// build the PortB output
// start with every bit reset
BitArray rBits = new BitArray(8);
// Bit0 - Vertical Sync ("1"=VSYNC active, "0"=VSYNC inactive)
if (CRTC.VSYNC)
rBits[0] = true;
// Bit0 - Vertical Sync ("1"=VSYNC active, "0"=VSYNC inactive)
if (CRTC.VSYNC)
rBits[0] = true;
// Bits1-3 - Distributor ID. Usually set to 4=Awa, 5=Schneider, or 7=Amstrad
// force AMstrad
rBits[1] = true;
rBits[2] = true;
rBits[3] = true;
// Bits1-3 - Distributor ID. Usually set to 4=Awa, 5=Schneider, or 7=Amstrad
// force AMstrad
rBits[1] = true;
rBits[2] = true;
rBits[3] = true;
// Bit4 - Screen Refresh Rate ("1"=50Hz, "0"=60Hz)
rBits[4] = true;
// Bit4 - Screen Refresh Rate ("1"=50Hz, "0"=60Hz)
rBits[4] = true;
// Bit5 - Expansion Port /EXP pin
rBits[5] = false;
// Bit5 - Expansion Port /EXP pin
rBits[5] = false;
// Bit6 - Parallel/Printer port ready signal, "1" = not ready, "0" = Ready
rBits[6] = true;
// Bit6 - Parallel/Printer port ready signal, "1" = not ready, "0" = Ready
rBits[6] = true;
// Bit7 - Cassette data input
rBits[7] = Tape.GetEarBit(_machine.CPU.TotalExecutedCycles);
// Bit7 - Cassette data input
rBits[7] = Tape.GetEarBit(_machine.CPU.TotalExecutedCycles);
// return the byte
byte[] bytes = new byte[1];
rBits.CopyTo(bytes, 0);
return bytes[0];
}
else
{
// return the latched value
return Regs[PORT_B];
}
}
// return the byte
byte[] bytes = new byte[1];
rBits.CopyTo(bytes, 0);
return bytes[0];
}
else
{
// return the latched value
return Regs[PORT_B];
}
}
/// <summary>
/// Reads from Port C
/// </summary>
private int INPortC()
{
// get the PortC value
int val = Regs[PORT_C];
/// <summary>
/// Reads from Port C
/// </summary>
private int INPortC()
{
// get the PortC value
int val = Regs[PORT_C];
if (DirPortCU == PortDirection.Input)
{
// upper port C bits
// remove upper half
val &= 0x0f;
if (DirPortCU == PortDirection.Input)
{
// upper port C bits
// remove upper half
val &= 0x0f;
// isolate control bits
var v = Regs[PORT_C] & 0xc0;
// isolate control bits
var v = Regs[PORT_C] & 0xc0;
if (v == 0xc0)
{
// set reg is present. change to write reg
v = 0x80;
}
if (v == 0xc0)
{
// set reg is present. change to write reg
v = 0x80;
}
// cas wr is always set
val |= v | 0x20;
// cas wr is always set
val |= v | 0x20;
if (Tape.TapeMotor)
{
val |= 0x10;
}
}
if (Tape.TapeMotor)
{
val |= 0x10;
}
}
if (DirPortCL == PortDirection.Input)
{
// lower port C bits
val |= 0x0f;
}
if (DirPortCL == PortDirection.Input)
{
// lower port C bits
val |= 0x0f;
}
return val;
}
return val;
}
#endregion
#endregion
#endregion
#endregion
#region Reset
#region Reset
public void Reset()
{
for (int i = 0; i < 3; i++)
{
Regs[i] = 0xff;
}
public void Reset()
{
for (int i = 0; i < 3; i++)
{
Regs[i] = 0xff;
}
Regs[3] = 0xff;
}
Regs[3] = 0xff;
}
#endregion
#endregion
#region IPortIODevice
#region IPortIODevice
/*
/*
#F4XX %xxxx0x00 xxxxxxxx 8255 PIO Port A (PSG Data) Read Write
#F5XX %xxxx0x01 xxxxxxxx 8255 PIO Port B (Vsync,PrnBusy,Tape,etc.) Read -
#F6XX %xxxx0x10 xxxxxxxx 8255 PIO Port C (KeybRow,Tape,PSG Control) - Write
#F7XX %xxxx0x11 xxxxxxxx 8255 PIO Control-Register - Write
*/
/// <summary>
/// Device responds to an IN instruction
/// </summary>
public bool ReadPort(ushort port, ref int result)
{
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
/// <summary>
/// Device responds to an IN instruction
/// </summary>
public bool ReadPort(ushort port, ref int result)
{
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
// The 8255 responds to bit 11 reset with A10 and A12-A15 set
//if (portUpper.Bit(3))
//return false;
// The 8255 responds to bit 11 reset with A10 and A12-A15 set
//if (portUpper.Bit(3))
//return false;
var PPIFunc = (port & 0x0300) >> 8; // portUpper & 3;
var PPIFunc = (port & 0x0300) >> 8; // portUpper & 3;
switch (PPIFunc)
{
// Port A Read
case 0:
// PSG (Sound/Keyboard/Joystick)
result = INPortA();
switch (PPIFunc)
{
// Port A Read
case 0:
break;
// PSG (Sound/Keyboard/Joystick)
result = INPortA();
// Port B Read
case 1:
break;
// Vsync/Jumpers/PrinterBusy/CasIn/Exp
result = INPortB();
// Port B Read
case 1:
break;
// Vsync/Jumpers/PrinterBusy/CasIn/Exp
result = INPortB();
// Port C Read (docs define this as write-only but we do need to do some processing)
case 2:
break;
// KeybRow/CasOut/PSG
result = INPortC();
// Port C Read (docs define this as write-only but we do need to do some processing)
case 2:
break;
}
// KeybRow/CasOut/PSG
result = INPortC();
return true;
}
break;
}
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
public bool WritePort(ushort port, int result)
{
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
return true;
}
// The 8255 responds to bit 11 reset with A10 and A12-A15 set
if (portUpper.Bit(3))
return false;
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
public bool WritePort(ushort port, int result)
{
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
var PPIFunc = portUpper & 3;
// The 8255 responds to bit 11 reset with A10 and A12-A15 set
if (portUpper.Bit(3))
return false;
switch (PPIFunc)
{
// Port A Write
case 0:
var PPIFunc = portUpper & 3;
// PSG (Sound/Keyboard/Joystick)
OUTPortA(result);
switch (PPIFunc)
{
// Port A Write
case 0:
break;
// PSG (Sound/Keyboard/Joystick)
OUTPortA(result);
// Port B Write
case 1:
break;
// Vsync/Jumpers/PrinterBusy/CasIn/Exp
OUTPortB(result);
// Port B Write
case 1:
break;
// Vsync/Jumpers/PrinterBusy/CasIn/Exp
OUTPortB(result);
// Port C Write
case 2:
break;
// KeybRow/CasOut/PSG
OUTPortC(result);
// Port C Write
case 2:
break;
// KeybRow/CasOut/PSG
OUTPortC(result);
// Control Register Write
case 3:
break;
// Control
OUTControl((byte)result);
// Control Register Write
case 3:
break;
}
// Control
OUTControl((byte)result);
return true;
}
break;
}
#endregion
return true;
}
#region Serialization
#endregion
public void SyncState(Serializer ser)
{
ser.BeginSection("PPI");
ser.Sync(nameof(Regs), ref Regs, false);
ser.EndSection();
}
#region Serialization
#endregion
}
public void SyncState(Serializer ser)
{
ser.BeginSection("PPI");
ser.Sync(nameof(Regs), ref Regs, false);
ser.EndSection();
}
public enum PortDirection
{
Input,
Output
}
#endregion
}
public enum PortDirection
{
Input,
Output
}
}

View File

@ -4,206 +4,206 @@ using System;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Logical Beeper class
/// Used to emulate the sound generated by tape loading
/// This implementation uses BlipBuffer and should *always* output at 44100 with 882 samples per frame
/// (so that it can be mixed easily further down the line)
/// </summary>
public class Beeper : ISoundProvider, IBeeperDevice
{
#region Fields and Properties
/// <summary>
/// Logical Beeper class
/// Used to emulate the sound generated by tape loading
/// This implementation uses BlipBuffer and should *always* output at 44100 with 882 samples per frame
/// (so that it can be mixed easily further down the line)
/// </summary>
public class Beeper : ISoundProvider, IBeeperDevice
{
#region Fields and Properties
/// <summary>
/// Sample Rate
/// This usually has to be 44100 for ISoundProvider
/// </summary>
private int _sampleRate;
public int SampleRate
{
get { return _sampleRate; }
set { _sampleRate = value; }
}
/// <summary>
/// Sample Rate
/// This usually has to be 44100 for ISoundProvider
/// </summary>
private int _sampleRate;
public int SampleRate
{
get { return _sampleRate; }
set { _sampleRate = value; }
}
/// <summary>
/// Buzzer volume
/// Accepts an int 0-100 value
/// </summary>
private int _volume;
public int Volume
{
get
{
return VolumeConverterOut(_volume);
}
set
{
var newVol = VolumeConverterIn(value);
if (newVol != _volume)
blip.Clear();
_volume = VolumeConverterIn(value);
}
}
/// <summary>
/// Buzzer volume
/// Accepts an int 0-100 value
/// </summary>
private int _volume;
public int Volume
{
get
{
return VolumeConverterOut(_volume);
}
set
{
var newVol = VolumeConverterIn(value);
if (newVol != _volume)
blip.Clear();
_volume = VolumeConverterIn(value);
}
}
/// <summary>
/// The last used volume (used to modify blipbuffer delta values)
/// </summary>
private int lastVolume;
/// <summary>
/// The last used volume (used to modify blipbuffer delta values)
/// </summary>
private int lastVolume;
/// <summary>
/// The number of cpu cycles per frame
/// </summary>
private long _tStatesPerFrame;
/// <summary>
/// The number of cpu cycles per frame
/// </summary>
private long _tStatesPerFrame;
/// <summary>
/// The parent emulated machine
/// </summary>
private CPCBase _machine;
/// <summary>
/// The parent emulated machine
/// </summary>
private CPCBase _machine;
/// <summary>
/// The last pulse
/// </summary>
private bool LastPulse;
/// <summary>
/// The last pulse
/// </summary>
private bool LastPulse;
/// <summary>
/// The last T-State (cpu cycle) that the last pulse was received
/// </summary>
private long LastPulseTState;
/// <summary>
/// The last T-State (cpu cycle) that the last pulse was received
/// </summary>
private long LastPulseTState;
/// <summary>
/// Device blipbuffer
/// </summary>
private readonly BlipBuffer blip = new BlipBuffer(883);
/// <summary>
/// Device blipbuffer
/// </summary>
private readonly BlipBuffer blip = new BlipBuffer(883);
#endregion
#endregion
#region Private Methods
#region Private Methods
/// <summary>
/// Takes an int 0-100 and returns the relevant short volume to output
/// </summary>
private int VolumeConverterIn(int vol)
{
int maxLimit = short.MaxValue / 3;
int increment = maxLimit / 100;
/// <summary>
/// Takes an int 0-100 and returns the relevant short volume to output
/// </summary>
private int VolumeConverterIn(int vol)
{
int maxLimit = short.MaxValue / 3;
int increment = maxLimit / 100;
return vol * increment;
}
return vol * increment;
}
/// <summary>
/// Takes an short volume and returns the relevant int value 0-100
/// </summary>
private int VolumeConverterOut(int shortvol)
{
int maxLimit = short.MaxValue / 3;
int increment = maxLimit / 100;
/// <summary>
/// Takes an short volume and returns the relevant int value 0-100
/// </summary>
private int VolumeConverterOut(int shortvol)
{
int maxLimit = short.MaxValue / 3;
int increment = maxLimit / 100;
if (shortvol > maxLimit)
shortvol = maxLimit;
if (shortvol > maxLimit)
shortvol = maxLimit;
return shortvol / increment;
}
return shortvol / increment;
}
#endregion
#endregion
#region Construction & Initialisation
#region Construction & Initialisation
public Beeper(CPCBase machine)
{
_machine = machine;
}
public Beeper(CPCBase machine)
{
_machine = machine;
}
/// <summary>
/// Initialises the beeper
/// </summary>
public void Init(int sampleRate, int tStatesPerFrame)
{
blip.SetRates((4000000), sampleRate);
_sampleRate = sampleRate;
_tStatesPerFrame = tStatesPerFrame;
}
/// <summary>
/// Initialises the beeper
/// </summary>
public void Init(int sampleRate, int tStatesPerFrame)
{
blip.SetRates((4000000), sampleRate);
_sampleRate = sampleRate;
_tStatesPerFrame = tStatesPerFrame;
}
#endregion
#endregion
#region IBeeperDevice
#region IBeeperDevice
/// <summary>
/// Processes an incoming pulse value and adds it to the blipbuffer
/// </summary>
public void ProcessPulseValue(bool pulse)
{
if (!_machine._renderSound)
return;
/// <summary>
/// Processes an incoming pulse value and adds it to the blipbuffer
/// </summary>
public void ProcessPulseValue(bool pulse)
{
if (!_machine._renderSound)
return;
if (LastPulse == pulse)
{
// no change
blip.AddDelta((uint)_machine.CurrentFrameCycle, 0);
}
else
{
if (pulse)
blip.AddDelta((uint)_machine.CurrentFrameCycle, (short)(_volume));
else
blip.AddDelta((uint)_machine.CurrentFrameCycle, -(short)(_volume));
if (LastPulse == pulse)
{
// no change
blip.AddDelta((uint)_machine.CurrentFrameCycle, 0);
}
lastVolume = _volume;
}
else
{
if (pulse)
blip.AddDelta((uint)_machine.CurrentFrameCycle, (short)(_volume));
else
blip.AddDelta((uint)_machine.CurrentFrameCycle, -(short)(_volume));
LastPulse = pulse;
}
lastVolume = _volume;
}
#endregion
LastPulse = pulse;
}
#region ISoundProvider
#endregion
public bool CanProvideAsync => false;
#region ISoundProvider
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public bool CanProvideAsync => false;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
throw new InvalidOperationException("Only Sync mode is supported.");
}
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
throw new InvalidOperationException("Only Sync mode is supported.");
}
public void DiscardSamples()
{
blip.Clear();
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
blip.EndFrame((uint)_tStatesPerFrame);
nsamp = blip.SamplesAvailable();
samples = new short[nsamp * 2];
blip.ReadSamples(samples, nsamp, true);
for (int i = 0; i < nsamp * 2; i += 2)
{
samples[i + 1] = samples[i];
}
}
public void DiscardSamples()
{
blip.Clear();
}
#endregion
public void GetSamplesSync(out short[] samples, out int nsamp)
{
blip.EndFrame((uint)_tStatesPerFrame);
nsamp = blip.SamplesAvailable();
samples = new short[nsamp * 2];
blip.ReadSamples(samples, nsamp, true);
for (int i = 0; i < nsamp * 2; i += 2)
{
samples[i + 1] = samples[i];
}
}
#region State Serialization
#endregion
public void SyncState(Serializer ser)
{
ser.BeginSection("Buzzer");
ser.Sync(nameof(_tStatesPerFrame), ref _tStatesPerFrame);
ser.Sync(nameof(_sampleRate), ref _sampleRate);
ser.Sync(nameof(LastPulse), ref LastPulse);
ser.Sync(nameof(LastPulseTState), ref LastPulseTState);
ser.EndSection();
}
#region State Serialization
#endregion
}
public void SyncState(Serializer ser)
{
ser.BeginSection("Buzzer");
ser.Sync(nameof(_tStatesPerFrame), ref _tStatesPerFrame);
ser.Sync(nameof(_sampleRate), ref _sampleRate);
ser.Sync(nameof(LastPulse), ref LastPulse);
ser.Sync(nameof(LastPulseTState), ref LastPulseTState);
ser.EndSection();
}
#endregion
}
}

View File

@ -6,142 +6,142 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPC464
/// * Memory *
/// </summary>
public partial class CPC464 : CPCBase
{
/// <summary>
/// Simulates reading from the bus
/// ROM paging should be handled here
/// </summary>
public override byte ReadBus(ushort addr)
{
int divisor = addr / 0x4000;
byte result = 0xff;
/// <summary>
/// CPC464
/// * Memory *
/// </summary>
public partial class CPC464 : CPCBase
{
/// <summary>
/// Simulates reading from the bus
/// ROM paging should be handled here
/// </summary>
public override byte ReadBus(ushort addr)
{
int divisor = addr / 0x4000;
byte result = 0xff;
switch (divisor)
{
// 0x000 or LowerROM
case 0:
if (LowerROMPaged)
result = ROMLower[addr % 0x4000];
else
result = RAM0[addr % 0x4000];
break;
switch (divisor)
{
// 0x000 or LowerROM
case 0:
if (LowerROMPaged)
result = ROMLower[addr % 0x4000];
else
result = RAM0[addr % 0x4000];
break;
// 0x4000
case 1:
result = RAM1[addr % 0x4000];
break;
// 0x4000
case 1:
result = RAM1[addr % 0x4000];
break;
// 0x8000
case 2:
result = RAM2[addr % 0x4000];
break;
// 0x8000
case 2:
result = RAM2[addr % 0x4000];
break;
// 0xc000 or UpperROM
case 3:
if (UpperROMPaged)
result = ROM0[addr % 0x4000];
else
result = RAM3[addr % 0x4000];
break;
default:
break;
}
// 0xc000 or UpperROM
case 3:
if (UpperROMPaged)
result = ROM0[addr % 0x4000];
else
result = RAM3[addr % 0x4000];
break;
default:
break;
}
return result;
}
return result;
}
/// <summary>
/// Simulates writing to the bus
/// Writes to the bus ALWAYS go to RAM, regardless of what upper and lower ROMs are paged in
/// </summary>
public override void WriteBus(ushort addr, byte value)
{
int divisor = addr / 0x4000;
/// <summary>
/// Simulates writing to the bus
/// Writes to the bus ALWAYS go to RAM, regardless of what upper and lower ROMs are paged in
/// </summary>
public override void WriteBus(ushort addr, byte value)
{
int divisor = addr / 0x4000;
switch (divisor)
{
// RAM 0x000
case 0:
RAM0[addr % 0x4000] = value;
break;
switch (divisor)
{
// RAM 0x000
case 0:
RAM0[addr % 0x4000] = value;
break;
// RAM 0x4000
case 1:
RAM1[addr % 0x4000] = value;
break;
// RAM 0x4000
case 1:
RAM1[addr % 0x4000] = value;
break;
// RAM 0x8000
case 2:
RAM2[addr % 0x4000] = value;
break;
// RAM 0x8000
case 2:
RAM2[addr % 0x4000] = value;
break;
// RAM 0xc000
case 3:
RAM3[addr % 0x4000] = value;
break;
default:
break;
}
}
// RAM 0xc000
case 3:
RAM3[addr % 0x4000] = value;
break;
default:
break;
}
}
/// <summary>
/// Reads a byte of data from a specified memory address
/// </summary>
public override byte ReadMemory(ushort addr)
{
var data = ReadBus(addr);
return data;
}
/// <summary>
/// Reads a byte of data from a specified memory address
/// </summary>
public override byte ReadMemory(ushort addr)
{
var data = ReadBus(addr);
return data;
}
/// <summary>
/// Writes a byte of data to a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public override void WriteMemory(ushort addr, byte value)
{
WriteBus(addr, value);
}
/// <summary>
/// Writes a byte of data to a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public override void WriteMemory(ushort addr, byte value)
{
WriteBus(addr, value);
}
/// <summary>
/// Sets up the ROM
/// </summary>
public override void InitROM(RomData[] romData)
{
foreach (var r in romData)
{
if (r.ROMType == RomData.ROMChipType.Lower)
{
for (int i = 0; i < 0x4000; i++)
{
ROMLower[i] = r.RomBytes[i];
/// <summary>
/// Sets up the ROM
/// </summary>
public override void InitROM(RomData[] romData)
{
foreach (var r in romData)
{
if (r.ROMType == RomData.ROMChipType.Lower)
{
for (int i = 0; i < 0x4000; i++)
{
ROMLower[i] = r.RomBytes[i];
}
}
else
{
for (int i = 0; i < 0x4000; i++)
{
switch (r.ROMPosition)
{
case 0:
ROM0[i] = r.RomBytes[i];
break;
case 7:
ROM7[i] = r.RomBytes[i];
break;
}
}
}
}
}
}
else
{
for (int i = 0; i < 0x4000; i++)
{
switch (r.ROMPosition)
{
case 0:
ROM0[i] = r.RomBytes[i];
break;
case 7:
ROM7[i] = r.RomBytes[i];
break;
}
}
}
}
LowerROMPaged = true;
UpperROMPaged = true;
}
}
LowerROMPaged = true;
UpperROMPaged = true;
}
}
}

View File

@ -7,97 +7,97 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPC464
/// * Port *
/// </summary>
public partial class CPC464 : CPCBase
{
/// <summary>
/// Reads a byte of data from a specified port address
/// </summary>
public override byte ReadPort(ushort port)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
/// <summary>
/// CPC464
/// * Port *
/// </summary>
public partial class CPC464 : CPCBase
{
/// <summary>
/// Reads a byte of data from a specified port address
/// </summary>
public override byte ReadPort(ushort port)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
int result = 0xff;
int result = 0xff;
if (DecodeINPort(port) == PortDevice.GateArray)
{
GateArray.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.CRCT)
{
CRCT.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.ROMSelect)
{
if (DecodeINPort(port) == PortDevice.GateArray)
{
GateArray.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.CRCT)
{
CRCT.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.ROMSelect)
{
}
else if (DecodeINPort(port) == PortDevice.Printer)
{
}
else if (DecodeINPort(port) == PortDevice.Printer)
{
}
else if (DecodeINPort(port) == PortDevice.PPI)
{
PPI.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.Expansion)
{
}
else if (DecodeINPort(port) == PortDevice.PPI)
{
PPI.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.Expansion)
{
}
}
return (byte)result;
}
return (byte)result;
}
/// <summary>
/// Writes a byte of data to a specified port address
/// Because of the port decoding, multiple devices can be written to
/// </summary>
public override void WritePort(ushort port, byte value)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
BitArray dataBits = new BitArray(BitConverter.GetBytes(value));
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
/// <summary>
/// Writes a byte of data to a specified port address
/// Because of the port decoding, multiple devices can be written to
/// </summary>
public override void WritePort(ushort port, byte value)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
BitArray dataBits = new BitArray(BitConverter.GetBytes(value));
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
var devs = DecodeOUTPort(port);
var devs = DecodeOUTPort(port);
foreach (var d in devs)
{
if (d == PortDevice.GateArray)
{
GateArray.WritePort(port, value);
}
else if (d == PortDevice.RAMManagement)
{
// not present in the unexpanded CPC464
}
else if (d == PortDevice.CRCT)
{
CRCT.WritePort(port, value);
}
else if (d == PortDevice.ROMSelect)
{
foreach (var d in devs)
{
if (d == PortDevice.GateArray)
{
GateArray.WritePort(port, value);
}
else if (d == PortDevice.RAMManagement)
{
// not present in the unexpanded CPC464
}
else if (d == PortDevice.CRCT)
{
CRCT.WritePort(port, value);
}
else if (d == PortDevice.ROMSelect)
{
}
else if (d == PortDevice.Printer)
{
}
else if (d == PortDevice.Printer)
{
}
else if (d == PortDevice.PPI)
{
PPI.WritePort(port, value);
}
else if (d == PortDevice.Expansion)
{
}
else if (d == PortDevice.PPI)
{
PPI.WritePort(port, value);
}
else if (d == PortDevice.Expansion)
{
}
}
}
}
return;
}
}
return;
}
}
}

View File

@ -4,43 +4,43 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPC464 construction
/// </summary>
public partial class CPC464 : CPCBase
{
#region Construction
/// <summary>
/// CPC464 construction
/// </summary>
public partial class CPC464 : CPCBase
{
#region Construction
/// <summary>
/// Main constructor
/// </summary>
public CPC464(AmstradCPC cpc, Z80A cpu, List<byte[]> files, bool autoTape, AmstradCPC.BorderType borderType)
{
CPC = cpc;
CPU = cpu;
/// <summary>
/// Main constructor
/// </summary>
public CPC464(AmstradCPC cpc, Z80A cpu, List<byte[]> files, bool autoTape, AmstradCPC.BorderType borderType)
{
CPC = cpc;
CPU = cpu;
FrameLength = 79872;
FrameLength = 79872;
CRCT = new CRCT_6845(CRCT_6845.CRCTType.MC6845, this);
//CRT = new CRTDevice(this);
GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007);
PPI = new PPI_8255(this);
CRCT = new CRCT_6845(CRCT_6845.CRCTType.MC6845, this);
//CRT = new CRTDevice(this);
GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007);
PPI = new PPI_8255(this);
TapeBuzzer = new Beeper(this);
TapeBuzzer.Init(44100, FrameLength);
TapeBuzzer = new Beeper(this);
TapeBuzzer.Init(44100, FrameLength);
//AYDevice = new PSG(this, PSG.ay38910_type_t.AY38910_TYPE_8912, GateArray.PSGClockSpeed, 882 * 50);
AYDevice = new AY38912(this);
AYDevice.Init(44100, FrameLength);
//AYDevice = new PSG(this, PSG.ay38910_type_t.AY38910_TYPE_8912, GateArray.PSGClockSpeed, 882 * 50);
AYDevice = new AY38912(this);
AYDevice.Init(44100, FrameLength);
KeyboardDevice = new StandardKeyboard(this);
KeyboardDevice = new StandardKeyboard(this);
TapeDevice = new DatacorderDevice(autoTape);
TapeDevice.Init(this);
TapeDevice = new DatacorderDevice(autoTape);
TapeDevice.Init(this);
InitializeMedia(files);
}
InitializeMedia(files);
}
#endregion
}
#endregion
}
}

View File

@ -6,257 +6,257 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPC6128
/// * Memory *
/// </summary>
public partial class CPC6128 : CPCBase
{
/// <summary>
/// Simulates reading from the bus
/// ROM and RAM paging should be handled here
/// </summary>
public override byte ReadBus(ushort addr)
{
int divisor = addr / 0x4000;
byte result = 0xff;
/// <summary>
/// CPC6128
/// * Memory *
/// </summary>
public partial class CPC6128 : CPCBase
{
/// <summary>
/// Simulates reading from the bus
/// ROM and RAM paging should be handled here
/// </summary>
public override byte ReadBus(ushort addr)
{
int divisor = addr / 0x4000;
byte result = 0xff;
switch (divisor)
{
// RAM 0x000
case 0:
if (LowerROMPaged)
{
result = ROMLower[addr % 0x4000];
}
else
{
switch (RAMConfig)
{
case 2:
result = RAM4[addr % 0x4000];
break;
default:
result = RAM0[addr % 0x4000];
break;
}
}
break;
switch (divisor)
{
// RAM 0x000
case 0:
if (LowerROMPaged)
{
result = ROMLower[addr % 0x4000];
}
else
{
switch (RAMConfig)
{
case 2:
result = RAM4[addr % 0x4000];
break;
default:
result = RAM0[addr % 0x4000];
break;
}
}
break;
// RAM 0x4000
case 1:
switch (RAMConfig)
{
case 0:
case 1:
result = RAM1[addr % 0x4000];
break;
case 2:
case 5:
result = RAM5[addr % 0x4000];
break;
case 3:
result = RAM3[addr % 0x4000];
break;
case 4:
result = RAM4[addr % 0x4000];
break;
case 6:
result = RAM6[addr % 0x4000];
break;
case 7:
result = RAM7[addr % 0x4000];
break;
}
// RAM 0x4000
case 1:
switch (RAMConfig)
{
case 0:
case 1:
result = RAM1[addr % 0x4000];
break;
case 2:
case 5:
result = RAM5[addr % 0x4000];
break;
case 3:
result = RAM3[addr % 0x4000];
break;
case 4:
result = RAM4[addr % 0x4000];
break;
case 6:
result = RAM6[addr % 0x4000];
break;
case 7:
result = RAM7[addr % 0x4000];
break;
}
break;
break;
// RAM 0x8000
case 2:
switch (RAMConfig)
{
case 2:
result = RAM6[addr % 0x4000];
break;
default:
result = RAM2[addr % 0x4000];
break;
}
break;
// RAM 0x8000
case 2:
switch (RAMConfig)
{
case 2:
result = RAM6[addr % 0x4000];
break;
default:
result = RAM2[addr % 0x4000];
break;
}
break;
// RAM 0xc000
case 3:
if (UpperROMPaged)
{
switch (UpperROMPosition)
{
case 7:
result = ROM7[addr % 0x4000];
break;
case 0:
default:
result = ROM0[addr % 0x4000];
break;
}
}
else
{
switch (RAMConfig)
{
case 1:
case 2:
case 3:
result = RAM7[addr % 0x4000];
break;
default:
result = RAM3[addr % 0x4000];
break;
}
}
break;
default:
break;
}
// RAM 0xc000
case 3:
if (UpperROMPaged)
{
switch (UpperROMPosition)
{
case 7:
result = ROM7[addr % 0x4000];
break;
case 0:
default:
result = ROM0[addr % 0x4000];
break;
}
}
else
{
switch (RAMConfig)
{
case 1:
case 2:
case 3:
result = RAM7[addr % 0x4000];
break;
default:
result = RAM3[addr % 0x4000];
break;
}
}
break;
default:
break;
}
return result;
}
return result;
}
/// <summary>
/// Simulates writing to the bus
/// Writes to the bus ALWAYS go to RAM, regardless of what upper and lower ROMs are paged in
/// </summary>
public override void WriteBus(ushort addr, byte value)
{
int divisor = addr / 0x4000;
/// <summary>
/// Simulates writing to the bus
/// Writes to the bus ALWAYS go to RAM, regardless of what upper and lower ROMs are paged in
/// </summary>
public override void WriteBus(ushort addr, byte value)
{
int divisor = addr / 0x4000;
switch (divisor)
{
// RAM 0x000
case 0:
switch (RAMConfig)
{
case 2:
RAM4[addr % 0x4000] = value;
break;
default:
RAM0[addr % 0x4000] = value;
break;
}
break;
switch (divisor)
{
// RAM 0x000
case 0:
switch (RAMConfig)
{
case 2:
RAM4[addr % 0x4000] = value;
break;
default:
RAM0[addr % 0x4000] = value;
break;
}
break;
// RAM 0x4000
case 1:
switch (RAMConfig)
{
case 0:
case 1:
RAM1[addr % 0x4000] = value;
break;
case 2:
case 5:
RAM5[addr % 0x4000] = value;
break;
case 3:
RAM3[addr % 0x4000] = value;
break;
case 4:
RAM4[addr % 0x4000] = value;
break;
case 6:
RAM6[addr % 0x4000] = value;
break;
case 7:
RAM7[addr % 0x4000] = value;
break;
}
break;
// RAM 0x4000
case 1:
switch (RAMConfig)
{
case 0:
case 1:
RAM1[addr % 0x4000] = value;
break;
case 2:
case 5:
RAM5[addr % 0x4000] = value;
break;
case 3:
RAM3[addr % 0x4000] = value;
break;
case 4:
RAM4[addr % 0x4000] = value;
break;
case 6:
RAM6[addr % 0x4000] = value;
break;
case 7:
RAM7[addr % 0x4000] = value;
break;
}
// RAM 0x8000
case 2:
switch (RAMConfig)
{
case 2:
RAM6[addr % 0x4000] = value;
break;
default:
RAM2[addr % 0x4000] = value;
break;
}
break;
break;
// RAM 0xc000
case 3:
switch (RAMConfig)
{
case 1:
case 2:
case 3:
RAM7[addr % 0x4000] = value;
break;
default:
RAM3[addr % 0x4000] = value;
break;
}
break;
default:
break;
}
}
// RAM 0x8000
case 2:
switch (RAMConfig)
{
case 2:
RAM6[addr % 0x4000] = value;
break;
default:
RAM2[addr % 0x4000] = value;
break;
}
break;
/// <summary>
/// Reads a byte of data from a specified memory address
/// </summary>
public override byte ReadMemory(ushort addr)
{
var data = ReadBus(addr);
return data;
}
// RAM 0xc000
case 3:
switch (RAMConfig)
{
case 1:
case 2:
case 3:
RAM7[addr % 0x4000] = value;
break;
default:
RAM3[addr % 0x4000] = value;
break;
}
break;
default:
break;
}
}
/// <summary>
/// Writes a byte of data to a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public override void WriteMemory(ushort addr, byte value)
{
WriteBus(addr, value);
}
/// <summary>
/// Reads a byte of data from a specified memory address
/// </summary>
public override byte ReadMemory(ushort addr)
{
var data = ReadBus(addr);
return data;
}
/// <summary>
/// Writes a byte of data to a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public override void WriteMemory(ushort addr, byte value)
{
WriteBus(addr, value);
}
/// <summary>
/// Sets up the ROM
/// </summary>
public override void InitROM(RomData[] romData)
{
foreach (var r in romData)
{
if (r.ROMType == RomData.ROMChipType.Lower)
{
for (int i = 0; i < 0x4000; i++)
{
ROMLower[i] = r.RomBytes[i];
/// <summary>
/// Sets up the ROM
/// </summary>
public override void InitROM(RomData[] romData)
{
foreach (var r in romData)
{
if (r.ROMType == RomData.ROMChipType.Lower)
{
for (int i = 0; i < 0x4000; i++)
{
ROMLower[i] = r.RomBytes[i];
}
}
else
{
for (int i = 0; i < 0x4000; i++)
{
switch (r.ROMPosition)
{
case 0:
ROM0[i] = r.RomBytes[i];
break;
case 7:
ROM7[i] = r.RomBytes[i];
break;
}
}
}
}
}
}
else
{
for (int i = 0; i < 0x4000; i++)
{
switch (r.ROMPosition)
{
case 0:
ROM0[i] = r.RomBytes[i];
break;
case 7:
ROM7[i] = r.RomBytes[i];
break;
}
}
}
}
LowerROMPaged = true;
UpperROMPaged = true;
}
}
LowerROMPaged = true;
UpperROMPaged = true;
}
}
}

View File

@ -8,129 +8,129 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPC6128
/// * Port *
/// </summary>
public partial class CPC6128 : CPCBase
{
/// <summary>
/// Reads a byte of data from a specified port address
/// </summary>
public override byte ReadPort(ushort port)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
/// <summary>
/// CPC6128
/// * Port *
/// </summary>
public partial class CPC6128 : CPCBase
{
/// <summary>
/// Reads a byte of data from a specified port address
/// </summary>
public override byte ReadPort(ushort port)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
int result = 0xff;
int result = 0xff;
if (DecodeINPort(port) == PortDevice.GateArray)
{
GateArray.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.CRCT)
{
CRCT.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.ROMSelect)
{
if (DecodeINPort(port) == PortDevice.GateArray)
{
GateArray.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.CRCT)
{
CRCT.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.ROMSelect)
{
}
else if (DecodeINPort(port) == PortDevice.Printer)
{
}
else if (DecodeINPort(port) == PortDevice.Printer)
{
}
else if (DecodeINPort(port) == PortDevice.PPI)
{
PPI.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.Expansion)
{
if (!port.Bit(7))
{
// FDC
if (port.Bit(8) && !port.Bit(0))
{
// FDC status register
UPDDiskDevice.ReadStatus(ref result);
}
if (port.Bit(8) && port.Bit(0))
{
// FDC data register
UPDDiskDevice.ReadData(ref result);
}
}
}
}
else if (DecodeINPort(port) == PortDevice.PPI)
{
PPI.ReadPort(port, ref result);
}
else if (DecodeINPort(port) == PortDevice.Expansion)
{
if (!port.Bit(7))
{
// FDC
if (port.Bit(8) && !port.Bit(0))
{
// FDC status register
UPDDiskDevice.ReadStatus(ref result);
}
if (port.Bit(8) && port.Bit(0))
{
// FDC data register
UPDDiskDevice.ReadData(ref result);
}
}
}
return (byte)result;
}
return (byte)result;
}
/// <summary>
/// Writes a byte of data to a specified port address
/// Because of the port decoding, multiple devices can be written to
/// </summary>
public override void WritePort(ushort port, byte value)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
BitArray dataBits = new BitArray(BitConverter.GetBytes(value));
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
/// <summary>
/// Writes a byte of data to a specified port address
/// Because of the port decoding, multiple devices can be written to
/// </summary>
public override void WritePort(ushort port, byte value)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
BitArray dataBits = new BitArray(BitConverter.GetBytes(value));
byte portUpper = (byte)(port >> 8);
byte portLower = (byte)(port & 0xff);
var devs = DecodeOUTPort(port);
var devs = DecodeOUTPort(port);
foreach (var d in devs)
{
if (d == PortDevice.GateArray)
{
GateArray.WritePort(port, value);
}
else if (d == PortDevice.RAMManagement)
{
if (value.Bit(7) && value.Bit(6))
{
RAMConfig = value & 0x07;
foreach (var d in devs)
{
if (d == PortDevice.GateArray)
{
GateArray.WritePort(port, value);
}
else if (d == PortDevice.RAMManagement)
{
if (value.Bit(7) && value.Bit(6))
{
RAMConfig = value & 0x07;
// additional 64K bank index
var b64 = value & 0x38;
}
}
else if (d == PortDevice.CRCT)
{
CRCT.WritePort(port, value);
}
else if (d == PortDevice.ROMSelect)
{
UpperROMPosition = value;
}
else if (d == PortDevice.Printer)
{
// additional 64K bank index
var b64 = value & 0x38;
}
}
else if (d == PortDevice.CRCT)
{
CRCT.WritePort(port, value);
}
else if (d == PortDevice.ROMSelect)
{
UpperROMPosition = value;
}
else if (d == PortDevice.Printer)
{
}
else if (d == PortDevice.PPI)
{
PPI.WritePort(port, value);
}
else if (d == PortDevice.Expansion)
{
if (!port.Bit(7))
{
// FDC
if (port.Bit(8) && !port.Bit(0) || port.Bit(8) && port.Bit(0))
{
// FDC data register
UPDDiskDevice.WriteData(value);
}
if ((!port.Bit(8) && !port.Bit(0)) || (!port.Bit(8) && port.Bit(0)))
{
// FDC motor
UPDDiskDevice.Motor(value);
}
}
}
}
}
else if (d == PortDevice.PPI)
{
PPI.WritePort(port, value);
}
else if (d == PortDevice.Expansion)
{
if (!port.Bit(7))
{
// FDC
if (port.Bit(8) && !port.Bit(0) || port.Bit(8) && port.Bit(0))
{
// FDC data register
UPDDiskDevice.WriteData(value);
}
if ((!port.Bit(8) && !port.Bit(0)) || (!port.Bit(8) && port.Bit(0)))
{
// FDC motor
UPDDiskDevice.Motor(value);
}
}
}
}
return;
}
}
return;
}
}
}

View File

@ -4,46 +4,46 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// CPC6128 construction
/// </summary>
public partial class CPC6128 : CPCBase
{
#region Construction
/// <summary>
/// CPC6128 construction
/// </summary>
public partial class CPC6128 : CPCBase
{
#region Construction
/// <summary>
/// Main constructor
/// </summary>
public CPC6128(AmstradCPC cpc, Z80A cpu, List<byte[]> files, bool autoTape, AmstradCPC.BorderType borderType)
{
CPC = cpc;
CPU = cpu;
/// <summary>
/// Main constructor
/// </summary>
public CPC6128(AmstradCPC cpc, Z80A cpu, List<byte[]> files, bool autoTape, AmstradCPC.BorderType borderType)
{
CPC = cpc;
CPU = cpu;
FrameLength = 79872;
FrameLength = 79872;
CRCT = new CRCT_6845(CRCT_6845.CRCTType.MC6845, this);
//CRT = new CRTDevice(this);
GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007);
PPI = new PPI_8255(this);
CRCT = new CRCT_6845(CRCT_6845.CRCTType.MC6845, this);
//CRT = new CRTDevice(this);
GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007);
PPI = new PPI_8255(this);
TapeBuzzer = new Beeper(this);
TapeBuzzer.Init(44100, FrameLength);
TapeBuzzer = new Beeper(this);
TapeBuzzer.Init(44100, FrameLength);
//AYDevice = new PSG(this, PSG.ay38910_type_t.AY38910_TYPE_8912, GateArray.PSGClockSpeed, 882 * 50);
AYDevice = new AY38912(this);
AYDevice.Init(44100, FrameLength);
//AYDevice = new PSG(this, PSG.ay38910_type_t.AY38910_TYPE_8912, GateArray.PSGClockSpeed, 882 * 50);
AYDevice = new AY38912(this);
AYDevice.Init(44100, FrameLength);
KeyboardDevice = new StandardKeyboard(this);
KeyboardDevice = new StandardKeyboard(this);
TapeDevice = new DatacorderDevice(autoTape);
TapeDevice.Init(this);
TapeDevice = new DatacorderDevice(autoTape);
TapeDevice.Init(this);
UPDDiskDevice = new NECUPD765();
UPDDiskDevice.Init(this);
UPDDiskDevice = new NECUPD765();
UPDDiskDevice.Init(this);
InitializeMedia(files);
}
InitializeMedia(files);
}
#endregion
}
#endregion
}
}

View File

@ -3,293 +3,293 @@ using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Input *
/// </summary>
public abstract partial class CPCBase
{
string Play = "Play Tape";
string Stop = "Stop Tape";
string RTZ = "RTZ Tape";
string Record = "Record Tape";
string NextTape = "Insert Next Tape";
string PrevTape = "Insert Previous Tape";
string NextBlock = "Next Tape Block";
string PrevBlock = "Prev Tape Block";
string TapeStatus = "Get Tape Status";
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Input *
/// </summary>
public abstract partial class CPCBase
{
string Play = "Play Tape";
string Stop = "Stop Tape";
string RTZ = "RTZ Tape";
string Record = "Record Tape";
string NextTape = "Insert Next Tape";
string PrevTape = "Insert Previous Tape";
string NextBlock = "Next Tape Block";
string PrevBlock = "Prev Tape Block";
string TapeStatus = "Get Tape Status";
string NextDisk = "Insert Next Disk";
string PrevDisk = "Insert Previous Disk";
string EjectDisk = "Eject Current Disk";
string DiskStatus = "Get Disk Status";
string NextDisk = "Insert Next Disk";
string PrevDisk = "Insert Previous Disk";
string EjectDisk = "Eject Current Disk";
string DiskStatus = "Get Disk Status";
string HardResetStr = "Power";
string SoftResetStr = "Reset";
string HardResetStr = "Power";
string SoftResetStr = "Reset";
bool pressed_Play = false;
bool pressed_Stop = false;
bool pressed_RTZ = false;
bool pressed_NextTape = false;
bool pressed_PrevTape = false;
bool pressed_NextBlock = false;
bool pressed_PrevBlock = false;
bool pressed_TapeStatus = false;
bool pressed_NextDisk = false;
bool pressed_PrevDisk = false;
bool pressed_EjectDisk = false;
bool pressed_DiskStatus = false;
bool pressed_HardReset = false;
bool pressed_SoftReset = false;
bool pressed_Play = false;
bool pressed_Stop = false;
bool pressed_RTZ = false;
bool pressed_NextTape = false;
bool pressed_PrevTape = false;
bool pressed_NextBlock = false;
bool pressed_PrevBlock = false;
bool pressed_TapeStatus = false;
bool pressed_NextDisk = false;
bool pressed_PrevDisk = false;
bool pressed_EjectDisk = false;
bool pressed_DiskStatus = false;
bool pressed_HardReset = false;
bool pressed_SoftReset = false;
/// <summary>
/// Cycles through all the input callbacks
/// This should be done once per frame
/// </summary>
public void PollInput()
{
CPC.InputCallbacks.Call();
/// <summary>
/// Cycles through all the input callbacks
/// This should be done once per frame
/// </summary>
public void PollInput()
{
CPC.InputCallbacks.Call();
lock (this)
{
// parse single keyboard matrix keys.
// J1 and J2 are scanned as part of the keyboard
for (var i = 0; i < KeyboardDevice.KeyboardMatrix.Length; i++)
{
string key = KeyboardDevice.KeyboardMatrix[i];
bool prevState = KeyboardDevice.GetKeyStatus(key);
bool currState = CPC._controller.IsPressed(key);
lock (this)
{
// parse single keyboard matrix keys.
// J1 and J2 are scanned as part of the keyboard
for (var i = 0; i < KeyboardDevice.KeyboardMatrix.Length; i++)
{
string key = KeyboardDevice.KeyboardMatrix[i];
bool prevState = KeyboardDevice.GetKeyStatus(key);
bool currState = CPC._controller.IsPressed(key);
if (currState != prevState)
KeyboardDevice.SetKeyStatus(key, currState);
}
if (currState != prevState)
KeyboardDevice.SetKeyStatus(key, currState);
}
// non matrix keys (J2)
foreach (string k in KeyboardDevice.NonMatrixKeys)
{
if (!k.StartsWith("P2"))
continue;
// non matrix keys (J2)
foreach (string k in KeyboardDevice.NonMatrixKeys)
{
if (!k.StartsWith("P2"))
continue;
bool currState = CPC._controller.IsPressed(k);
bool currState = CPC._controller.IsPressed(k);
switch (k)
{
case "P2 Up":
if (currState)
KeyboardDevice.SetKeyStatus("Key 6", true);
else if (!KeyboardDevice.GetKeyStatus("Key 6"))
KeyboardDevice.SetKeyStatus("Key 6", false);
break;
case "P2 Down":
if (currState)
KeyboardDevice.SetKeyStatus("Key 5", true);
else if (!KeyboardDevice.GetKeyStatus("Key 5"))
KeyboardDevice.SetKeyStatus("Key 5", false);
break;
case "P2 Left":
if (currState)
KeyboardDevice.SetKeyStatus("Key R", true);
else if (!KeyboardDevice.GetKeyStatus("Key R"))
KeyboardDevice.SetKeyStatus("Key R", false);
break;
case "P2 Right":
if (currState)
KeyboardDevice.SetKeyStatus("Key T", true);
else if (!KeyboardDevice.GetKeyStatus("Key T"))
KeyboardDevice.SetKeyStatus("Key T", false);
break;
case "P2 Fire":
if (currState)
KeyboardDevice.SetKeyStatus("Key G", true);
else if (!KeyboardDevice.GetKeyStatus("Key G"))
KeyboardDevice.SetKeyStatus("Key G", false);
break;
}
}
}
switch (k)
{
case "P2 Up":
if (currState)
KeyboardDevice.SetKeyStatus("Key 6", true);
else if (!KeyboardDevice.GetKeyStatus("Key 6"))
KeyboardDevice.SetKeyStatus("Key 6", false);
break;
case "P2 Down":
if (currState)
KeyboardDevice.SetKeyStatus("Key 5", true);
else if (!KeyboardDevice.GetKeyStatus("Key 5"))
KeyboardDevice.SetKeyStatus("Key 5", false);
break;
case "P2 Left":
if (currState)
KeyboardDevice.SetKeyStatus("Key R", true);
else if (!KeyboardDevice.GetKeyStatus("Key R"))
KeyboardDevice.SetKeyStatus("Key R", false);
break;
case "P2 Right":
if (currState)
KeyboardDevice.SetKeyStatus("Key T", true);
else if (!KeyboardDevice.GetKeyStatus("Key T"))
KeyboardDevice.SetKeyStatus("Key T", false);
break;
case "P2 Fire":
if (currState)
KeyboardDevice.SetKeyStatus("Key G", true);
else if (!KeyboardDevice.GetKeyStatus("Key G"))
KeyboardDevice.SetKeyStatus("Key G", false);
break;
}
}
}
// Tape control
if (CPC._controller.IsPressed(Play))
{
if (!pressed_Play)
{
CPC.OSD_FireInputMessage(Play);
TapeDevice.Play();
pressed_Play = true;
}
}
else
pressed_Play = false;
// Tape control
if (CPC._controller.IsPressed(Play))
{
if (!pressed_Play)
{
CPC.OSD_FireInputMessage(Play);
TapeDevice.Play();
pressed_Play = true;
}
}
else
pressed_Play = false;
if (CPC._controller.IsPressed(Stop))
{
if (!pressed_Stop)
{
CPC.OSD_FireInputMessage(Stop);
TapeDevice.Stop();
pressed_Stop = true;
}
}
else
pressed_Stop = false;
if (CPC._controller.IsPressed(Stop))
{
if (!pressed_Stop)
{
CPC.OSD_FireInputMessage(Stop);
TapeDevice.Stop();
pressed_Stop = true;
}
}
else
pressed_Stop = false;
if (CPC._controller.IsPressed(RTZ))
{
if (!pressed_RTZ)
{
CPC.OSD_FireInputMessage(RTZ);
TapeDevice.RTZ();
pressed_RTZ = true;
}
}
else
pressed_RTZ = false;
if (CPC._controller.IsPressed(RTZ))
{
if (!pressed_RTZ)
{
CPC.OSD_FireInputMessage(RTZ);
TapeDevice.RTZ();
pressed_RTZ = true;
}
}
else
pressed_RTZ = false;
if (CPC._controller.IsPressed(Record))
{
if (CPC._controller.IsPressed(Record))
{
}
if (CPC._controller.IsPressed(NextTape))
{
if (!pressed_NextTape)
{
CPC.OSD_FireInputMessage(NextTape);
TapeMediaIndex++;
pressed_NextTape = true;
}
}
else
pressed_NextTape = false;
}
if (CPC._controller.IsPressed(NextTape))
{
if (!pressed_NextTape)
{
CPC.OSD_FireInputMessage(NextTape);
TapeMediaIndex++;
pressed_NextTape = true;
}
}
else
pressed_NextTape = false;
if (CPC._controller.IsPressed(PrevTape))
{
if (!pressed_PrevTape)
{
CPC.OSD_FireInputMessage(PrevTape);
TapeMediaIndex--;
pressed_PrevTape = true;
}
}
else
pressed_PrevTape = false;
if (CPC._controller.IsPressed(PrevTape))
{
if (!pressed_PrevTape)
{
CPC.OSD_FireInputMessage(PrevTape);
TapeMediaIndex--;
pressed_PrevTape = true;
}
}
else
pressed_PrevTape = false;
if (CPC._controller.IsPressed(NextBlock))
{
if (!pressed_NextBlock)
{
CPC.OSD_FireInputMessage(NextBlock);
TapeDevice.SkipBlock(true);
pressed_NextBlock = true;
}
}
else
pressed_NextBlock = false;
if (CPC._controller.IsPressed(NextBlock))
{
if (!pressed_NextBlock)
{
CPC.OSD_FireInputMessage(NextBlock);
TapeDevice.SkipBlock(true);
pressed_NextBlock = true;
}
}
else
pressed_NextBlock = false;
if (CPC._controller.IsPressed(PrevBlock))
{
if (!pressed_PrevBlock)
{
CPC.OSD_FireInputMessage(PrevBlock);
TapeDevice.SkipBlock(false);
pressed_PrevBlock = true;
}
}
else
pressed_PrevBlock = false;
if (CPC._controller.IsPressed(PrevBlock))
{
if (!pressed_PrevBlock)
{
CPC.OSD_FireInputMessage(PrevBlock);
TapeDevice.SkipBlock(false);
pressed_PrevBlock = true;
}
}
else
pressed_PrevBlock = false;
if (CPC._controller.IsPressed(TapeStatus))
{
if (!pressed_TapeStatus)
{
//Spectrum.OSD_FireInputMessage(TapeStatus);
CPC.OSD_ShowTapeStatus();
pressed_TapeStatus = true;
}
}
else
pressed_TapeStatus = false;
if (CPC._controller.IsPressed(TapeStatus))
{
if (!pressed_TapeStatus)
{
//Spectrum.OSD_FireInputMessage(TapeStatus);
CPC.OSD_ShowTapeStatus();
pressed_TapeStatus = true;
}
}
else
pressed_TapeStatus = false;
if (CPC._controller.IsPressed(HardResetStr))
{
if (!pressed_HardReset)
{
HardReset();
pressed_HardReset = true;
}
}
else
pressed_HardReset = false;
if (CPC._controller.IsPressed(HardResetStr))
{
if (!pressed_HardReset)
{
HardReset();
pressed_HardReset = true;
}
}
else
pressed_HardReset = false;
if (CPC._controller.IsPressed(SoftResetStr))
{
if (!pressed_SoftReset)
{
SoftReset();
pressed_SoftReset = true;
}
}
else
pressed_SoftReset = false;
if (CPC._controller.IsPressed(SoftResetStr))
{
if (!pressed_SoftReset)
{
SoftReset();
pressed_SoftReset = true;
}
}
else
pressed_SoftReset = false;
// disk control
if (CPC._controller.IsPressed(NextDisk))
{
if (!pressed_NextDisk)
{
CPC.OSD_FireInputMessage(NextDisk);
DiskMediaIndex++;
pressed_NextDisk = true;
}
}
else
pressed_NextDisk = false;
// disk control
if (CPC._controller.IsPressed(NextDisk))
{
if (!pressed_NextDisk)
{
CPC.OSD_FireInputMessage(NextDisk);
DiskMediaIndex++;
pressed_NextDisk = true;
}
}
else
pressed_NextDisk = false;
if (CPC._controller.IsPressed(PrevDisk))
{
if (!pressed_PrevDisk)
{
CPC.OSD_FireInputMessage(PrevDisk);
DiskMediaIndex--;
pressed_PrevDisk = true;
}
}
else
pressed_PrevDisk = false;
if (CPC._controller.IsPressed(PrevDisk))
{
if (!pressed_PrevDisk)
{
CPC.OSD_FireInputMessage(PrevDisk);
DiskMediaIndex--;
pressed_PrevDisk = true;
}
}
else
pressed_PrevDisk = false;
if (CPC._controller.IsPressed(EjectDisk))
{
if (!pressed_EjectDisk)
{
CPC.OSD_FireInputMessage(EjectDisk);
//if (UPDDiskDevice != null)
// UPDDiskDevice.FDD_EjectDisk();
}
}
else
pressed_EjectDisk = false;
if (CPC._controller.IsPressed(EjectDisk))
{
if (!pressed_EjectDisk)
{
CPC.OSD_FireInputMessage(EjectDisk);
//if (UPDDiskDevice != null)
// UPDDiskDevice.FDD_EjectDisk();
}
}
else
pressed_EjectDisk = false;
if (CPC._controller.IsPressed(DiskStatus))
{
if (!pressed_DiskStatus)
{
//Spectrum.OSD_FireInputMessage(TapeStatus);
CPC.OSD_ShowDiskStatus();
pressed_DiskStatus = true;
}
}
else
pressed_DiskStatus = false;
}
if (CPC._controller.IsPressed(DiskStatus))
{
if (!pressed_DiskStatus)
{
//Spectrum.OSD_FireInputMessage(TapeStatus);
CPC.OSD_ShowDiskStatus();
pressed_DiskStatus = true;
}
}
else
pressed_DiskStatus = false;
}
/// <summary>
/// Signs whether input read has been requested
/// This forms part of the IEmulator LagFrame implementation
/// </summary>
private bool inputRead;
public bool InputRead
{
get { return inputRead; }
set { inputRead = value; }
}
}
/// <summary>
/// Signs whether input read has been requested
/// This forms part of the IEmulator LagFrame implementation
/// </summary>
private bool inputRead;
public bool InputRead
{
get { return inputRead; }
set { inputRead = value; }
}
}
}

View File

@ -5,262 +5,262 @@ using System.Text;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Imported media *
/// </summary>
public abstract partial class CPCBase
{
/// <summary>
/// The tape or disk image(s) that are passed in from the main ZXSpectrum class
/// </summary>
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Imported media *
/// </summary>
public abstract partial class CPCBase
{
/// <summary>
/// The tape or disk image(s) that are passed in from the main ZXSpectrum class
/// </summary>
protected List<byte[]> mediaImages { get; set; }
/// <summary>
/// Tape images
/// </summary>
public List<byte[]> tapeImages { get; set; }
/// <summary>
/// Tape images
/// </summary>
public List<byte[]> tapeImages { get; set; }
/// <summary>
/// Disk images
/// </summary>
public List<byte[]> diskImages { get; set; }
/// <summary>
/// Disk images
/// </summary>
public List<byte[]> diskImages { get; set; }
/// <summary>
/// The index of the currently 'loaded' tape image
/// </summary>
protected int tapeMediaIndex;
public int TapeMediaIndex
{
get { return tapeMediaIndex; }
set
{
int tmp = value;
int result = value;
/// <summary>
/// The index of the currently 'loaded' tape image
/// </summary>
protected int tapeMediaIndex;
public int TapeMediaIndex
{
get { return tapeMediaIndex; }
set
{
int tmp = value;
int result = value;
if (tapeImages == null || tapeImages.Count() == 0)
{
// no tape images found
return;
}
if (tapeImages == null || tapeImages.Count() == 0)
{
// no tape images found
return;
}
if (value >= tapeImages.Count())
{
// media at this index does not exist - loop back to 0
result = 0;
}
else if (value < 0)
{
// negative index not allowed - move to last item in the collection
result = tapeImages.Count() - 1;
}
if (value >= tapeImages.Count())
{
// media at this index does not exist - loop back to 0
result = 0;
}
else if (value < 0)
{
// negative index not allowed - move to last item in the collection
result = tapeImages.Count() - 1;
}
// load the media into the tape device
tapeMediaIndex = result;
// fire osd message
//Spectrum.OSD_TapeInserted();
LoadTapeMedia();
}
}
// load the media into the tape device
tapeMediaIndex = result;
// fire osd message
//Spectrum.OSD_TapeInserted();
LoadTapeMedia();
}
}
/// <summary>
/// The index of the currently 'loaded' disk image
/// </summary>
protected int diskMediaIndex;
public int DiskMediaIndex
{
get { return diskMediaIndex; }
set
{
int tmp = value;
int result = value;
/// <summary>
/// The index of the currently 'loaded' disk image
/// </summary>
protected int diskMediaIndex;
public int DiskMediaIndex
{
get { return diskMediaIndex; }
set
{
int tmp = value;
int result = value;
if (diskImages == null || diskImages.Count() == 0)
{
// no tape images found
return;
}
if (diskImages == null || diskImages.Count() == 0)
{
// no tape images found
return;
}
if (value >= diskImages.Count())
{
// media at this index does not exist - loop back to 0
result = 0;
}
else if (value < 0)
{
// negative index not allowed - move to last item in the collection
result = diskImages.Count() - 1;
}
if (value >= diskImages.Count())
{
// media at this index does not exist - loop back to 0
result = 0;
}
else if (value < 0)
{
// negative index not allowed - move to last item in the collection
result = diskImages.Count() - 1;
}
// load the media into the disk device
diskMediaIndex = result;
// load the media into the disk device
diskMediaIndex = result;
// fire osd message
CPC.OSD_DiskInserted();
// fire osd message
CPC.OSD_DiskInserted();
LoadDiskMedia();
}
}
LoadDiskMedia();
}
}
/// <summary>
/// Called on first instantiation (and subsequent core reboots)
/// </summary>
protected void InitializeMedia(List<byte[]> files)
{
mediaImages = files;
LoadAllMedia();
}
/// <summary>
/// Called on first instantiation (and subsequent core reboots)
/// </summary>
protected void InitializeMedia(List<byte[]> files)
{
mediaImages = files;
LoadAllMedia();
}
/// <summary>
/// Attempts to load all media into the relevant structures
/// </summary>
protected void LoadAllMedia()
{
tapeImages = new List<byte[]>();
diskImages = new List<byte[]>();
/// <summary>
/// Attempts to load all media into the relevant structures
/// </summary>
protected void LoadAllMedia()
{
tapeImages = new List<byte[]>();
diskImages = new List<byte[]>();
int cnt = 0;
foreach (var m in mediaImages)
{
switch (IdentifyMedia(m))
{
case CPCMediaType.Tape:
tapeImages.Add(m);
CPC._tapeInfo.Add(CPC._gameInfo[cnt]);
break;
case CPCMediaType.Disk:
diskImages.Add(m);
CPC._diskInfo.Add(CPC._gameInfo[cnt]);
break;
case CPCMediaType.DiskDoubleSided:
// this is a bit tricky. we will attempt to parse the double sided disk image byte array,
// then output two separate image byte arrays
List<byte[]> working = new List<byte[]>();
foreach (DiskType type in Enum.GetValues(typeof(DiskType)))
{
bool found = false;
int cnt = 0;
foreach (var m in mediaImages)
{
switch (IdentifyMedia(m))
{
case CPCMediaType.Tape:
tapeImages.Add(m);
CPC._tapeInfo.Add(CPC._gameInfo[cnt]);
break;
case CPCMediaType.Disk:
diskImages.Add(m);
CPC._diskInfo.Add(CPC._gameInfo[cnt]);
break;
case CPCMediaType.DiskDoubleSided:
// this is a bit tricky. we will attempt to parse the double sided disk image byte array,
// then output two separate image byte arrays
List<byte[]> working = new List<byte[]>();
foreach (DiskType type in Enum.GetValues(typeof(DiskType)))
{
bool found = false;
switch (type)
{
case DiskType.CPCExtended:
found = CPCExtendedFloppyDisk.SplitDoubleSided(m, working);
break;
case DiskType.CPC:
found = CPCFloppyDisk.SplitDoubleSided(m, working);
break;
}
switch (type)
{
case DiskType.CPCExtended:
found = CPCExtendedFloppyDisk.SplitDoubleSided(m, working);
break;
case DiskType.CPC:
found = CPCFloppyDisk.SplitDoubleSided(m, working);
break;
}
if (found)
{
// add side 1
diskImages.Add(working[0]);
// add side 2
diskImages.Add(working[1]);
if (found)
{
// add side 1
diskImages.Add(working[0]);
// add side 2
diskImages.Add(working[1]);
Common.GameInfo one = new Common.GameInfo();
Common.GameInfo two = new Common.GameInfo();
var gi = CPC._gameInfo[cnt];
for (int i = 0; i < 2; i++)
{
Common.GameInfo work = new Common.GameInfo();
if (i == 0)
{
work = one;
}
else if (i == 1)
{
work = two;
}
Common.GameInfo one = new Common.GameInfo();
Common.GameInfo two = new Common.GameInfo();
var gi = CPC._gameInfo[cnt];
for (int i = 0; i < 2; i++)
{
Common.GameInfo work = new Common.GameInfo();
if (i == 0)
{
work = one;
}
else if (i == 1)
{
work = two;
}
work.FirmwareHash = gi.FirmwareHash;
work.Hash = gi.Hash;
work.Name = gi.Name + " (Parsed Side " + (i + 1) + ")";
work.Region = gi.Region;
work.NotInDatabase = gi.NotInDatabase;
work.Status = gi.Status;
work.System = gi.System;
work.FirmwareHash = gi.FirmwareHash;
work.Hash = gi.Hash;
work.Name = gi.Name + " (Parsed Side " + (i + 1) + ")";
work.Region = gi.Region;
work.NotInDatabase = gi.NotInDatabase;
work.Status = gi.Status;
work.System = gi.System;
CPC._diskInfo.Add(work);
}
}
else
{
CPC._diskInfo.Add(work);
}
}
else
{
}
}
break;
}
}
}
break;
}
cnt++;
}
cnt++;
}
if (tapeImages.Count > 0)
LoadTapeMedia();
if (tapeImages.Count > 0)
LoadTapeMedia();
if (diskImages.Count > 0)
LoadDiskMedia();
}
if (diskImages.Count > 0)
LoadDiskMedia();
}
/// <summary>
/// Attempts to load a tape into the tape device based on tapeMediaIndex
/// </summary>
protected void LoadTapeMedia()
{
TapeDevice.LoadTape(tapeImages[tapeMediaIndex]);
}
/// <summary>
/// Attempts to load a tape into the tape device based on tapeMediaIndex
/// </summary>
protected void LoadTapeMedia()
{
TapeDevice.LoadTape(tapeImages[tapeMediaIndex]);
}
/// <summary>
/// Attempts to load a disk into the disk device based on diskMediaIndex
/// </summary>
protected void LoadDiskMedia()
{
if (this.GetType() == typeof(CPC464))
{
CPC.CoreComm.ShowMessage("You are trying to load one of more disk images.\n\n Please select something other than CPC 464 emulation immediately and reboot the core");
return;
}
/// <summary>
/// Attempts to load a disk into the disk device based on diskMediaIndex
/// </summary>
protected void LoadDiskMedia()
{
if (this.GetType() == typeof(CPC464))
{
CPC.CoreComm.ShowMessage("You are trying to load one of more disk images.\n\n Please select something other than CPC 464 emulation immediately and reboot the core");
return;
}
UPDDiskDevice.FDD_LoadDisk(diskImages[diskMediaIndex]);
}
UPDDiskDevice.FDD_LoadDisk(diskImages[diskMediaIndex]);
}
/// <summary>
/// Identifies and sorts the various media types
/// </summary>
private CPCMediaType IdentifyMedia(byte[] data)
{
// get first 16 bytes as a string
string hdr = Encoding.ASCII.GetString(data.Take(16).ToArray());
/// <summary>
/// Identifies and sorts the various media types
/// </summary>
private CPCMediaType IdentifyMedia(byte[] data)
{
// get first 16 bytes as a string
string hdr = Encoding.ASCII.GetString(data.Take(16).ToArray());
// disk checking first
if (hdr.ToUpper().Contains("EXTENDED CPC DSK") || hdr.ToUpper().Contains("MV - CPC"))
{
// amstrad .dsk disk file
// check for number of sides
var sides = data[0x31];
if (sides == 1)
return CPCMediaType.Disk;
else
return CPCMediaType.DiskDoubleSided;
}
// disk checking first
if (hdr.ToUpper().Contains("EXTENDED CPC DSK") || hdr.ToUpper().Contains("MV - CPC"))
{
// amstrad .dsk disk file
// check for number of sides
var sides = data[0x31];
if (sides == 1)
return CPCMediaType.Disk;
else
return CPCMediaType.DiskDoubleSided;
}
// tape checking
if (hdr.ToUpper().StartsWith("ZXTAPE!"))
{
// cdt tape file
return CPCMediaType.Tape;
}
// tape checking
if (hdr.ToUpper().StartsWith("ZXTAPE!"))
{
// cdt tape file
return CPCMediaType.Tape;
}
// not found
return CPCMediaType.None;
}
}
// not found
return CPCMediaType.None;
}
}
public enum CPCMediaType
{
None,
Tape,
Disk,
DiskDoubleSided
}
public enum CPCMediaType
{
None,
Tape,
Disk,
DiskDoubleSided
}
}

View File

@ -3,144 +3,144 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Memory *
/// </summary>
public abstract partial class CPCBase
{
#region Memory Fields & Properties
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Memory *
/// </summary>
public abstract partial class CPCBase
{
#region Memory Fields & Properties
/* ROM Banks */
/// <summary>
/// Lower: OS ROM
/// </summary>
public byte[] ROMLower = new byte[0x4000];
/// <summary>
/// Upper: POS 0 (usually BASIC)
/// </summary>
public byte[] ROM0 = new byte[0x4000];
/// <summary>
/// Upper: POS 7 (usually AMSDOS)
/// </summary>
public byte[] ROM7 = new byte[0x4000];
/* ROM Banks */
/// <summary>
/// Lower: OS ROM
/// </summary>
public byte[] ROMLower = new byte[0x4000];
/// <summary>
/// Upper: POS 0 (usually BASIC)
/// </summary>
public byte[] ROM0 = new byte[0x4000];
/// <summary>
/// Upper: POS 7 (usually AMSDOS)
/// </summary>
public byte[] ROM7 = new byte[0x4000];
/* RAM Banks - Lower 64K */
public byte[] RAM0 = new byte[0x4000];
public byte[] RAM1 = new byte[0x4000];
public byte[] RAM2 = new byte[0x4000];
public byte[] RAM3 = new byte[0x4000];
/* RAM Banks - Lower 64K */
public byte[] RAM0 = new byte[0x4000];
public byte[] RAM1 = new byte[0x4000];
public byte[] RAM2 = new byte[0x4000];
public byte[] RAM3 = new byte[0x4000];
/* RAM Banks - Upper 64K */
public byte[] RAM4 = new byte[0x4000];
public byte[] RAM5 = new byte[0x4000];
public byte[] RAM6 = new byte[0x4000];
public byte[] RAM7 = new byte[0x4000];
/* RAM Banks - Upper 64K */
public byte[] RAM4 = new byte[0x4000];
public byte[] RAM5 = new byte[0x4000];
public byte[] RAM6 = new byte[0x4000];
public byte[] RAM7 = new byte[0x4000];
/// <summary>
/// Signs whether Upper ROM is paged in
/// </summary>
public bool UpperROMPaged;
/// <summary>
/// Signs whether Upper ROM is paged in
/// </summary>
public bool UpperROMPaged;
/// <summary>
/// The position of the currently paged upper ROM
/// </summary>
public int UpperROMPosition;
/// <summary>
/// The position of the currently paged upper ROM
/// </summary>
public int UpperROMPosition;
/// <summary>
/// Signs whether Lower ROM is paged in
/// </summary>
public bool LowerROMPaged;
/// <summary>
/// Signs whether Lower ROM is paged in
/// </summary>
public bool LowerROMPaged;
/// <summary>
/// The currently selected RAM config
/// </summary>
public int RAMConfig;
/// <summary>
/// The currently selected RAM config
/// </summary>
public int RAMConfig;
/// <summary>
/// Always 0 on a CPC6128
/// On a machine with more than 128K RAM (standard memory expansion) this selects each additional 64K above the first upper 64K
/// </summary>
public int RAM64KBank;
/// <summary>
/// Always 0 on a CPC6128
/// On a machine with more than 128K RAM (standard memory expansion) this selects each additional 64K above the first upper 64K
/// </summary>
public int RAM64KBank;
#endregion
#endregion
#region Memory Related Methods
#region Memory Related Methods
/// <summary>
/// Simulates reading from the bus
/// Paging should be handled here
/// </summary>
public abstract byte ReadBus(ushort addr);
/// <summary>
/// Simulates reading from the bus
/// Paging should be handled here
/// </summary>
public abstract byte ReadBus(ushort addr);
/// <summary>
/// Pushes a value onto the data bus that should be valid as long as the interrupt is true
/// </summary>
public virtual byte PushBus()
{
return 0xFF;
}
/// <summary>
/// Pushes a value onto the data bus that should be valid as long as the interrupt is true
/// </summary>
public virtual byte PushBus()
{
return 0xFF;
}
/// <summary>
/// Simulates writing to the bus
/// Paging should be handled here
/// </summary>
public abstract void WriteBus(ushort addr, byte value);
/// <summary>
/// Simulates writing to the bus
/// Paging should be handled here
/// </summary>
public abstract void WriteBus(ushort addr, byte value);
/// <summary>
/// Reads a byte of data from a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public abstract byte ReadMemory(ushort addr);
/// <summary>
/// Reads a byte of data from a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public abstract byte ReadMemory(ushort addr);
/// <summary>
/// Writes a byte of data to a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public abstract void WriteMemory(ushort addr, byte value);
/// <summary>
/// Writes a byte of data to a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public abstract void WriteMemory(ushort addr, byte value);
/// <summary>
/// Sets up the ROM
/// </summary>
public abstract void InitROM(RomData[] romData);
/// <summary>
/// Sets up the ROM
/// </summary>
public abstract void InitROM(RomData[] romData);
/// <summary>
/// ULA reads the memory at the specified address
/// (No memory contention)
/// </summary>
public virtual byte FetchScreenMemory(ushort addr)
{
int divisor = addr / 0x4000;
byte result = 0xff;
/// <summary>
/// ULA reads the memory at the specified address
/// (No memory contention)
/// </summary>
public virtual byte FetchScreenMemory(ushort addr)
{
int divisor = addr / 0x4000;
byte result = 0xff;
switch (divisor)
{
// 0x000
case 0:
result = RAM0[addr % 0x4000];
break;
switch (divisor)
{
// 0x000
case 0:
result = RAM0[addr % 0x4000];
break;
// 0x4000
case 1:
result = RAM1[addr % 0x4000];
break;
// 0x4000
case 1:
result = RAM1[addr % 0x4000];
break;
// 0x8000
case 2:
result = RAM2[addr % 0x4000];
break;
// 0x8000
case 2:
result = RAM2[addr % 0x4000];
break;
// 0xc000 or UpperROM
case 3:
result = RAM3[addr % 0x4000];
break;
default:
break;
}
// 0xc000 or UpperROM
case 3:
result = RAM3[addr % 0x4000];
break;
default:
break;
}
return result;
}
return result;
}
#endregion
}
#endregion
}
}

View File

@ -5,103 +5,103 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Port Access *
/// </summary>
public abstract partial class CPCBase
{
/// <summary>
/// Reads a byte of data from a specified port address
/// </summary>
public abstract byte ReadPort(ushort port);
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Port Access *
/// </summary>
public abstract partial class CPCBase
{
/// <summary>
/// Reads a byte of data from a specified port address
/// </summary>
public abstract byte ReadPort(ushort port);
/// <summary>
/// Writes a byte of data to a specified port address
/// </summary>
public abstract void WritePort(ushort port, byte value);
/// <summary>
/// Writes a byte of data to a specified port address
/// </summary>
public abstract void WritePort(ushort port, byte value);
/// <summary>
/// Returns a single port device enum based on the port address
/// (for IN operations)
/// https://web.archive.org/web/20090808085929/http://www.cepece.info/amstrad/docs/iopord.html
/// http://www.cpcwiki.eu/index.php/I/O_Port_Summary
/// </summary>
protected virtual PortDevice DecodeINPort(ushort port)
{
PortDevice dev = PortDevice.Unknown;
/// <summary>
/// Returns a single port device enum based on the port address
/// (for IN operations)
/// https://web.archive.org/web/20090808085929/http://www.cepece.info/amstrad/docs/iopord.html
/// http://www.cpcwiki.eu/index.php/I/O_Port_Summary
/// </summary>
protected virtual PortDevice DecodeINPort(ushort port)
{
PortDevice dev = PortDevice.Unknown;
if (!port.Bit(15) && port.Bit(14))
dev = PortDevice.GateArray;
if (!port.Bit(15) && port.Bit(14))
dev = PortDevice.GateArray;
else if (!port.Bit(15))
dev = PortDevice.RAMManagement;
else if (!port.Bit(15))
dev = PortDevice.RAMManagement;
else if (!port.Bit(14))
dev = PortDevice.CRCT;
else if (!port.Bit(14))
dev = PortDevice.CRCT;
else if (!port.Bit(13))
dev = PortDevice.ROMSelect;
else if (!port.Bit(13))
dev = PortDevice.ROMSelect;
else if (!port.Bit(12))
dev = PortDevice.Printer;
else if (!port.Bit(12))
dev = PortDevice.Printer;
else if (!port.Bit(11))
dev = PortDevice.PPI;
else if (!port.Bit(11))
dev = PortDevice.PPI;
else if (!port.Bit(10))
dev = PortDevice.Expansion;
else if (!port.Bit(10))
dev = PortDevice.Expansion;
return dev;
}
return dev;
}
/// <summary>
/// Returns a list of port device enums based on the port address
/// (for OUT operations)
/// https://web.archive.org/web/20090808085929/http://www.cepece.info/amstrad/docs/iopord.html
/// http://www.cpcwiki.eu/index.php/I/O_Port_Summary
/// </summary>
protected virtual List<PortDevice> DecodeOUTPort(ushort port)
{
List<PortDevice> devs = new List<PortDevice>();
/// <summary>
/// Returns a list of port device enums based on the port address
/// (for OUT operations)
/// https://web.archive.org/web/20090808085929/http://www.cepece.info/amstrad/docs/iopord.html
/// http://www.cpcwiki.eu/index.php/I/O_Port_Summary
/// </summary>
protected virtual List<PortDevice> DecodeOUTPort(ushort port)
{
List<PortDevice> devs = new List<PortDevice>();
if (!port.Bit(15) && port.Bit(14))
devs.Add(PortDevice.GateArray);
if (!port.Bit(15) && port.Bit(14))
devs.Add(PortDevice.GateArray);
if (!port.Bit(15))
devs.Add(PortDevice.RAMManagement);
if (!port.Bit(15))
devs.Add(PortDevice.RAMManagement);
if (!port.Bit(14))
devs.Add(PortDevice.CRCT);
if (!port.Bit(14))
devs.Add(PortDevice.CRCT);
if (!port.Bit(13))
devs.Add(PortDevice.ROMSelect);
if (!port.Bit(13))
devs.Add(PortDevice.ROMSelect);
if (!port.Bit(12))
devs.Add(PortDevice.Printer);
if (!port.Bit(12))
devs.Add(PortDevice.Printer);
if (!port.Bit(11))
devs.Add(PortDevice.PPI);
if (!port.Bit(11))
devs.Add(PortDevice.PPI);
if (!port.Bit(10))
devs.Add(PortDevice.Expansion);
if (!port.Bit(10))
devs.Add(PortDevice.Expansion);
return devs;
}
return devs;
}
/// <summary>
/// Potential port devices
/// </summary>
public enum PortDevice
{
Unknown,
GateArray,
RAMManagement,
CRCT,
ROMSelect,
Printer,
PPI,
Expansion
}
}
/// <summary>
/// Potential port devices
/// </summary>
public enum PortDevice
{
Unknown,
GateArray,
RAMManagement,
CRCT,
ROMSelect,
Printer,
PPI,
Expansion
}
}
}

View File

@ -3,213 +3,213 @@ using BizHawk.Emulation.Cores.Components.Z80A;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Main properties / fields / contruction*
/// </summary>
public abstract partial class CPCBase
{
#region Devices
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Main properties / fields / contruction*
/// </summary>
public abstract partial class CPCBase
{
#region Devices
/// <summary>
/// The calling ZXSpectrum class (piped in via constructor)
/// </summary>
public AmstradCPC CPC { get; set; }
/// <summary>
/// The calling ZXSpectrum class (piped in via constructor)
/// </summary>
public AmstradCPC CPC { get; set; }
/// <summary>
/// Reference to the instantiated Z80 cpu (piped in via constructor)
/// </summary>
public Z80A CPU { get; set; }
/// <summary>
/// Reference to the instantiated Z80 cpu (piped in via constructor)
/// </summary>
public Z80A CPU { get; set; }
/// <summary>
/// ROM and extended info
/// </summary>
public RomData RomData { get; set; }
/// <summary>
/// ROM and extended info
/// </summary>
public RomData RomData { get; set; }
/// <summary>
/// The Amstrad datacorder device
/// </summary>
public virtual DatacorderDevice TapeDevice { get; set; }
/// <summary>
/// The Amstrad datacorder device
/// </summary>
public virtual DatacorderDevice TapeDevice { get; set; }
/// <summary>
/// beeper output for the tape
/// </summary>
public IBeeperDevice TapeBuzzer { get; set; }
/// <summary>
/// beeper output for the tape
/// </summary>
public IBeeperDevice TapeBuzzer { get; set; }
/// <summary>
/// Device representing the AY-3-8912 chip found in the CPC
/// </summary>
public IPSG AYDevice { get; set; }
/// <summary>
/// Device representing the AY-3-8912 chip found in the CPC
/// </summary>
public IPSG AYDevice { get; set; }
/// <summary>
/// The keyboard device
/// Technically, this is controlled by the PSG, but has been abstracted due to the port over from ZXHawk
/// </summary>
public IKeyboard KeyboardDevice { get; set; }
/// <summary>
/// The keyboard device
/// Technically, this is controlled by the PSG, but has been abstracted due to the port over from ZXHawk
/// </summary>
public IKeyboard KeyboardDevice { get; set; }
/// <summary>
/// The Amstrad disk drive
/// </summary>
public virtual NECUPD765 UPDDiskDevice { get; set; }
/// <summary>
/// The Amstrad disk drive
/// </summary>
public virtual NECUPD765 UPDDiskDevice { get; set; }
/// <summary>
/// The Cathode Ray Tube Controller chip
/// </summary>
public CRCT_6845 CRCT { get; set; }
/// <summary>
/// The Cathode Ray Tube Controller chip
/// </summary>
public CRCT_6845 CRCT { get; set; }
/// <summary>
/// The Amstrad gate array
/// </summary>
public AmstradGateArray GateArray { get; set; }
/// <summary>
/// The Amstrad gate array
/// </summary>
public AmstradGateArray GateArray { get; set; }
// /// <summary>
// /// Renders pixels to the screen
// /// </summary>
// public CRTDevice CRT { get; set; }
// /// <summary>
// /// Renders pixels to the screen
// /// </summary>
// public CRTDevice CRT { get; set; }
/// <summary>
/// The PPI contoller chip
/// </summary>
public PPI_8255 PPI { get; set; }
/// <summary>
/// The PPI contoller chip
/// </summary>
public PPI_8255 PPI { get; set; }
/// <summary>
/// The length of a standard frame in CPU cycles
/// </summary>
public int FrameLength;
/// <summary>
/// The length of a standard frame in CPU cycles
/// </summary>
public int FrameLength;
#endregion
#endregion
#region Emulator State
#region Emulator State
/// <summary>
/// Signs whether the frame has ended
/// </summary>
public bool FrameCompleted;
/// <summary>
/// Signs whether the frame has ended
/// </summary>
public bool FrameCompleted;
/// <summary>
/// Overflow from the previous frame (in Z80 cycles)
/// </summary>
public int OverFlow;
/// <summary>
/// Overflow from the previous frame (in Z80 cycles)
/// </summary>
public int OverFlow;
/// <summary>
/// The total number of frames rendered
/// </summary>
public int FrameCount;
/// <summary>
/// The total number of frames rendered
/// </summary>
public int FrameCount;
/// <summary>
/// The current cycle (T-State) that we are at in the frame
/// </summary>
public long _frameCycles;
/// <summary>
/// The current cycle (T-State) that we are at in the frame
/// </summary>
public long _frameCycles;
/// <summary>
/// Stores where we are in the frame after each CPU cycle
/// </summary>
public long LastFrameStartCPUTick;
/// <summary>
/// Stores where we are in the frame after each CPU cycle
/// </summary>
public long LastFrameStartCPUTick;
/// <summary>
/// Gets the current frame cycle according to the CPU tick count
/// </summary>
public virtual long CurrentFrameCycle => GateArray.FrameClock; // CPU.TotalExecutedCycles - LastFrameStartCPUTick;
/// <summary>
/// Gets the current frame cycle according to the CPU tick count
/// </summary>
public virtual long CurrentFrameCycle => GateArray.FrameClock; // CPU.TotalExecutedCycles - LastFrameStartCPUTick;
/// <summary>
/// Non-Deterministic bools
/// </summary>
public bool _render;
public bool _renderSound;
/// <summary>
/// Non-Deterministic bools
/// </summary>
public bool _render;
public bool _renderSound;
#endregion
#endregion
#region Constants
#region Constants
/// <summary>
/// Mask constants & misc
/// </summary>
protected const int BORDER_BIT = 0x07;
protected const int EAR_BIT = 0x10;
protected const int MIC_BIT = 0x08;
protected const int TAPE_BIT = 0x40;
protected const int AY_SAMPLE_RATE = 16;
/// <summary>
/// Mask constants & misc
/// </summary>
protected const int BORDER_BIT = 0x07;
protected const int EAR_BIT = 0x10;
protected const int MIC_BIT = 0x08;
protected const int TAPE_BIT = 0x40;
protected const int AY_SAMPLE_RATE = 16;
#endregion
#endregion
#region Emulation Loop
#region Emulation Loop
/// <summary>
/// Executes a single frame
/// </summary>
public virtual void ExecuteFrame(bool render, bool renderSound)
{
GateArray.FrameEnd = false;
CRCT.lineCounter = 0;
/// <summary>
/// Executes a single frame
/// </summary>
public virtual void ExecuteFrame(bool render, bool renderSound)
{
GateArray.FrameEnd = false;
CRCT.lineCounter = 0;
InputRead = false;
_render = render;
_renderSound = renderSound;
InputRead = false;
_render = render;
_renderSound = renderSound;
FrameCompleted = false;
FrameCompleted = false;
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.StartFrame();
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.StartFrame();
if (_renderSound)
{
AYDevice.StartFrame();
}
if (_renderSound)
{
AYDevice.StartFrame();
}
PollInput();
PollInput();
//CRT.SetupVideo();
//CRT.ScanlineCounter = 0;
//CRT.SetupVideo();
//CRT.ScanlineCounter = 0;
while (!GateArray.FrameEnd)
{
GateArray.ClockCycle();
while (!GateArray.FrameEnd)
{
GateArray.ClockCycle();
// cycle the tape device
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.TapeCycle();
}
// we have reached the end of a frame
LastFrameStartCPUTick = CPU.TotalExecutedCycles; // - OverFlow;
// cycle the tape device
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.TapeCycle();
}
// we have reached the end of a frame
LastFrameStartCPUTick = CPU.TotalExecutedCycles; // - OverFlow;
if (AYDevice != null)
AYDevice.EndFrame();
if (AYDevice != null)
AYDevice.EndFrame();
FrameCount++;
FrameCount++;
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.EndFrame();
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.EndFrame();
FrameCompleted = true;
FrameCompleted = true;
// is this a lag frame?
CPC.IsLagFrame = !InputRead;
// is this a lag frame?
CPC.IsLagFrame = !InputRead;
// FDC debug
if (UPDDiskDevice != null && UPDDiskDevice.writeDebug)
{
// only write UPD log every second
if (FrameCount % 10 == 0)
{
System.IO.File.AppendAllLines(UPDDiskDevice.outputfile, UPDDiskDevice.dLog);
UPDDiskDevice.dLog = new System.Collections.Generic.List<string>();
//System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString);
}
}
// FDC debug
if (UPDDiskDevice != null && UPDDiskDevice.writeDebug)
{
// only write UPD log every second
if (FrameCount % 10 == 0)
{
System.IO.File.AppendAllLines(UPDDiskDevice.outputfile, UPDDiskDevice.dLog);
UPDDiskDevice.dLog = new System.Collections.Generic.List<string>();
//System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString);
}
}
GateArray.FrameClock = 0;
}
GateArray.FrameClock = 0;
}
#endregion
#endregion
#region Reset Functions
#region Reset Functions
/// <summary>
/// Hard reset of the emulated machine
/// </summary>
public virtual void HardReset()
{
/*
/// <summary>
/// Hard reset of the emulated machine
/// </summary>
public virtual void HardReset()
{
/*
//ULADevice.ResetInterrupt();
ROMPaged = 0;
SpecialPagingMode = false;
@ -256,14 +256,14 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
}
*/
}
}
/// <summary>
/// Soft reset of the emulated machine
/// </summary>
public virtual void SoftReset()
{
/*
/// <summary>
/// Soft reset of the emulated machine
/// </summary>
public virtual void SoftReset()
{
/*
//ULADevice.ResetInterrupt();
ROMPaged = 0;
SpecialPagingMode = false;
@ -310,65 +310,65 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
}
}
*/
}
}
#endregion
#endregion
#region IStatable
#region IStatable
public void SyncState(Serializer ser)
{
ser.BeginSection("CPCMachine");
ser.Sync(nameof(FrameCompleted), ref FrameCompleted);
ser.Sync(nameof(OverFlow), ref OverFlow);
ser.Sync(nameof(FrameCount), ref FrameCount);
ser.Sync(nameof(_frameCycles), ref _frameCycles);
ser.Sync(nameof(inputRead), ref inputRead);
ser.Sync(nameof(LastFrameStartCPUTick), ref LastFrameStartCPUTick);
ser.Sync(nameof(ROMLower), ref ROMLower, false);
ser.Sync(nameof(ROM0), ref ROM0, false);
ser.Sync(nameof(ROM7), ref ROM7, false);
ser.Sync(nameof(RAM0), ref RAM0, false);
ser.Sync(nameof(RAM1), ref RAM1, false);
ser.Sync(nameof(RAM2), ref RAM2, false);
ser.Sync(nameof(RAM3), ref RAM3, false);
ser.Sync(nameof(RAM4), ref RAM4, false);
ser.Sync(nameof(RAM5), ref RAM5, false);
ser.Sync(nameof(RAM6), ref RAM6, false);
ser.Sync(nameof(RAM7), ref RAM7, false);
public void SyncState(Serializer ser)
{
ser.BeginSection("CPCMachine");
ser.Sync(nameof(FrameCompleted), ref FrameCompleted);
ser.Sync(nameof(OverFlow), ref OverFlow);
ser.Sync(nameof(FrameCount), ref FrameCount);
ser.Sync(nameof(_frameCycles), ref _frameCycles);
ser.Sync(nameof(inputRead), ref inputRead);
ser.Sync(nameof(LastFrameStartCPUTick), ref LastFrameStartCPUTick);
ser.Sync(nameof(ROMLower), ref ROMLower, false);
ser.Sync(nameof(ROM0), ref ROM0, false);
ser.Sync(nameof(ROM7), ref ROM7, false);
ser.Sync(nameof(RAM0), ref RAM0, false);
ser.Sync(nameof(RAM1), ref RAM1, false);
ser.Sync(nameof(RAM2), ref RAM2, false);
ser.Sync(nameof(RAM3), ref RAM3, false);
ser.Sync(nameof(RAM4), ref RAM4, false);
ser.Sync(nameof(RAM5), ref RAM5, false);
ser.Sync(nameof(RAM6), ref RAM6, false);
ser.Sync(nameof(RAM7), ref RAM7, false);
ser.Sync(nameof(UpperROMPosition), ref UpperROMPosition);
ser.Sync(nameof(UpperROMPaged), ref UpperROMPaged);
ser.Sync(nameof(LowerROMPaged), ref LowerROMPaged);
ser.Sync(nameof(RAMConfig), ref RAMConfig);
ser.Sync(nameof(RAM64KBank), ref RAM64KBank);
ser.Sync(nameof(UpperROMPosition), ref UpperROMPosition);
ser.Sync(nameof(UpperROMPaged), ref UpperROMPaged);
ser.Sync(nameof(LowerROMPaged), ref LowerROMPaged);
ser.Sync(nameof(RAMConfig), ref RAMConfig);
ser.Sync(nameof(RAM64KBank), ref RAM64KBank);
CRCT.SyncState(ser);
//CRT.SyncState(ser);
GateArray.SyncState(ser);
KeyboardDevice.SyncState(ser);
TapeBuzzer.SyncState(ser);
AYDevice.SyncState(ser);
CRCT.SyncState(ser);
//CRT.SyncState(ser);
GateArray.SyncState(ser);
KeyboardDevice.SyncState(ser);
TapeBuzzer.SyncState(ser);
AYDevice.SyncState(ser);
ser.Sync(nameof(tapeMediaIndex), ref tapeMediaIndex);
if (ser.IsReader)
TapeMediaIndex = tapeMediaIndex;
ser.Sync(nameof(tapeMediaIndex), ref tapeMediaIndex);
if (ser.IsReader)
TapeMediaIndex = tapeMediaIndex;
TapeDevice.SyncState(ser);
TapeDevice.SyncState(ser);
ser.Sync(nameof(diskMediaIndex), ref diskMediaIndex);
if (ser.IsReader)
DiskMediaIndex = diskMediaIndex;
ser.Sync(nameof(diskMediaIndex), ref diskMediaIndex);
if (ser.IsReader)
DiskMediaIndex = diskMediaIndex;
if (UPDDiskDevice != null)
{
UPDDiskDevice.SyncState(ser);
}
if (UPDDiskDevice != null)
{
UPDDiskDevice.SyncState(ser);
}
ser.EndSection();
}
ser.EndSection();
}
#endregion
#endregion
}
}
}

View File

@ -10,60 +10,60 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Amstrad Gate Array *
/// https://web.archive.org/web/20170612081209/http://www.grimware.org/doku.php/documentations/devices/gatearray
/// </summary>
public abstract class GateArrayBase : IVideoProvider
{
public int Z80ClockSpeed = 4000000;
public int FrameLength = 79872;
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Amstrad Gate Array *
/// https://web.archive.org/web/20170612081209/http://www.grimware.org/doku.php/documentations/devices/gatearray
/// </summary>
public abstract class GateArrayBase : IVideoProvider
{
public int Z80ClockSpeed = 4000000;
public int FrameLength = 79872;
#region Devices
#region Devices
private CPCBase _machine;
private Z80A CPU => _machine.CPU;
private CRCT_6845 CRCT => _machine.CRCT;
private IPSG PSG => _machine.AYDevice;
private CPCBase _machine;
private Z80A CPU => _machine.CPU;
private CRCT_6845 CRCT => _machine.CRCT;
private IPSG PSG => _machine.AYDevice;
#endregion
#endregion
#region Constants
#region Constants
/// <summary>
/// CRTC Register constants
/// </summary>
public const int HOR_TOTAL = 0;
public const int HOR_DISPLAYED = 1;
public const int HOR_SYNC_POS = 2;
public const int HOR_AND_VER_SYNC_WIDTHS = 3;
public const int VER_TOTAL = 4;
public const int VER_TOTAL_ADJUST = 5;
public const int VER_DISPLAYED = 6;
public const int VER_SYNC_POS = 7;
public const int INTERLACE_SKEW = 8;
public const int MAX_RASTER_ADDR = 9;
public const int CUR_START_RASTER = 10;
public const int CUR_END_RASTER = 11;
public const int DISP_START_ADDR_H = 12;
public const int DISP_START_ADDR_L = 13;
public const int CUR_ADDR_H = 14;
public const int CUR_ADDR_L = 15;
public const int LPEN_ADDR_H = 16;
public const int LPEN_ADDR_L = 17;
/// <summary>
/// CRTC Register constants
/// </summary>
public const int HOR_TOTAL = 0;
public const int HOR_DISPLAYED = 1;
public const int HOR_SYNC_POS = 2;
public const int HOR_AND_VER_SYNC_WIDTHS = 3;
public const int VER_TOTAL = 4;
public const int VER_TOTAL_ADJUST = 5;
public const int VER_DISPLAYED = 6;
public const int VER_SYNC_POS = 7;
public const int INTERLACE_SKEW = 8;
public const int MAX_RASTER_ADDR = 9;
public const int CUR_START_RASTER = 10;
public const int CUR_END_RASTER = 11;
public const int DISP_START_ADDR_H = 12;
public const int DISP_START_ADDR_L = 13;
public const int CUR_ADDR_H = 14;
public const int CUR_ADDR_L = 15;
public const int LPEN_ADDR_H = 16;
public const int LPEN_ADDR_L = 17;
#endregion
#endregion
#region Palletes
#region Palletes
/// <summary>
/// The standard CPC Pallete (ordered by firmware #)
/// http://www.cpcwiki.eu/index.php/CPC_Palette
/// </summary>
private static readonly int[] CPCFirmwarePalette =
{
Colors.ARGB(0x00, 0x00, 0x00), // Black
/// <summary>
/// The standard CPC Pallete (ordered by firmware #)
/// http://www.cpcwiki.eu/index.php/CPC_Palette
/// </summary>
private static readonly int[] CPCFirmwarePalette =
{
Colors.ARGB(0x00, 0x00, 0x00), // Black
Colors.ARGB(0x00, 0x00, 0x80), // Blue
Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue
Colors.ARGB(0x80, 0x00, 0x00), // Red
@ -92,13 +92,13 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White
};
/// <summary>
/// The standard CPC Pallete (ordered by hardware #)
/// http://www.cpcwiki.eu/index.php/CPC_Palette
/// </summary>
private static readonly int[] CPCHardwarePalette =
{
Colors.ARGB(0x80, 0x80, 0x80), // White
/// <summary>
/// The standard CPC Pallete (ordered by hardware #)
/// http://www.cpcwiki.eu/index.php/CPC_Palette
/// </summary>
private static readonly int[] CPCHardwarePalette =
{
Colors.ARGB(0x80, 0x80, 0x80), // White
Colors.ARGB(0x80, 0x80, 0x80), // White (duplicate)
Colors.ARGB(0x00, 0xFF, 0x80), // Sea Green
Colors.ARGB(0xFF, 0xFF, 0x80), // Pastel Yellow
@ -132,374 +132,374 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
Colors.ARGB(0x80, 0x80, 0xFF), // Pastel Blue
};
#endregion
#endregion
#region Construction
#region Construction
public GateArrayBase(CPCBase machine)
{
_machine = machine;
PenColours = new int[17];
SetupScreenSize();
Reset();
}
public GateArrayBase(CPCBase machine)
{
_machine = machine;
PenColours = new int[17];
SetupScreenSize();
Reset();
}
/// <summary>
/// Inits the pen lookup table
/// </summary>
public void SetupScreenMapping()
{
for (int m = 0; m < 4; m++)
{
Lookup[m] = new int[256 * 8];
int pos = 0;
/// <summary>
/// Inits the pen lookup table
/// </summary>
public void SetupScreenMapping()
{
for (int m = 0; m < 4; m++)
{
Lookup[m] = new int[256 * 8];
int pos = 0;
for (int b = 0; b < 256; b++)
{
switch (m)
{
case 1:
int pc = (((b & 0x80) >> 7) | ((b & 0x80) >> 2));
Lookup[m][pos++] = pc;
Lookup[m][pos++] = pc;
pc = (((b & 0x40) >> 6) | ((b & 0x04) >> 1));
Lookup[m][pos++] = pc;
Lookup[m][pos++] = pc;
pc = (((b & 0x20) >> 5) | (b & 0x02));
Lookup[m][pos++] = pc;
Lookup[m][pos++] = pc;
pc = (((b & 0x10) >> 4) | ((b & 0x01) << 1));
break;
for (int b = 0; b < 256; b++)
{
switch (m)
{
case 1:
int pc = (((b & 0x80) >> 7) | ((b & 0x80) >> 2));
Lookup[m][pos++] = pc;
Lookup[m][pos++] = pc;
pc = (((b & 0x40) >> 6) | ((b & 0x04) >> 1));
Lookup[m][pos++] = pc;
Lookup[m][pos++] = pc;
pc = (((b & 0x20) >> 5) | (b & 0x02));
Lookup[m][pos++] = pc;
Lookup[m][pos++] = pc;
pc = (((b & 0x10) >> 4) | ((b & 0x01) << 1));
break;
case 2:
for (int i = 7; i >= 0; i--)
{
bool pixel_on = ((b & (1 << i)) != 0);
Lookup[m][pos++] = pixel_on ? 1 : 0;
}
break;
case 2:
for (int i = 7; i >= 0; i--)
{
bool pixel_on = ((b & (1 << i)) != 0);
Lookup[m][pos++] = pixel_on ? 1 : 0;
}
break;
case 3:
case 0:
int pc2 = (b & 0xAA);
pc2 = (
((pc2 & 0x80) >> 7) |
((pc2 & 0x08) >> 2) |
((pc2 & 0x20) >> 3) |
((pc2 & 0x02) << 2));
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
pc2 = (b & 0x55);
pc2 = (
((pc2 & 0x40) >> 6) |
((pc2 & 0x04) >> 1) |
((pc2 & 0x10) >> 2) |
((pc2 & 0x01) << 3));
case 3:
case 0:
int pc2 = (b & 0xAA);
pc2 = (
((pc2 & 0x80) >> 7) |
((pc2 & 0x08) >> 2) |
((pc2 & 0x20) >> 3) |
((pc2 & 0x02) << 2));
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
pc2 = (b & 0x55);
pc2 = (
((pc2 & 0x40) >> 6) |
((pc2 & 0x04) >> 1) |
((pc2 & 0x10) >> 2) |
((pc2 & 0x01) << 3));
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
break;
}
}
}
}
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2;
break;
}
}
}
}
#endregion
#endregion
#region State
#region State
private int[] PenColours;
private int CurrentPen;
private int ScreenMode;
private int INTScanlineCnt;
//private int VSYNCDelyCnt;
private int[] PenColours;
private int CurrentPen;
private int ScreenMode;
private int INTScanlineCnt;
//private int VSYNCDelyCnt;
private int[][] Lookup = new int[4][];
private int[][] Lookup = new int[4][];
//private bool DoModeUpdate;
//private bool DoModeUpdate;
//private int LatchedMode;
//private int buffPos;
//private int LatchedMode;
//private int buffPos;
public bool FrameEnd;
public bool FrameEnd;
public bool WaitLine;
public bool WaitLine;
#endregion
#endregion
#region Clock Operations
#region Clock Operations
/// <summary>
/// The gatearray runs on a 16Mhz clock
/// (for the purposes of emulation, we will use a 4Mhz clock)
/// From this it generates:
/// 1Mhz clock for the CRTC chip
/// 1Mhz clock for the AY-3-8912 PSG
/// 4Mhz clock for the Z80 CPU
/// </summary>
public void ClockCycle()
{
// 4-phase clock
for (int i = 1; i < 5; i++)
{
switch (i)
{
// Phase 1
case 1:
CRCT.ClockCycle();
CPU.ExecuteOne();
break;
// Phase 2
case 2:
CPU.ExecuteOne();
break;
// Phase 3
case 3:
// video fetch
break;
// Phase 4
case 4:
// video fetch
break;
}
}
}
/// <summary>
/// The gatearray runs on a 16Mhz clock
/// (for the purposes of emulation, we will use a 4Mhz clock)
/// From this it generates:
/// 1Mhz clock for the CRTC chip
/// 1Mhz clock for the AY-3-8912 PSG
/// 4Mhz clock for the Z80 CPU
/// </summary>
public void ClockCycle()
{
// 4-phase clock
for (int i = 1; i < 5; i++)
{
switch (i)
{
// Phase 1
case 1:
CRCT.ClockCycle();
CPU.ExecuteOne();
break;
// Phase 2
case 2:
CPU.ExecuteOne();
break;
// Phase 3
case 3:
// video fetch
break;
// Phase 4
case 4:
// video fetch
break;
}
}
}
#endregion
#endregion
#region Internal Methods
#region Internal Methods
/// <summary>
/// Selects the pen
/// </summary>
public virtual void SetPen(BitArray bi)
{
if (bi[4])
{
// border select
CurrentPen = 16;
}
else
{
// pen select
byte[] b = new byte[1];
bi.CopyTo(b, 0);
CurrentPen = b[0] & 0x0f;
}
}
/// <summary>
/// Selects the pen
/// </summary>
public virtual void SetPen(BitArray bi)
{
if (bi[4])
{
// border select
CurrentPen = 16;
}
else
{
// pen select
byte[] b = new byte[1];
bi.CopyTo(b, 0);
CurrentPen = b[0] & 0x0f;
}
}
/// <summary>
/// Selects colour for the currently selected pen
/// </summary>
public virtual void SetPenColour(BitArray bi)
{
byte[] b = new byte[1];
bi.CopyTo(b, 0);
var colour = b[0] & 0x1f;
PenColours[CurrentPen] = colour;
}
/// <summary>
/// Selects colour for the currently selected pen
/// </summary>
public virtual void SetPenColour(BitArray bi)
{
byte[] b = new byte[1];
bi.CopyTo(b, 0);
var colour = b[0] & 0x1f;
PenColours[CurrentPen] = colour;
}
/// <summary>
/// Returns the actual ARGB pen colour value
/// </summary>
public virtual int GetPenColour(int idx)
{
return CPCHardwarePalette[PenColours[idx]];
}
/// <summary>
/// Returns the actual ARGB pen colour value
/// </summary>
public virtual int GetPenColour(int idx)
{
return CPCHardwarePalette[PenColours[idx]];
}
/// <summary>
/// Screen mode and ROM config
/// </summary>
public virtual void SetReg2(BitArray bi)
{
byte[] b = new byte[1];
bi.CopyTo(b, 0);
/// <summary>
/// Screen mode and ROM config
/// </summary>
public virtual void SetReg2(BitArray bi)
{
byte[] b = new byte[1];
bi.CopyTo(b, 0);
// screen mode
var mode = b[0] & 0x03;
ScreenMode = mode;
// screen mode
var mode = b[0] & 0x03;
ScreenMode = mode;
// ROM
// ROM
// upper
if ((b[0] & 0x08) != 0)
{
_machine.UpperROMPaged = false;
}
else
{
_machine.UpperROMPaged = true;
}
// upper
if ((b[0] & 0x08) != 0)
{
_machine.UpperROMPaged = false;
}
else
{
_machine.UpperROMPaged = true;
}
// lower
if ((b[0] & 0x04) != 0)
{
_machine.LowerROMPaged = false;
}
else
{
_machine.LowerROMPaged = true;
}
// lower
if ((b[0] & 0x04) != 0)
{
_machine.LowerROMPaged = false;
}
else
{
_machine.LowerROMPaged = true;
}
// INT delay
if ((b[0] & 0x10) != 0)
{
INTScanlineCnt = 0;
}
}
// INT delay
if ((b[0] & 0x10) != 0)
{
INTScanlineCnt = 0;
}
}
/// <summary>
/// Only available on machines with a 64KB memory expansion
/// Default assume we dont have this
/// </summary>
public virtual void SetRAM(BitArray bi)
{
return;
}
/// <summary>
/// Only available on machines with a 64KB memory expansion
/// Default assume we dont have this
/// </summary>
public virtual void SetRAM(BitArray bi)
{
return;
}
public void InterruptACK()
{
INTScanlineCnt &= 0x01f;
}
public void InterruptACK()
{
INTScanlineCnt &= 0x01f;
}
#endregion
#region Reset
public void Reset()
{
CurrentPen = 0;
ScreenMode = 1;
for (int i = 0; i < 17; i++)
PenColours[i] = 0;
INTScanlineCnt = 0;
//VSYNCDelyCnt = 0;
}
#endregion
#endregion
#region Reset
#region IPortIODevice
public void Reset()
{
CurrentPen = 0;
ScreenMode = 1;
for (int i = 0; i < 17; i++)
PenColours[i] = 0;
INTScanlineCnt = 0;
//VSYNCDelyCnt = 0;
}
/// <summary>
/// Device responds to an IN instruction
/// </summary>
public bool ReadPort(ushort port, ref int result)
{
// gate array is OUT only
return false;
}
#endregion
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
public bool WritePort(ushort port, int result)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
BitArray dataBits = new BitArray(BitConverter.GetBytes((byte)result));
#region IPortIODevice
// The gate array responds to port 0x7F
bool accessed = !portBits[15];
if (!accessed)
return false;
/// <summary>
/// Device responds to an IN instruction
/// </summary>
public bool ReadPort(ushort port, ref int result)
{
// gate array is OUT only
return false;
}
// Bit 9 and 8 of the data byte define the function to access
if (!dataBits[6] && !dataBits[7])
{
// select pen
SetPen(dataBits);
}
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
public bool WritePort(ushort port, int result)
{
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
BitArray dataBits = new BitArray(BitConverter.GetBytes((byte)result));
if (dataBits[6] && !dataBits[7])
{
// select colour for selected pen
SetPenColour(dataBits);
}
// The gate array responds to port 0x7F
bool accessed = !portBits[15];
if (!accessed)
return false;
if (!dataBits[6] && dataBits[7])
{
// select screen mode, ROM configuration and interrupt control
SetReg2(dataBits);
}
// Bit 9 and 8 of the data byte define the function to access
if (!dataBits[6] && !dataBits[7])
{
// select pen
SetPen(dataBits);
}
if (dataBits[6] && dataBits[7])
{
// RAM memory management
SetRAM(dataBits);
}
if (dataBits[6] && !dataBits[7])
{
// select colour for selected pen
SetPenColour(dataBits);
}
return true;
}
if (!dataBits[6] && dataBits[7])
{
// select screen mode, ROM configuration and interrupt control
SetReg2(dataBits);
}
#endregion
if (dataBits[6] && dataBits[7])
{
// RAM memory management
SetRAM(dataBits);
}
#region IVideoProvider
return true;
}
/// <summary>
/// Video output buffer
/// </summary>
public int[] ScreenBuffer;
#endregion
private int _virtualWidth;
private int _virtualHeight;
private int _bufferWidth;
private int _bufferHeight;
#region IVideoProvider
public int BackgroundColor
{
get { return CPCHardwarePalette[16]; }
}
/// <summary>
/// Video output buffer
/// </summary>
public int[] ScreenBuffer;
public int VirtualWidth
{
get { return _virtualWidth; }
set { _virtualWidth = value; }
}
private int _virtualWidth;
private int _virtualHeight;
private int _bufferWidth;
private int _bufferHeight;
public int VirtualHeight
{
get { return _virtualHeight; }
set { _virtualHeight = value; }
}
public int BackgroundColor
{
get { return CPCHardwarePalette[16]; }
}
public int BufferWidth
{
get { return _bufferWidth; }
set { _bufferWidth = value; }
}
public int VirtualWidth
{
get { return _virtualWidth; }
set { _virtualWidth = value; }
}
public int BufferHeight
{
get { return _bufferHeight; }
set { _bufferHeight = value; }
}
public int VirtualHeight
{
get { return _virtualHeight; }
set { _virtualHeight = value; }
}
public int VsyncNumerator
{
get { return Z80ClockSpeed * 50; }
set { }
}
public int BufferWidth
{
get { return _bufferWidth; }
set { _bufferWidth = value; }
}
public int VsyncDenominator
{
get { return Z80ClockSpeed; }
}
public int BufferHeight
{
get { return _bufferHeight; }
set { _bufferHeight = value; }
}
public int[] GetVideoBuffer()
{
return ScreenBuffer;
}
public int VsyncNumerator
{
get { return Z80ClockSpeed * 50; }
set { }
}
protected void SetupScreenSize()
{
/*
public int VsyncDenominator
{
get { return Z80ClockSpeed; }
}
public int[] GetVideoBuffer()
{
return ScreenBuffer;
}
protected void SetupScreenSize()
{
/*
* Rect Pixels: Mode 0: 160×200 pixels with 16 colors (4 bpp)
Sqaure Pixels: Mode 1: 320×200 pixels with 4 colors (2 bpp)
Rect Pixels: Mode 2: 640×200 pixels with 2 colors (1 bpp)
@ -507,25 +507,25 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
*
* */
// define maximum screen buffer size
// to fit all possible resolutions, 640x400 should do it
// therefore a scanline will take two buffer rows
// and buffer columns will be:
// Mode 1: 2 pixels
// Mode 2: 1 pixel
// Mode 0: 4 pixels
// Mode 3: 4 pixels
// define maximum screen buffer size
// to fit all possible resolutions, 640x400 should do it
// therefore a scanline will take two buffer rows
// and buffer columns will be:
// Mode 1: 2 pixels
// Mode 2: 1 pixel
// Mode 0: 4 pixels
// Mode 3: 4 pixels
BufferWidth = 640;
BufferHeight = 400;
VirtualHeight = BufferHeight;
VirtualWidth = BufferWidth;
ScreenBuffer = new int[BufferWidth * BufferHeight];
croppedBuffer = ScreenBuffer;
}
BufferWidth = 640;
BufferHeight = 400;
VirtualHeight = BufferHeight;
VirtualWidth = BufferWidth;
ScreenBuffer = new int[BufferWidth * BufferHeight];
croppedBuffer = ScreenBuffer;
}
protected int[] croppedBuffer;
protected int[] croppedBuffer;
#endregion
}
#endregion
}
}

View File

@ -1,18 +1,18 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// The various CPC models CPCHawk emulates
/// </summary>
public enum MachineType
{
/// <summary>
/// Original Amstrad CPC model with builtin datacorder
/// </summary>
CPC464,
/// <summary>
/// 128K model with builtin 3" disk drive
/// </summary>
CPC6128,
}
/// <summary>
/// The various CPC models CPCHawk emulates
/// </summary>
public enum MachineType
{
/// <summary>
/// Original Amstrad CPC model with builtin datacorder
/// </summary>
CPC464,
/// <summary>
/// 128K model with builtin 3" disk drive
/// </summary>
CPC6128,
}
}

View File

@ -5,249 +5,249 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Logical object representing a standard +3 disk image
/// </summary>
public class CPCExtendedFloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.CPCExtended;
/// <summary>
/// Logical object representing a standard +3 disk image
/// </summary>
public class CPCExtendedFloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.CPCExtended;
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("EXTENDED CPC DSK"))
{
// incorrect format
return false;
}
if (!ident.ToUpper().Contains("EXTENDED CPC DSK"))
{
// incorrect format
return false;
}
// read the disk information block
DiskHeader.DiskIdent = ident;
DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14);
DiskHeader.NumberOfTracks = data[0x30];
DiskHeader.NumberOfSides = data[0x31];
DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskData = data;
int pos = 0x34;
// read the disk information block
DiskHeader.DiskIdent = ident;
DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14);
DiskHeader.NumberOfTracks = data[0x30];
DiskHeader.NumberOfSides = data[0x31];
DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskData = data;
int pos = 0x34;
if (DiskHeader.NumberOfSides > 1)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk image contains multiple sides.");
sbm.AppendLine("This is NOT currently supported in CPCHawk.");
sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk).");
throw new System.NotImplementedException(sbm.ToString());
}
if (DiskHeader.NumberOfSides > 1)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk image contains multiple sides.");
sbm.AppendLine("This is NOT currently supported in CPCHawk.");
sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk).");
throw new System.NotImplementedException(sbm.ToString());
}
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
DiskHeader.TrackSizes[i] = data[pos++] * 256;
}
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
DiskHeader.TrackSizes[i] = data[pos++] * 256;
}
// move to first track information block
pos = 0x100;
// move to first track information block
pos = 0x100;
// parse each track
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
// check for unformatted track
if (DiskHeader.TrackSizes[i] == 0)
{
DiskTracks[i] = new Track();
DiskTracks[i].Sectors = new Sector[0];
continue;
}
// parse each track
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
// check for unformatted track
if (DiskHeader.TrackSizes[i] == 0)
{
DiskTracks[i] = new Track();
DiskTracks[i].Sectors = new Sector[0];
continue;
}
int p = pos;
DiskTracks[i] = new Track();
int p = pos;
DiskTracks[i] = new Track();
// track info block
DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12);
p += 16;
DiskTracks[i].TrackNumber = data[p++];
DiskTracks[i].SideNumber = data[p++];
DiskTracks[i].DataRate = data[p++];
DiskTracks[i].RecordingMode = data[p++];
DiskTracks[i].SectorSize = data[p++];
DiskTracks[i].NumberOfSectors = data[p++];
DiskTracks[i].GAP3Length = data[p++];
DiskTracks[i].FillerByte = data[p++];
// track info block
DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12);
p += 16;
DiskTracks[i].TrackNumber = data[p++];
DiskTracks[i].SideNumber = data[p++];
DiskTracks[i].DataRate = data[p++];
DiskTracks[i].RecordingMode = data[p++];
DiskTracks[i].SectorSize = data[p++];
DiskTracks[i].NumberOfSectors = data[p++];
DiskTracks[i].GAP3Length = data[p++];
DiskTracks[i].FillerByte = data[p++];
int dpos = pos + 0x100;
int dpos = pos + 0x100;
// sector info list
DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors];
for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++)
{
DiskTracks[i].Sectors[s] = new Sector();
// sector info list
DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors];
for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++)
{
DiskTracks[i].Sectors[s] = new Sector();
DiskTracks[i].Sectors[s].TrackNumber = data[p++];
DiskTracks[i].Sectors[s].SideNumber = data[p++];
DiskTracks[i].Sectors[s].SectorID = data[p++];
DiskTracks[i].Sectors[s].SectorSize = data[p++];
DiskTracks[i].Sectors[s].Status1 = data[p++];
DiskTracks[i].Sectors[s].Status2 = data[p++];
DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p);
p += 2;
DiskTracks[i].Sectors[s].TrackNumber = data[p++];
DiskTracks[i].Sectors[s].SideNumber = data[p++];
DiskTracks[i].Sectors[s].SectorID = data[p++];
DiskTracks[i].Sectors[s].SectorSize = data[p++];
DiskTracks[i].Sectors[s].Status1 = data[p++];
DiskTracks[i].Sectors[s].Status2 = data[p++];
DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p);
p += 2;
// sector data - begins at 0x100 offset from the start of the track info block (in this case dpos)
DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength];
// sector data - begins at 0x100 offset from the start of the track info block (in this case dpos)
DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength];
// copy the data
for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++)
{
DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b];
}
// copy the data
for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++)
{
DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b];
}
// check for multiple weak/random sectors stored
if (DiskTracks[i].Sectors[s].SectorSize <= 7)
{
// sectorsize n=8 is equivilent to n=0 - FDC will use DTL for length
int specifiedSize = 0x80 << DiskTracks[i].Sectors[s].SectorSize;
// check for multiple weak/random sectors stored
if (DiskTracks[i].Sectors[s].SectorSize <= 7)
{
// sectorsize n=8 is equivilent to n=0 - FDC will use DTL for length
int specifiedSize = 0x80 << DiskTracks[i].Sectors[s].SectorSize;
if (specifiedSize < DiskTracks[i].Sectors[s].ActualDataByteLength)
{
// more data stored than sectorsize defines
// check for multiple weak/random copies
if (DiskTracks[i].Sectors[s].ActualDataByteLength % specifiedSize != 0)
{
DiskTracks[i].Sectors[s].ContainsMultipleWeakSectors = true;
}
}
}
if (specifiedSize < DiskTracks[i].Sectors[s].ActualDataByteLength)
{
// more data stored than sectorsize defines
// check for multiple weak/random copies
if (DiskTracks[i].Sectors[s].ActualDataByteLength % specifiedSize != 0)
{
DiskTracks[i].Sectors[s].ContainsMultipleWeakSectors = true;
}
}
}
// move dpos to the next sector data postion
dpos += DiskTracks[i].Sectors[s].ActualDataByteLength;
}
// move dpos to the next sector data postion
dpos += DiskTracks[i].Sectors[s].ActualDataByteLength;
}
// move to the next track info block
pos += DiskHeader.TrackSizes[i];
}
// move to the next track info block
pos += DiskHeader.TrackSizes[i];
}
// run protection scheme detector
ParseProtection();
// run protection scheme detector
ParseProtection();
return true;
}
return true;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("EXTENDED CPC DSK"))
{
// incorrect format
return false;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("EXTENDED CPC DSK"))
{
// incorrect format
return false;
}
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
// disk info block
Array.Copy(data, 0, S0, 0, 0x100);
Array.Copy(data, 0, S1, 0, 0x100);
// change side number
S0[0x31] = 1;
S1[0x31] = 1;
// disk info block
Array.Copy(data, 0, S0, 0, 0x100);
Array.Copy(data, 0, S1, 0, 0x100);
// change side number
S0[0x31] = 1;
S1[0x31] = 1;
// extended format has different track sizes
int[] trkSizes = new int[data[0x30] * data[0x31]];
// extended format has different track sizes
int[] trkSizes = new int[data[0x30] * data[0x31]];
int pos = 0x34;
for (int i = 0; i < data[0x30] * data[0x31]; i++)
{
trkSizes[i] = data[pos] * 256;
// clear destination trk sizes (will be added later)
S0[pos] = 0;
S1[pos] = 0;
pos++;
}
int pos = 0x34;
for (int i = 0; i < data[0x30] * data[0x31]; i++)
{
trkSizes[i] = data[pos] * 256;
// clear destination trk sizes (will be added later)
S0[pos] = 0;
S1[pos] = 0;
pos++;
}
// start at track info blocks
int mPos = 0x100;
int s0Pos = 0x100;
int s0tCount = 0;
int s1tCount = 0;
int s1Pos = 0x100;
int tCount = 0;
// start at track info blocks
int mPos = 0x100;
int s0Pos = 0x100;
int s0tCount = 0;
int s1tCount = 0;
int s1Pos = 0x100;
int tCount = 0;
while (tCount < data[0x30] * data[0x31])
{
// which side is this?
var side = data[mPos + 0x11];
if (side == 0)
{
// side 1
Array.Copy(data, mPos, S0, s0Pos, trkSizes[tCount]);
s0Pos += trkSizes[tCount];
// trk size table
S0[0x34 + s0tCount++] = (byte)(trkSizes[tCount] / 256);
}
else if (side == 1)
{
// side 2
Array.Copy(data, mPos, S1, s1Pos, trkSizes[tCount]);
s1Pos += trkSizes[tCount];
// trk size table
S1[0x34 + s1tCount++] = (byte)(trkSizes[tCount] / 256);
}
mPos += trkSizes[tCount++];
}
while (tCount < data[0x30] * data[0x31])
{
// which side is this?
var side = data[mPos + 0x11];
if (side == 0)
{
// side 1
Array.Copy(data, mPos, S0, s0Pos, trkSizes[tCount]);
s0Pos += trkSizes[tCount];
// trk size table
S0[0x34 + s0tCount++] = (byte)(trkSizes[tCount] / 256);
}
else if (side == 1)
{
// side 2
Array.Copy(data, mPos, S1, s1Pos, trkSizes[tCount]);
s1Pos += trkSizes[tCount];
// trk size table
S1[0x34 + s1tCount++] = (byte)(trkSizes[tCount] / 256);
}
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
mPos += trkSizes[tCount++];
}
results.Add(s0final);
results.Add(s1final);
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
return true;
}
results.Add(s0final);
results.Add(s1final);
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
return true;
}
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
}
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
// sync deterministic track and sector counters
ser.Sync(nameof( _randomCounter), ref _randomCounter);
RandomCounter = _randomCounter;
}
ser.EndSection();
}
}
// sync deterministic track and sector counters
ser.Sync(nameof(_randomCounter), ref _randomCounter);
RandomCounter = _randomCounter;
ser.EndSection();
}
}
}

View File

@ -5,232 +5,232 @@ using System;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Logical object representing a standard +3 disk image
/// </summary>
public class CPCFloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.CPC;
/// <summary>
/// Logical object representing a standard +3 disk image
/// </summary>
public class CPCFloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.CPC;
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("MV - CPC"))
{
// incorrect format
return false;
}
if (!ident.ToUpper().Contains("MV - CPC"))
{
// incorrect format
return false;
}
// read the disk information block
DiskHeader.DiskIdent = ident;
DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14);
DiskHeader.NumberOfTracks = data[0x30];
DiskHeader.NumberOfSides = data[0x31];
DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskData = data;
int pos = 0x32;
// read the disk information block
DiskHeader.DiskIdent = ident;
DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14);
DiskHeader.NumberOfTracks = data[0x30];
DiskHeader.NumberOfSides = data[0x31];
DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskData = data;
int pos = 0x32;
if (DiskHeader.NumberOfSides > 1)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk image contains multiple sides.");
sbm.AppendLine("This is NOT currently supported in CPCHawk.");
sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk).");
throw new System.NotImplementedException(sbm.ToString());
}
if (DiskHeader.NumberOfSides > 1)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk image contains multiple sides.");
sbm.AppendLine("This is NOT currently supported in CPCHawk.");
sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk).");
throw new System.NotImplementedException(sbm.ToString());
}
// standard CPC format all track sizes are the same in the image
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
DiskHeader.TrackSizes[i] = MediaConverter.GetWordValue(data, pos);
}
// standard CPC format all track sizes are the same in the image
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
DiskHeader.TrackSizes[i] = MediaConverter.GetWordValue(data, pos);
}
// move to first track information block
pos = 0x100;
// move to first track information block
pos = 0x100;
// parse each track
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
// check for unformatted track
if (DiskHeader.TrackSizes[i] == 0)
{
DiskTracks[i] = new Track();
DiskTracks[i].Sectors = new Sector[0];
continue;
}
// parse each track
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
// check for unformatted track
if (DiskHeader.TrackSizes[i] == 0)
{
DiskTracks[i] = new Track();
DiskTracks[i].Sectors = new Sector[0];
continue;
}
int p = pos;
DiskTracks[i] = new Track();
int p = pos;
DiskTracks[i] = new Track();
// track info block
DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12);
p += 16;
DiskTracks[i].TrackNumber = data[p++];
DiskTracks[i].SideNumber = data[p++];
p += 2;
DiskTracks[i].SectorSize = data[p++];
DiskTracks[i].NumberOfSectors = data[p++];
DiskTracks[i].GAP3Length = data[p++];
DiskTracks[i].FillerByte = data[p++];
// track info block
DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12);
p += 16;
DiskTracks[i].TrackNumber = data[p++];
DiskTracks[i].SideNumber = data[p++];
p += 2;
DiskTracks[i].SectorSize = data[p++];
DiskTracks[i].NumberOfSectors = data[p++];
DiskTracks[i].GAP3Length = data[p++];
DiskTracks[i].FillerByte = data[p++];
int dpos = pos + 0x100;
int dpos = pos + 0x100;
// sector info list
DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors];
for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++)
{
DiskTracks[i].Sectors[s] = new Sector();
// sector info list
DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors];
for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++)
{
DiskTracks[i].Sectors[s] = new Sector();
DiskTracks[i].Sectors[s].TrackNumber = data[p++];
DiskTracks[i].Sectors[s].SideNumber = data[p++];
DiskTracks[i].Sectors[s].SectorID = data[p++];
DiskTracks[i].Sectors[s].SectorSize = data[p++];
DiskTracks[i].Sectors[s].Status1 = data[p++];
DiskTracks[i].Sectors[s].Status2 = data[p++];
DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p);
p += 2;
DiskTracks[i].Sectors[s].TrackNumber = data[p++];
DiskTracks[i].Sectors[s].SideNumber = data[p++];
DiskTracks[i].Sectors[s].SectorID = data[p++];
DiskTracks[i].Sectors[s].SectorSize = data[p++];
DiskTracks[i].Sectors[s].Status1 = data[p++];
DiskTracks[i].Sectors[s].Status2 = data[p++];
DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p);
p += 2;
// actualdatabytelength value is calculated now
if (DiskTracks[i].Sectors[s].SectorSize == 0)
{
// no sectorsize specified - DTL will be used at runtime
DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i];
}
else if (DiskTracks[i].Sectors[s].SectorSize > 6)
{
// invalid - wrap around to 0
DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i];
}
else if (DiskTracks[i].Sectors[s].SectorSize == 6)
{
// only 0x1800 bytes are stored
DiskTracks[i].Sectors[s].ActualDataByteLength = 0x1800;
}
else
{
// valid sector size for this format
DiskTracks[i].Sectors[s].ActualDataByteLength = 0x80 << DiskTracks[i].Sectors[s].SectorSize;
}
// actualdatabytelength value is calculated now
if (DiskTracks[i].Sectors[s].SectorSize == 0)
{
// no sectorsize specified - DTL will be used at runtime
DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i];
}
else if (DiskTracks[i].Sectors[s].SectorSize > 6)
{
// invalid - wrap around to 0
DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i];
}
else if (DiskTracks[i].Sectors[s].SectorSize == 6)
{
// only 0x1800 bytes are stored
DiskTracks[i].Sectors[s].ActualDataByteLength = 0x1800;
}
else
{
// valid sector size for this format
DiskTracks[i].Sectors[s].ActualDataByteLength = 0x80 << DiskTracks[i].Sectors[s].SectorSize;
}
// sector data - begins at 0x100 offset from the start of the track info block (in this case dpos)
DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength];
// sector data - begins at 0x100 offset from the start of the track info block (in this case dpos)
DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength];
// copy the data
for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++)
{
DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b];
}
// copy the data
for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++)
{
DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b];
}
// move dpos to the next sector data postion
dpos += DiskTracks[i].Sectors[s].ActualDataByteLength;
}
// move dpos to the next sector data postion
dpos += DiskTracks[i].Sectors[s].ActualDataByteLength;
}
// move to the next track info block
pos += DiskHeader.TrackSizes[i];
}
// move to the next track info block
pos += DiskHeader.TrackSizes[i];
}
// run protection scheme detector
ParseProtection();
// run protection scheme detector
ParseProtection();
return true;
}
return true;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("MV - CPC"))
{
// incorrect format
return false;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("MV - CPC"))
{
// incorrect format
return false;
}
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
// disk info block
Array.Copy(data, 0, S0, 0, 0x100);
Array.Copy(data, 0, S1, 0, 0x100);
// change side number
S0[0x31] = 1;
S1[0x31] = 1;
// disk info block
Array.Copy(data, 0, S0, 0, 0x100);
Array.Copy(data, 0, S1, 0, 0x100);
// change side number
S0[0x31] = 1;
S1[0x31] = 1;
int trkSize = MediaConverter.GetWordValue(data, 0x32);
int trkSize = MediaConverter.GetWordValue(data, 0x32);
// start at track info blocks
int mPos = 0x100;
int s0Pos = 0x100;
int s1Pos = 0x100;
// start at track info blocks
int mPos = 0x100;
int s0Pos = 0x100;
int s1Pos = 0x100;
while (mPos < trkSize * data[0x30] * data[0x31])
{
// which side is this?
var side = data[mPos + 0x11];
if (side == 0)
{
// side 1
Array.Copy(data, mPos, S0, s0Pos, trkSize);
s0Pos += trkSize;
}
else if (side == 1)
{
// side 2
Array.Copy(data, mPos, S1, s1Pos, trkSize);
s1Pos += trkSize;
}
while (mPos < trkSize * data[0x30] * data[0x31])
{
// which side is this?
var side = data[mPos + 0x11];
if (side == 0)
{
// side 1
Array.Copy(data, mPos, S0, s0Pos, trkSize);
s0Pos += trkSize;
}
else if (side == 1)
{
// side 2
Array.Copy(data, mPos, S1, s1Pos, trkSize);
s1Pos += trkSize;
}
mPos += trkSize;
}
mPos += trkSize;
}
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
results.Add(s0final);
results.Add(s1final);
results.Add(s0final);
results.Add(s1final);
return true;
}
return true;
}
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
}
}
ser.EndSection();
}
}
ser.EndSection();
}
}
}

View File

@ -6,12 +6,12 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// This is called first when importing disk images
/// Disk images can be single or double-sided, so we need to handle that
/// </summary>
public class DiskHandler
{
/// <summary>
/// This is called first when importing disk images
/// Disk images can be single or double-sided, so we need to handle that
/// </summary>
public class DiskHandler
{
}
}
}

View File

@ -1,19 +1,19 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// The different disk formats ZXHawk currently supports
/// </summary>
public enum DiskType
{
/// <summary>
/// Standard CPCEMU disk format (used in the built-in +3 disk drive)
/// </summary>
CPC,
/// <summary>
/// The different disk formats ZXHawk currently supports
/// </summary>
public enum DiskType
{
/// <summary>
/// Standard CPCEMU disk format (used in the built-in +3 disk drive)
/// </summary>
CPC,
/// <summary>
/// Extended CPCEMU disk format (used in the built-in +3 disk drive)
/// </summary>
CPCExtended
}
/// <summary>
/// Extended CPCEMU disk format (used in the built-in +3 disk drive)
/// </summary>
CPCExtended
}
}

View File

@ -4,132 +4,132 @@ using System.IO.Compression;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Abtract class that represents all Media Converters
/// </summary>
public abstract class MediaConverter
{
/// <summary>
/// The type of serializer
/// </summary>
public abstract MediaConverterType FormatType { get; }
/// <summary>
/// Abtract class that represents all Media Converters
/// </summary>
public abstract class MediaConverter
{
/// <summary>
/// The type of serializer
/// </summary>
public abstract MediaConverterType FormatType { get; }
/// <summary>
/// Signs whether this class can be used to read the data format
/// </summary>
public virtual bool IsReader
{
get
{
return false;
}
}
/// <summary>
/// Signs whether this class can be used to read the data format
/// </summary>
public virtual bool IsReader
{
get
{
return false;
}
}
/// <summary>
/// Signs whether this class can be used to write the data format
/// </summary>
public virtual bool IsWriter
{
get
{
return false;
}
}
/// <summary>
/// Signs whether this class can be used to write the data format
/// </summary>
public virtual bool IsWriter
{
get
{
return false;
}
}
/// <summary>
/// Serialization method
/// </summary>
public virtual void Read(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Read operation is not implemented for this converter");
}
/// <summary>
/// Serialization method
/// </summary>
public virtual void Read(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Read operation is not implemented for this converter");
}
/// <summary>
/// DeSerialization method
/// </summary>
public virtual void Write(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Write operation is not implemented for this converter");
}
/// <summary>
/// DeSerialization method
/// </summary>
public virtual void Write(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Write operation is not implemented for this converter");
}
/// <summary>
/// Serializer does a quick check, returns TRUE if file is detected as this type
/// </summary>
public virtual bool CheckType(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Check type operation is not implemented for this converter");
}
/// <summary>
/// Serializer does a quick check, returns TRUE if file is detected as this type
/// </summary>
public virtual bool CheckType(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Check type operation is not implemented for this converter");
}
#region Static Tools
#region Static Tools
/// <summary>
/// Converts an int32 value into a byte array
/// </summary>
public static byte[] GetBytes(int value)
{
byte[] buf = new byte[4];
buf[0] = (byte)value;
buf[1] = (byte)(value >> 8);
buf[2] = (byte)(value >> 16);
buf[3] = (byte)(value >> 24);
return buf;
}
/// <summary>
/// Converts an int32 value into a byte array
/// </summary>
public static byte[] GetBytes(int value)
{
byte[] buf = new byte[4];
buf[0] = (byte)value;
buf[1] = (byte)(value >> 8);
buf[2] = (byte)(value >> 16);
buf[3] = (byte)(value >> 24);
return buf;
}
/// <summary>
/// Returns an int32 from a byte array based on offset
/// </summary>
public static int GetInt32(byte[] buf, int offsetIndex)
{
return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24;
}
/// <summary>
/// Returns an int32 from a byte array based on offset
/// </summary>
public static int GetInt32(byte[] buf, int offsetIndex)
{
return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24;
}
/// <summary>
/// Returns an uint16 from a byte array based on offset
/// </summary>
public static ushort GetWordValue(byte[] buf, int offsetIndex)
{
return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8);
}
/// <summary>
/// Returns an uint16 from a byte array based on offset
/// </summary>
public static ushort GetWordValue(byte[] buf, int offsetIndex)
{
return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8);
}
/// <summary>
/// Updates a byte array with a uint16 value based on offset
/// </summary>
public static void SetWordValue(byte[] buf, int offsetIndex, ushort value)
{
buf[offsetIndex] = (byte)value;
buf[offsetIndex + 1] = (byte)(value >> 8);
}
/// <summary>
/// Updates a byte array with a uint16 value based on offset
/// </summary>
public static void SetWordValue(byte[] buf, int offsetIndex, ushort value)
{
buf[offsetIndex] = (byte)value;
buf[offsetIndex + 1] = (byte)(value >> 8);
}
/// <summary>
/// Takes a PauseInMilliseconds value and returns the value in T-States
/// </summary>
public static int TranslatePause(int pauseInMS)
{
// t-states per millisecond
var tspms = (69888 * 50) / 1000;
// get value
int res = pauseInMS * tspms;
/// <summary>
/// Takes a PauseInMilliseconds value and returns the value in T-States
/// </summary>
public static int TranslatePause(int pauseInMS)
{
// t-states per millisecond
var tspms = (69888 * 50) / 1000;
// get value
int res = pauseInMS * tspms;
return res;
}
return res;
}
/// <summary>
/// Decompresses a byte array that is Z-RLE compressed
/// </summary>
public static void DecompressZRLE(byte[] sourceBuffer, ref byte[] destBuffer)
{
MemoryStream stream = new MemoryStream();
stream.Write(sourceBuffer, 0, sourceBuffer.Length);
stream.Position = 0;
stream.ReadByte();
stream.ReadByte();
DeflateStream ds = new DeflateStream(stream, CompressionMode.Decompress, false);
ds.Read(destBuffer, 0, destBuffer.Length);
}
/// <summary>
/// Decompresses a byte array that is Z-RLE compressed
/// </summary>
public static void DecompressZRLE(byte[] sourceBuffer, ref byte[] destBuffer)
{
MemoryStream stream = new MemoryStream();
stream.Write(sourceBuffer, 0, sourceBuffer.Length);
stream.Position = 0;
stream.ReadByte();
stream.ReadByte();
DeflateStream ds = new DeflateStream(stream, CompressionMode.Decompress, false);
ds.Read(destBuffer, 0, destBuffer.Length);
}
#endregion
}
#endregion
}
}

View File

@ -1,13 +1,13 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Represents the different types of media serializer avaiable
/// </summary>
public enum MediaConverterType
{
NONE,
CDT,
DSK
}
/// <summary>
/// Represents the different types of media serializer avaiable
/// </summary>
public enum MediaConverterType
{
NONE,
CDT,
DSK
}
}

View File

@ -1,16 +1,16 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Represents the possible commands that can be raised from each tape block
/// </summary>
public enum TapeCommand
{
NONE,
STOP_THE_TAPE,
STOP_THE_TAPE_48K,
BEGIN_GROUP,
END_GROUP,
SHOW_MESSAGE,
}
/// <summary>
/// Represents the possible commands that can be raised from each tape block
/// </summary>
public enum TapeCommand
{
NONE,
STOP_THE_TAPE,
STOP_THE_TAPE_48K,
BEGIN_GROUP,
END_GROUP,
SHOW_MESSAGE,
}
}

View File

@ -5,51 +5,53 @@ using System.Text;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Represents a tape block
/// </summary>
public class TapeDataBlock
{
/// <summary>
/// Either the TZX block ID, or -1 in the case of non-tzx blocks
/// </summary>
private int _blockID = -1;
public int BlockID
{
get { return _blockID; }
set {
_blockID = value;
/// <summary>
/// Represents a tape block
/// </summary>
public class TapeDataBlock
{
/// <summary>
/// Either the TZX block ID, or -1 in the case of non-tzx blocks
/// </summary>
private int _blockID = -1;
public int BlockID
{
get { return _blockID; }
set
{
_blockID = value;
if (MetaData == null)
MetaData = new Dictionary<BlockDescriptorTitle, string>();
if (MetaData == null)
MetaData = new Dictionary<BlockDescriptorTitle, string>();
AddMetaData(BlockDescriptorTitle.Block_ID, value.ToString());
}
}
AddMetaData(BlockDescriptorTitle.Block_ID, value.ToString());
}
}
/// <summary>
/// The block type
/// </summary>
private BlockType _blockType;
public BlockType BlockDescription
{
get { return _blockType; }
set {
_blockType = value;
if (MetaData == null)
MetaData = new Dictionary<BlockDescriptorTitle, string>();
}
}
/// <summary>
/// The block type
/// </summary>
private BlockType _blockType;
public BlockType BlockDescription
{
get { return _blockType; }
set
{
_blockType = value;
if (MetaData == null)
MetaData = new Dictionary<BlockDescriptorTitle, string>();
}
}
/// <summary>
/// Byte array containing the raw block data
/// </summary>
private byte[] _blockData;
public byte[] BlockData
{
get { return _blockData; }
set { _blockData = value; }
}
/// <summary>
/// Byte array containing the raw block data
/// </summary>
private byte[] _blockData;
public byte[] BlockData
{
get { return _blockData; }
set { _blockData = value; }
}
/*
/// <summary>
@ -78,203 +80,203 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
*/
#region Block Meta Data
#region Block Meta Data
/// <summary>
/// Dictionary of block related data
/// </summary>
public Dictionary<BlockDescriptorTitle, string> MetaData { get; set; }
/// <summary>
/// Dictionary of block related data
/// </summary>
public Dictionary<BlockDescriptorTitle, string> MetaData { get; set; }
/// <summary>
/// Adds a single metadata item to the Dictionary
/// </summary>
public void AddMetaData(BlockDescriptorTitle descriptor, string data)
{
// check whether entry already exists
bool check = MetaData.ContainsKey(descriptor);
if (check)
{
// already exists - update
MetaData[descriptor] = data;
}
else
{
// create new
MetaData.Add(descriptor, data);
}
}
/// <summary>
/// Adds a single metadata item to the Dictionary
/// </summary>
public void AddMetaData(BlockDescriptorTitle descriptor, string data)
{
// check whether entry already exists
bool check = MetaData.ContainsKey(descriptor);
if (check)
{
// already exists - update
MetaData[descriptor] = data;
}
else
{
// create new
MetaData.Add(descriptor, data);
}
}
#endregion
#endregion
/// <summary>
/// List containing the pulse timing values
/// </summary>
public List<int> DataPeriods = new List<int>();
/// <summary>
/// List containing the pulse timing values
/// </summary>
public List<int> DataPeriods = new List<int>();
public bool InitialPulseLevel;
public bool InitialPulseLevel;
/// <summary>
/// Command that is raised by this data block
/// (that may or may not need to be acted on)
/// </summary>
private TapeCommand _command = TapeCommand.NONE;
public TapeCommand Command
{
get { return _command; }
set { _command = value; }
}
/// <summary>
/// Command that is raised by this data block
/// (that may or may not need to be acted on)
/// </summary>
private TapeCommand _command = TapeCommand.NONE;
public TapeCommand Command
{
get { return _command; }
set { _command = value; }
}
/// <summary>
/// The defined post-block pause
/// </summary>
private int _pauseInMS;
public int PauseInMS
{
get { return _pauseInMS; }
set { _pauseInMS = value; }
}
/// <summary>
/// The defined post-block pause
/// </summary>
private int _pauseInMS;
public int PauseInMS
{
get { return _pauseInMS; }
set { _pauseInMS = value; }
}
/// <summary>
/// Returns the data periods as an array
/// (primarily to aid in bizhawk state serialization)
/// </summary>
public int[] GetDataPeriodsArray()
{
return DataPeriods.ToArray();
}
/// <summary>
/// Returns the data periods as an array
/// (primarily to aid in bizhawk state serialization)
/// </summary>
public int[] GetDataPeriodsArray()
{
return DataPeriods.ToArray();
}
/// <summary>
/// Accepts an array of data periods and updates the DataPeriods list accordingly
/// (primarily to aid in bizhawk state serialization)
/// </summary>
public void SetDataPeriodsArray(int[] periodArray)
{
DataPeriods = periodArray?.ToList() ?? new List<int>();
}
/// <summary>
/// Accepts an array of data periods and updates the DataPeriods list accordingly
/// (primarily to aid in bizhawk state serialization)
/// </summary>
public void SetDataPeriodsArray(int[] periodArray)
{
DataPeriods = periodArray?.ToList() ?? new List<int>();
}
/// <summary>
/// Bizhawk state serialization
/// </summary>
public void SyncState(Serializer ser, int blockPosition)
{
ser.BeginSection("DataBlock" + blockPosition);
/// <summary>
/// Bizhawk state serialization
/// </summary>
public void SyncState(Serializer ser, int blockPosition)
{
ser.BeginSection("DataBlock" + blockPosition);
ser.Sync(nameof(_blockID), ref _blockID);
//ser.SyncFixedString(nameof(_blockDescription), ref _blockDescription, 200);
ser.SyncEnum(nameof(_blockType), ref _blockType);
ser.Sync(nameof(_blockData), ref _blockData, true);
ser.SyncEnum(nameof(_command), ref _command);
ser.Sync(nameof(_blockID), ref _blockID);
//ser.SyncFixedString(nameof(_blockDescription), ref _blockDescription, 200);
ser.SyncEnum(nameof(_blockType), ref _blockType);
ser.Sync(nameof(_blockData), ref _blockData, true);
ser.SyncEnum(nameof(_command), ref _command);
int[] tempArray = null;
int[] tempArray = null;
if (ser.IsWriter)
{
tempArray = GetDataPeriodsArray();
ser.Sync("_periods", ref tempArray, true);
}
else
{
ser.Sync("_periods", ref tempArray, true);
SetDataPeriodsArray(tempArray);
}
if (ser.IsWriter)
{
tempArray = GetDataPeriodsArray();
ser.Sync("_periods", ref tempArray, true);
}
else
{
ser.Sync("_periods", ref tempArray, true);
SetDataPeriodsArray(tempArray);
}
ser.EndSection();
}
}
ser.EndSection();
}
}
/// <summary>
/// The types of TZX blocks
/// </summary>
public enum BlockType
{
Standard_Speed_Data_Block = 0x10,
Turbo_Speed_Data_Block = 0x11,
Pure_Tone = 0x12,
Pulse_Sequence = 0x13,
Pure_Data_Block = 0x14,
Direct_Recording = 0x15,
CSW_Recording = 0x18,
Generalized_Data_Block = 0x19,
Pause_or_Stop_the_Tape = 0x20,
Group_Start = 0x21,
Group_End = 0x22,
Jump_to_Block = 0x23,
Loop_Start = 0x24,
Loop_End = 0x25,
Call_Sequence = 0x26,
Return_From_Sequence = 0x27,
Select_Block = 0x28,
Stop_the_Tape_48K = 0x2A,
Set_Signal_Level = 0x2B,
Text_Description = 0x30,
Message_Block = 0x31,
Archive_Info = 0x32,
Hardware_Type = 0x33,
Custom_Info_Block = 0x35,
Glue_Block = 0x5A,
/// <summary>
/// The types of TZX blocks
/// </summary>
public enum BlockType
{
Standard_Speed_Data_Block = 0x10,
Turbo_Speed_Data_Block = 0x11,
Pure_Tone = 0x12,
Pulse_Sequence = 0x13,
Pure_Data_Block = 0x14,
Direct_Recording = 0x15,
CSW_Recording = 0x18,
Generalized_Data_Block = 0x19,
Pause_or_Stop_the_Tape = 0x20,
Group_Start = 0x21,
Group_End = 0x22,
Jump_to_Block = 0x23,
Loop_Start = 0x24,
Loop_End = 0x25,
Call_Sequence = 0x26,
Return_From_Sequence = 0x27,
Select_Block = 0x28,
Stop_the_Tape_48K = 0x2A,
Set_Signal_Level = 0x2B,
Text_Description = 0x30,
Message_Block = 0x31,
Archive_Info = 0x32,
Hardware_Type = 0x33,
Custom_Info_Block = 0x35,
Glue_Block = 0x5A,
// depreciated blocks
C64_ROM_Type_Data_Block = 0x16,
C64_Turbo_Tape_Data_Block = 0x17,
Emulation_Info = 0x34,
Snapshot_Block = 0x40,
// depreciated blocks
C64_ROM_Type_Data_Block = 0x16,
C64_Turbo_Tape_Data_Block = 0x17,
Emulation_Info = 0x34,
Snapshot_Block = 0x40,
// unsupported / undetected
Unsupported,
// unsupported / undetected
Unsupported,
// PZX blocks
PZXT,
PULS,
DATA,
BRWS,
PAUS,
// PZX blocks
PZXT,
PULS,
DATA,
BRWS,
PAUS,
// zxhawk proprietry
PAUSE_BLOCK,
// zxhawk proprietry
PAUSE_BLOCK,
WAV_Recording
}
WAV_Recording
}
/// <summary>
/// Different title possibilities
/// </summary>
public enum BlockDescriptorTitle
{
Undefined,
Block_ID,
Program,
Data_Bytes,
Bytes,
Pilot_Pulse_Length,
Pilot_Pulse_Count,
First_Sync_Length,
Second_Sync_Length,
Zero_Bit_Length,
One_Bit_Length,
Data_Length,
Bits_In_Last_Byte,
Pause_After_Data,
/// <summary>
/// Different title possibilities
/// </summary>
public enum BlockDescriptorTitle
{
Undefined,
Block_ID,
Program,
Data_Bytes,
Bytes,
Pulse_Length,
Pulse_Count,
Pilot_Pulse_Length,
Pilot_Pulse_Count,
First_Sync_Length,
Second_Sync_Length,
Zero_Bit_Length,
One_Bit_Length,
Data_Length,
Bits_In_Last_Byte,
Pause_After_Data,
Text_Description,
Title,
Publisher,
Author,
Year,
Language,
Type,
Price,
Protection,
Origin,
Comments,
Pulse_Length,
Pulse_Count,
Needs_Parsing
}
Text_Description,
Title,
Publisher,
Author,
Year,
Language,
Type,
Price,
Protection,
Origin,
Comments,
Needs_Parsing
}
}

View File

@ -1,64 +1,64 @@

namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// Information about Amstrad ROM
/// </summary>
public class RomData
{
/// <summary>
/// ROM Contents
/// </summary>
public byte[] RomBytes
{
get { return _romBytes; }
set { _romBytes = value; }
}
private byte[] _romBytes;
/// <summary>
/// Information about Amstrad ROM
/// </summary>
public class RomData
{
/// <summary>
/// ROM Contents
/// </summary>
public byte[] RomBytes
{
get { return _romBytes; }
set { _romBytes = value; }
}
private byte[] _romBytes;
public enum ROMChipType
{
Lower,
Upper
}
public enum ROMChipType
{
Lower,
Upper
}
/// <summary>
/// Whether this is an Upper or Lower ROM
/// </summary>
public ROMChipType ROMType;
/// <summary>
/// Whether this is an Upper or Lower ROM
/// </summary>
public ROMChipType ROMType;
/// <summary>
/// The designated ROM position for this ROM
/// </summary>
public int ROMPosition;
/// <summary>
/// The designated ROM position for this ROM
/// </summary>
public int ROMPosition;
/// <summary>
/// Initialise a RomData object
/// </summary>
public static RomData InitROM(MachineType machineType, byte[] rom, ROMChipType type, int romPosition = 0)
{
RomData RD = new RomData();
RD.RomBytes = new byte[rom.Length];
RD.RomBytes = rom;
RD.ROMType = type;
/// <summary>
/// Initialise a RomData object
/// </summary>
public static RomData InitROM(MachineType machineType, byte[] rom, ROMChipType type, int romPosition = 0)
{
RomData RD = new RomData();
RD.RomBytes = new byte[rom.Length];
RD.RomBytes = rom;
RD.ROMType = type;
if (type == ROMChipType.Upper)
{
RD.ROMPosition = romPosition;
}
if (type == ROMChipType.Upper)
{
RD.ROMPosition = romPosition;
}
for (int i = 0; i < rom.Length; i++)
RD.RomBytes[i] = rom[i];
for (int i = 0; i < rom.Length; i++)
RD.RomBytes[i] = rom[i];
switch (machineType)
{
case MachineType.CPC464:
break;
case MachineType.CPC6128:
break;
}
switch (machineType)
{
case MachineType.CPC464:
break;
case MachineType.CPC6128:
break;
}
return RD;
}
}
return RD;
}
}
}

View File

@ -5,213 +5,213 @@ using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>
/// My attempt at mixing multiple ISoundProvider sources together and outputting another ISoundProvider
/// Currently only supports SyncSoundMode.Sync
/// Attached ISoundProvider sources must already be stereo 44.1khz and ideally sound buffers should be the same length (882)
/// (if not, only 882 samples of their buffer will be used)
/// </summary>
internal sealed class SoundProviderMixer : ISoundProvider
{
private class Provider
{
public ISoundProvider SoundProvider { get; set; }
public string ProviderDescription { get; set; }
public int MaxVolume { get; set; }
public short[] Buffer { get; set; }
public int NSamp { get; set; }
}
/// <summary>
/// My attempt at mixing multiple ISoundProvider sources together and outputting another ISoundProvider
/// Currently only supports SyncSoundMode.Sync
/// Attached ISoundProvider sources must already be stereo 44.1khz and ideally sound buffers should be the same length (882)
/// (if not, only 882 samples of their buffer will be used)
/// </summary>
internal sealed class SoundProviderMixer : ISoundProvider
{
private class Provider
{
public ISoundProvider SoundProvider { get; set; }
public string ProviderDescription { get; set; }
public int MaxVolume { get; set; }
public short[] Buffer { get; set; }
public int NSamp { get; set; }
}
private bool _stereo = true;
public bool Stereo
{
get { return _stereo; }
set { _stereo = value; }
}
private bool _stereo = true;
public bool Stereo
{
get { return _stereo; }
set { _stereo = value; }
}
private readonly List<Provider> SoundProviders;
public SoundProviderMixer(params ISoundProvider[] soundProviders)
{
SoundProviders = new List<Provider>();
private readonly List<Provider> SoundProviders;
foreach (var s in soundProviders)
{
SoundProviders.Add(new Provider
{
SoundProvider = s,
MaxVolume = short.MaxValue,
});
}
public SoundProviderMixer(params ISoundProvider[] soundProviders)
{
SoundProviders = new List<Provider>();
EqualizeVolumes();
}
foreach (var s in soundProviders)
{
SoundProviders.Add(new Provider
{
SoundProvider = s,
MaxVolume = short.MaxValue,
});
}
public SoundProviderMixer(short maxVolume, string description, params ISoundProvider[] soundProviders)
{
SoundProviders = new List<Provider>();
EqualizeVolumes();
}
foreach (var s in soundProviders)
{
SoundProviders.Add(new Provider
{
SoundProvider = s,
MaxVolume = maxVolume,
ProviderDescription = description
});
}
public SoundProviderMixer(short maxVolume, string description, params ISoundProvider[] soundProviders)
{
SoundProviders = new List<Provider>();
EqualizeVolumes();
}
foreach (var s in soundProviders)
{
SoundProviders.Add(new Provider
{
SoundProvider = s,
MaxVolume = maxVolume,
ProviderDescription = description
});
}
public void AddSource(ISoundProvider source, string description)
{
SoundProviders.Add(new Provider
{
SoundProvider = source,
MaxVolume = short.MaxValue,
ProviderDescription = description
});
EqualizeVolumes();
}
EqualizeVolumes();
}
public void AddSource(ISoundProvider source, string description)
{
SoundProviders.Add(new Provider
{
SoundProvider = source,
MaxVolume = short.MaxValue,
ProviderDescription = description
});
public void AddSource(ISoundProvider source, short maxVolume, string description)
{
SoundProviders.Add(new Provider
{
SoundProvider = source,
MaxVolume = maxVolume,
ProviderDescription = description
});
EqualizeVolumes();
}
EqualizeVolumes();
}
public void AddSource(ISoundProvider source, short maxVolume, string description)
{
SoundProviders.Add(new Provider
{
SoundProvider = source,
MaxVolume = maxVolume,
ProviderDescription = description
});
public void DisableSource(ISoundProvider source)
{
var sp = SoundProviders.Where(a => a.SoundProvider == source);
if (sp.Count() == 1)
SoundProviders.Remove(sp.First());
else if (sp.Count() > 1)
foreach (var s in sp)
SoundProviders.Remove(s);
EqualizeVolumes();
}
EqualizeVolumes();
}
public void DisableSource(ISoundProvider source)
{
var sp = SoundProviders.Where(a => a.SoundProvider == source);
if (sp.Count() == 1)
SoundProviders.Remove(sp.First());
else if (sp.Count() > 1)
foreach (var s in sp)
SoundProviders.Remove(s);
public void EqualizeVolumes()
{
if (SoundProviders.Count < 1)
return;
EqualizeVolumes();
}
int eachVolume = short.MaxValue / SoundProviders.Count;
foreach (var source in SoundProviders)
{
source.MaxVolume = eachVolume;
}
}
public void EqualizeVolumes()
{
if (SoundProviders.Count < 1)
return;
#region ISoundProvider
int eachVolume = short.MaxValue / SoundProviders.Count;
foreach (var source in SoundProviders)
{
source.MaxVolume = eachVolume;
}
}
public bool CanProvideAsync => false;
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
#region ISoundProvider
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
throw new InvalidOperationException("Only Sync mode is supported.");
}
public bool CanProvideAsync => false;
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
throw new InvalidOperationException("Only Sync mode is supported.");
}
public void DiscardSamples()
{
foreach (var soundSource in SoundProviders)
{
soundSource.SoundProvider.DiscardSamples();
}
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = null;
nsamp = 0;
public void DiscardSamples()
{
foreach (var soundSource in SoundProviders)
{
soundSource.SoundProvider.DiscardSamples();
}
}
// get samples from all the providers
foreach (var sp in SoundProviders)
{
int sampCount;
short[] samp;
sp.SoundProvider.GetSamplesSync(out samp, out sampCount);
sp.NSamp = sampCount;
sp.Buffer = samp;
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = null;
nsamp = 0;
// are all the sample lengths the same?
var firstEntry = SoundProviders.First();
bool sameCount = SoundProviders.All(s => s.NSamp == firstEntry.NSamp);
// get samples from all the providers
foreach (var sp in SoundProviders)
{
int sampCount;
short[] samp;
sp.SoundProvider.GetSamplesSync(out samp, out sampCount);
sp.NSamp = sampCount;
sp.Buffer = samp;
}
if (!sameCount)
{
// this is a bit hacky, really all ISoundProviders should be supplying 44100 with 882 samples per frame.
// we will make sure this happens (no matter how it sounds)
if (SoundProviders.Count > 1)
{
for (int i = 0; i < SoundProviders.Count; i++)
{
int ns = SoundProviders[i].NSamp;
short[] buff = new short[882 * 2];
// are all the sample lengths the same?
var firstEntry = SoundProviders.First();
bool sameCount = SoundProviders.All(s => s.NSamp == firstEntry.NSamp);
for (int b = 0; b < 882 * 2; b++)
{
if (b == SoundProviders[i].Buffer.Length - 1)
{
// end of source buffer
break;
}
if (!sameCount)
{
// this is a bit hacky, really all ISoundProviders should be supplying 44100 with 882 samples per frame.
// we will make sure this happens (no matter how it sounds)
if (SoundProviders.Count > 1)
{
for (int i = 0; i < SoundProviders.Count; i++)
{
int ns = SoundProviders[i].NSamp;
short[] buff = new short[882 * 2];
buff[b] = SoundProviders[i].Buffer[b];
}
for (int b = 0; b < 882 * 2; b++)
{
if (b == SoundProviders[i].Buffer.Length - 1)
{
// end of source buffer
break;
}
// save back to the soundprovider
SoundProviders[i].NSamp = 882;
SoundProviders[i].Buffer = buff;
}
}
else
{
// just process what we have as-is
}
}
buff[b] = SoundProviders[i].Buffer[b];
}
// mix the soundproviders together
nsamp = 882;
samples = new short[nsamp * 2];
// save back to the soundprovider
SoundProviders[i].NSamp = 882;
SoundProviders[i].Buffer = buff;
}
}
else
{
// just process what we have as-is
}
}
for (int i = 0; i < samples.Length; i++)
{
short sectorVal = 0;
foreach (var sp in SoundProviders)
{
if (i < sp.Buffer.Length)
{
if (sp.Buffer[i] > sp.MaxVolume)
sectorVal += (short)sp.MaxVolume;
else
sectorVal += sp.Buffer[i];
}
}
// mix the soundproviders together
nsamp = 882;
samples = new short[nsamp * 2];
samples[i] = sectorVal;
}
}
for (int i = 0; i < samples.Length; i++)
{
short sectorVal = 0;
foreach (var sp in SoundProviders)
{
if (i < sp.Buffer.Length)
{
if (sp.Buffer[i] > sp.MaxVolume)
sectorVal += (short)sp.MaxVolume;
else
sectorVal += sp.Buffer[i];
}
#endregion
}
}
samples[i] = sectorVal;
}
}
#endregion
}
}

View File

@ -2,24 +2,24 @@
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Represents a beeper/buzzer device
/// </summary>
public interface IBeeperDevice
{
/// <summary>
/// Initialisation
/// </summary>
void Init(int sampleRate, int tStatesPerFrame);
/// <summary>
/// Represents a beeper/buzzer device
/// </summary>
public interface IBeeperDevice
{
/// <summary>
/// Initialisation
/// </summary>
void Init(int sampleRate, int tStatesPerFrame);
/// <summary>
/// Processes an incoming pulse value and adds it to the blipbuffer
/// </summary>
void ProcessPulseValue(bool pulse);
/// <summary>
/// Processes an incoming pulse value and adds it to the blipbuffer
/// </summary>
void ProcessPulseValue(bool pulse);
/// <summary>
/// State serialization
/// </summary>
void SyncState(Serializer ser);
}
/// <summary>
/// State serialization
/// </summary>
void SyncState(Serializer ser);
}
}

View File

@ -1,29 +1,29 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Defines an object that can load a floppy disk image
/// </summary>
public interface IFDDHost
{
/// <summary>
/// The currently inserted diskimage
/// </summary>
FloppyDisk Disk { get; set; }
/// <summary>
/// Defines an object that can load a floppy disk image
/// </summary>
public interface IFDDHost
{
/// <summary>
/// The currently inserted diskimage
/// </summary>
FloppyDisk Disk { get; set; }
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
void FDD_LoadDisk(byte[] diskData);
/// <summary>
/// Parses a new disk image and loads it into this floppy drive
/// </summary>
void FDD_LoadDisk(byte[] diskData);
/// <summary>
/// Ejects the current disk
/// </summary>
void FDD_EjectDisk();
/// <summary>
/// Ejects the current disk
/// </summary>
void FDD_EjectDisk();
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
bool FDD_IsDiskLoaded { get; }
}
/// <summary>
/// Signs whether the current active drive has a disk inserted
/// </summary>
bool FDD_IsDiskLoaded { get; }
}
}

View File

@ -1,34 +1,34 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Represents a spectrum joystick
/// </summary>
public interface IJoystick
{
/// <summary>
/// The type of joystick
/// </summary>
JoystickType JoyType { get; }
/// <summary>
/// Represents a spectrum joystick
/// </summary>
public interface IJoystick
{
/// <summary>
/// The type of joystick
/// </summary>
JoystickType JoyType { get; }
/// <summary>
/// Array of all the possibly button press names
/// </summary>
string[] ButtonCollection { get; set; }
/// <summary>
/// Array of all the possibly button press names
/// </summary>
string[] ButtonCollection { get; set; }
/// <summary>
/// The player number that this controller is currently assigned to
/// </summary>
int PlayerNumber { get; set; }
/// <summary>
/// The player number that this controller is currently assigned to
/// </summary>
int PlayerNumber { get; set; }
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
void SetJoyInput(string key, bool isPressed);
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
void SetJoyInput(string key, bool isPressed);
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
bool GetJoyInput(string key);
}
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
bool GetJoyInput(string key);
}
}

View File

@ -2,69 +2,69 @@
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Represents a spectrum keyboard
/// </summary>
public interface IKeyboard : IPortIODevice
{
/// <summary>
/// The calling spectrumbase class
/// </summary>
SpectrumBase _machine { get; }
/// <summary>
/// Represents a spectrum keyboard
/// </summary>
public interface IKeyboard : IPortIODevice
{
/// <summary>
/// The calling spectrumbase class
/// </summary>
SpectrumBase _machine { get; }
/// <summary>
/// The keyboard matrix for a particular spectrum model
/// </summary>
string[] KeyboardMatrix { get; set; }
/// <summary>
/// The keyboard matrix for a particular spectrum model
/// </summary>
string[] KeyboardMatrix { get; set; }
/// <summary>
/// Other keyboard keys that are not in the matrix
/// (usually keys derived from key combos)
/// </summary>
string[] NonMatrixKeys { get; set; }
/// <summary>
/// Other keyboard keys that are not in the matrix
/// (usually keys derived from key combos)
/// </summary>
string[] NonMatrixKeys { get; set; }
/// <summary>
/// Represents the spectrum key state
/// </summary>
int[] KeyLine { get; set; }
/// <summary>
/// Represents the spectrum key state
/// </summary>
int[] KeyLine { get; set; }
/// <summary>
/// Resets the line status
/// </summary>
void ResetLineStatus();
/// <summary>
/// Resets the line status
/// </summary>
void ResetLineStatus();
/// <summary>
/// There are some slight differences in how PortIN and PortOUT functions
/// between Issue2 and Issue3 keyboards (16k/48k spectrum only)
/// It is possible that some very old games require Issue2 emulation
/// </summary>
bool IsIssue2Keyboard { get; set; }
/// <summary>
/// There are some slight differences in how PortIN and PortOUT functions
/// between Issue2 and Issue3 keyboards (16k/48k spectrum only)
/// It is possible that some very old games require Issue2 emulation
/// </summary>
bool IsIssue2Keyboard { get; set; }
/// <summary>
/// Sets the spectrum key status
/// </summary>
void SetKeyStatus(string key, bool isPressed);
/// <summary>
/// Sets the spectrum key status
/// </summary>
void SetKeyStatus(string key, bool isPressed);
/// <summary>
/// Gets the status of a spectrum key
/// </summary>
bool GetKeyStatus(string key);
/// <summary>
/// Gets the status of a spectrum key
/// </summary>
bool GetKeyStatus(string key);
/// <summary>
/// Returns the query byte
/// </summary>
byte GetLineStatus(byte lines);
/// <summary>
/// Returns the query byte
/// </summary>
byte GetLineStatus(byte lines);
/// <summary>
/// Reads a keyboard byte
/// </summary>
byte ReadKeyboardByte(ushort addr);
/// <summary>
/// Reads a keyboard byte
/// </summary>
byte ReadKeyboardByte(ushort addr);
/// <summary>
/// Looks up a key in the keyboard matrix and returns the relevent byte value
/// </summary>
byte GetByteFromKeyMatrix(string key);
/// <summary>
/// Looks up a key in the keyboard matrix and returns the relevent byte value
/// </summary>
byte GetByteFromKeyMatrix(string key);
void SyncState(Serializer ser);
}
void SyncState(Serializer ser);
}
}

View File

@ -3,62 +3,62 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Represents a PSG device (in this case an AY-3-891x)
/// </summary>
public interface IPSG : ISoundProvider, IPortIODevice
{
/// <summary>
/// Initlization routine
/// </summary>
void Init(int sampleRate, int tStatesPerFrame);
/// <summary>
/// Represents a PSG device (in this case an AY-3-891x)
/// </summary>
public interface IPSG : ISoundProvider, IPortIODevice
{
/// <summary>
/// Initlization routine
/// </summary>
void Init(int sampleRate, int tStatesPerFrame);
/// <summary>
/// Activates a register
/// </summary>
int SelectedRegister { get; set; }
/// <summary>
/// Activates a register
/// </summary>
int SelectedRegister { get; set; }
int[] ExportRegisters();
/// <summary>
/// Writes to the PSG
/// </summary>
void PortWrite(int value);
int[] ExportRegisters();
/// <summary>
/// Reads from the PSG
/// </summary>
int PortRead();
/// <summary>
/// Writes to the PSG
/// </summary>
void PortWrite(int value);
/// <summary>
/// Resets the PSG
/// </summary>
void Reset();
/// <summary>
/// Reads from the PSG
/// </summary>
int PortRead();
/// <summary>
/// The volume of the AY chip
/// </summary>
int Volume { get; set; }
/// <summary>
/// Called at the start of a frame
/// </summary>
void StartFrame();
/// <summary>
/// Resets the PSG
/// </summary>
void Reset();
/// <summary>
/// called at the end of a frame
/// </summary>
void EndFrame();
/// <summary>
/// The volume of the AY chip
/// </summary>
int Volume { get; set; }
/// <summary>
/// Updates the sound based on number of frame cycles
/// </summary>
void UpdateSound(int frameCycle);
/// <summary>
/// Called at the start of a frame
/// </summary>
void StartFrame();
/// <summary>
/// IStatable serialization
/// </summary>
void SyncState(Serializer ser);
}
/// <summary>
/// called at the end of a frame
/// </summary>
void EndFrame();
/// <summary>
/// Updates the sound based on number of frame cycles
/// </summary>
void UpdateSound(int frameCycle);
/// <summary>
/// IStatable serialization
/// </summary>
void SyncState(Serializer ser);
}
}

View File

@ -1,19 +1,19 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Represents a device that utilizes port IN & OUT
/// </summary>
public interface IPortIODevice
{
/// <summary>
/// Device responds to an IN instruction
/// </summary>
bool ReadPort(ushort port, ref int result);
/// <summary>
/// Represents a device that utilizes port IN & OUT
/// </summary>
public interface IPortIODevice
{
/// <summary>
/// Device responds to an IN instruction
/// </summary>
bool ReadPort(ushort port, ref int result);
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
bool WritePort(ushort port, int result);
}
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
bool WritePort(ushort port, int result);
}
}

View File

@ -1,180 +1,180 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Used for the sector CHRN structure
/// </summary>
public class CHRN
{
/// <summary>
/// Track
/// </summary>
public byte C { get; set; }
/// <summary>
/// Used for the sector CHRN structure
/// </summary>
public class CHRN
{
/// <summary>
/// Track
/// </summary>
public byte C { get; set; }
/// <summary>
/// Side
/// </summary>
public byte H { get; set; }
/// <summary>
/// Side
/// </summary>
public byte H { get; set; }
/// <summary>
/// Sector ID
/// </summary>
public byte R { get; set; }
/// <summary>
/// Sector ID
/// </summary>
public byte R { get; set; }
/// <summary>
/// Sector Size
/// </summary>
public byte N { get; set; }
/// <summary>
/// Sector Size
/// </summary>
public byte N { get; set; }
/// <summary>
/// Status register 1
/// </summary>
private byte _flag1;
public byte Flag1
{
get { return _flag1; }
set { _flag1 = value; }
}
/// <summary>
/// Status register 1
/// </summary>
private byte _flag1;
public byte Flag1
{
get { return _flag1; }
set { _flag1 = value; }
}
/// <summary>
/// Status register 2
/// </summary>
private byte _flag2;
public byte Flag2
{
get { return _flag2; }
set { _flag2 = value; }
}
/// <summary>
/// Status register 2
/// </summary>
private byte _flag2;
public byte Flag2
{
get { return _flag2; }
set { _flag2 = value; }
}
/// <summary>
/// Used to store the last transmitted/received data bytes
/// </summary>
public byte[] DataBytes { get; set; }
/// <summary>
/// Used to store the last transmitted/received data bytes
/// </summary>
public byte[] DataBytes { get; set; }
/// <summary>
/// ID for the read/write data command
/// </summary>
public int DataID { get; set; }
/// <summary>
/// ID for the read/write data command
/// </summary>
public int DataID { get; set; }
#region Helper Methods
#region Helper Methods
/// <summary>
/// Missing Address Mark (Sector_ID or DAM not found)
/// </summary>
public bool ST1MA
{
get { return NECUPD765.GetBit(0, _flag1); }
set
{
if (value) { NECUPD765.SetBit(0, ref _flag1); }
else { NECUPD765.UnSetBit(0, ref _flag1); }
}
}
/// <summary>
/// Missing Address Mark (Sector_ID or DAM not found)
/// </summary>
public bool ST1MA
{
get { return NECUPD765.GetBit(0, _flag1); }
set
{
if (value) { NECUPD765.SetBit(0, ref _flag1); }
else { NECUPD765.UnSetBit(0, ref _flag1); }
}
}
/// <summary>
/// No Data (Sector_ID not found, CRC fail in ID_field)
/// </summary>
public bool ST1ND
{
get { return NECUPD765.GetBit(2, _flag1); }
set
{
if (value) { NECUPD765.SetBit(2, ref _flag1); }
else { NECUPD765.UnSetBit(2, ref _flag1); }
}
}
/// <summary>
/// No Data (Sector_ID not found, CRC fail in ID_field)
/// </summary>
public bool ST1ND
{
get { return NECUPD765.GetBit(2, _flag1); }
set
{
if (value) { NECUPD765.SetBit(2, ref _flag1); }
else { NECUPD765.UnSetBit(2, ref _flag1); }
}
}
/// <summary>
/// Data Error (CRC-fail in ID- or Data-Field)
/// </summary>
public bool ST1DE
{
get { return NECUPD765.GetBit(5, _flag1); }
set
{
if (value) { NECUPD765.SetBit(5, ref _flag1); }
else { NECUPD765.UnSetBit(5, ref _flag1); }
}
}
/// <summary>
/// Data Error (CRC-fail in ID- or Data-Field)
/// </summary>
public bool ST1DE
{
get { return NECUPD765.GetBit(5, _flag1); }
set
{
if (value) { NECUPD765.SetBit(5, ref _flag1); }
else { NECUPD765.UnSetBit(5, ref _flag1); }
}
}
/// <summary>
/// End of Track (set past most read/write commands) (see IC)
/// </summary>
public bool ST1EN
{
get { return NECUPD765.GetBit(7, _flag1); }
set
{
if (value) { NECUPD765.SetBit(7, ref _flag1); }
else { NECUPD765.UnSetBit(7, ref _flag1); }
}
}
/// <summary>
/// End of Track (set past most read/write commands) (see IC)
/// </summary>
public bool ST1EN
{
get { return NECUPD765.GetBit(7, _flag1); }
set
{
if (value) { NECUPD765.SetBit(7, ref _flag1); }
else { NECUPD765.UnSetBit(7, ref _flag1); }
}
}
/// <summary>
/// Missing Address Mark in Data Field (DAM not found)
/// </summary>
public bool ST2MD
{
get { return NECUPD765.GetBit(0, _flag2); }
set
{
if (value) { NECUPD765.SetBit(0, ref _flag2); }
else { NECUPD765.UnSetBit(0, ref _flag2); }
}
}
/// <summary>
/// Missing Address Mark in Data Field (DAM not found)
/// </summary>
public bool ST2MD
{
get { return NECUPD765.GetBit(0, _flag2); }
set
{
if (value) { NECUPD765.SetBit(0, ref _flag2); }
else { NECUPD765.UnSetBit(0, ref _flag2); }
}
}
/// <summary>
/// Bad Cylinder (read/programmed track-ID different and read-ID = FF)
/// </summary>
public bool ST2BC
{
get { return NECUPD765.GetBit(1, _flag2); }
set
{
if (value) { NECUPD765.SetBit(1, ref _flag2); }
else { NECUPD765.UnSetBit(1, ref _flag2); }
}
}
/// <summary>
/// Bad Cylinder (read/programmed track-ID different and read-ID = FF)
/// </summary>
public bool ST2BC
{
get { return NECUPD765.GetBit(1, _flag2); }
set
{
if (value) { NECUPD765.SetBit(1, ref _flag2); }
else { NECUPD765.UnSetBit(1, ref _flag2); }
}
}
/// <summary>
/// Wrong Cylinder (read/programmed track-ID different) (see b1)
/// </summary>
public bool ST2WC
{
get { return NECUPD765.GetBit(4, _flag2); }
set
{
if (value) { NECUPD765.SetBit(4, ref _flag2); }
else { NECUPD765.UnSetBit(4, ref _flag2); }
}
}
/// <summary>
/// Wrong Cylinder (read/programmed track-ID different) (see b1)
/// </summary>
public bool ST2WC
{
get { return NECUPD765.GetBit(4, _flag2); }
set
{
if (value) { NECUPD765.SetBit(4, ref _flag2); }
else { NECUPD765.UnSetBit(4, ref _flag2); }
}
}
/// <summary>
/// Data Error in Data Field (CRC-fail in data-field)
/// </summary>
public bool ST2DD
{
get { return NECUPD765.GetBit(5, _flag2); }
set
{
if (value) { NECUPD765.SetBit(5, ref _flag2); }
else { NECUPD765.UnSetBit(5, ref _flag2); }
}
}
/// <summary>
/// Data Error in Data Field (CRC-fail in data-field)
/// </summary>
public bool ST2DD
{
get { return NECUPD765.GetBit(5, _flag2); }
set
{
if (value) { NECUPD765.SetBit(5, ref _flag2); }
else { NECUPD765.UnSetBit(5, ref _flag2); }
}
}
/// <summary>
/// Control Mark (read/scan command found sector with deleted DAM)
/// </summary>
public bool ST2CM
{
get { return NECUPD765.GetBit(6, _flag2); }
set
{
if (value) { NECUPD765.SetBit(6, ref _flag2); }
else { NECUPD765.UnSetBit(6, ref _flag2); }
}
}
/// <summary>
/// Control Mark (read/scan command found sector with deleted DAM)
/// </summary>
public bool ST2CM
{
get { return NECUPD765.GetBit(6, _flag2); }
set
{
if (value) { NECUPD765.SetBit(6, ref _flag2); }
else { NECUPD765.UnSetBit(6, ref _flag2); }
}
}
#endregion
}
#endregion
}
}

View File

@ -3,243 +3,243 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// The NEC floppy disk controller (and floppy drive) found in the +3
/// </summary>
#region Attribution
/*
/// <summary>
/// The NEC floppy disk controller (and floppy drive) found in the +3
/// </summary>
#region Attribution
/*
Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC
and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/
#endregion
public partial class NECUPD765
{
#region Devices
#endregion
public partial class NECUPD765
{
#region Devices
/// <summary>
/// The emulated spectrum machine
/// </summary>
private SpectrumBase _machine;
/// <summary>
/// The emulated spectrum machine
/// </summary>
private SpectrumBase _machine;
#endregion
#endregion
#region Construction & Initialization
#region Construction & Initialization
/// <summary>
/// Main constructor
/// </summary>
public NECUPD765()
{
InitCommandList();
}
/// <summary>
/// Main constructor
/// </summary>
public NECUPD765()
{
InitCommandList();
}
/// <summary>
/// Initialization routine
/// </summary>
public void Init(SpectrumBase machine)
{
_machine = machine;
FDD_Init();
TimingInit();
Reset();
}
/// <summary>
/// Resets the FDC
/// </summary>
public void Reset()
{
// setup main status
StatusMain = 0;
/// <summary>
/// Initialization routine
/// </summary>
public void Init(SpectrumBase machine)
{
_machine = machine;
FDD_Init();
TimingInit();
Reset();
}
Status0 = 0;
Status1 = 0;
Status2 = 0;
Status3 = 0;
/// <summary>
/// Resets the FDC
/// </summary>
public void Reset()
{
// setup main status
StatusMain = 0;
SetBit(MSR_RQM, ref StatusMain);
Status0 = 0;
Status1 = 0;
Status2 = 0;
Status3 = 0;
SetPhase_Idle();
SetBit(MSR_RQM, ref StatusMain);
//FDC_FLAG_RQM = true;
//ActiveDirection = CommandDirection.IN;
SRT = 6;
HUT = 16;
HLT = 2;
HLT_Counter = 0;
HUT_Counter = 0;
IndexPulseCounter = 0;
CMD_FLAG_MF = false;
SetPhase_Idle();
foreach (var d in DriveStates)
{
//d.SeekingTrack = d.CurrentTrack;
////d.SeekCounter = 0;
//d.FLAG_SEEK_INTERRUPT = false;
//d.IntStatus = 0;
//d.SeekState = SeekSubState.Idle;
//d.SeekIntState = SeekIntStatus.Normal;
//FDC_FLAG_RQM = true;
//ActiveDirection = CommandDirection.IN;
SRT = 6;
HUT = 16;
HLT = 2;
HLT_Counter = 0;
HUT_Counter = 0;
IndexPulseCounter = 0;
CMD_FLAG_MF = false;
}
}
foreach (var d in DriveStates)
{
//d.SeekingTrack = d.CurrentTrack;
////d.SeekCounter = 0;
//d.FLAG_SEEK_INTERRUPT = false;
//d.IntStatus = 0;
//d.SeekState = SeekSubState.Idle;
//d.SeekIntState = SeekIntStatus.Normal;
/// <summary>
/// Setup the command structure
/// Each command represents one of the internal UPD765 commands
/// </summary>
private void InitCommandList()
{
CommandList = new List<Command>
{
}
}
/// <summary>
/// Setup the command structure
/// Each command represents one of the internal UPD765 commands
/// </summary>
private void InitCommandList()
{
CommandList = new List<Command>
{
// read data
new Command { CommandDelegate = UPD_ReadData, CommandCode = 0x06, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
// read id
new Command { CommandDelegate = UPD_ReadID, CommandCode = 0x0a, MF = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 7 },
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 7 },
// specify
new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03,
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
// read diagnostic
new Command { CommandDelegate = UPD_ReadDiagnostic, CommandCode = 0x02, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
// scan equal
new Command { CommandDelegate = UPD_ScanEqual, CommandCode = 0x11, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// scan high or equal
new Command { CommandDelegate = UPD_ScanHighOrEqual, CommandCode = 0x1d, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// scan low or equal
new Command { CommandDelegate = UPD_ScanLowOrEqual, CommandCode = 0x19, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// read deleted data
new Command { CommandDelegate = UPD_ReadDeletedData, CommandCode = 0x0c, MT = true, MF = true, SK = true, IsRead = true,
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 },
// write data
new Command { CommandDelegate = UPD_WriteData, CommandCode = 0x05, MT = true, MF = true, IsWrite = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// write id
new Command { CommandDelegate = UPD_WriteID, CommandCode = 0x0d, MF = true, IsWrite = true,
Direction = CommandDirection.IN, ParameterByteCount = 5, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 5, ResultByteCount = 7 },
// write deleted data
new Command { CommandDelegate = UPD_WriteDeletedData, CommandCode = 0x09, MT = true, MF = true, IsWrite = true,
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 },
// seek
new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f,
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
// recalibrate (seek track00)
new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 },
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 },
// sense interrupt status
new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 },
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 },
// sense drive status
new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 },
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 },
// version
new Command { CommandDelegate = UPD_Version, CommandCode = 0x10,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
// invalid
new Command { CommandDelegate = UPD_Invalid, CommandCode = 0x00,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
};
}
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
};
}
#endregion
#endregion
#region State Serialization
#region State Serialization
public void SyncState(Serializer ser)
{
ser.BeginSection("NEC-UPD765");
public void SyncState(Serializer ser)
{
ser.BeginSection("NEC-UPD765");
#region FDD
ser.Sync(nameof(FDD_FLAG_MOTOR), ref FDD_FLAG_MOTOR);
#region FDD
for (int i = 0; i < 4; i++)
{
ser.BeginSection("HITDrive_" + i);
DriveStates[i].SyncState(ser);
ser.EndSection();
}
ser.Sync(nameof(FDD_FLAG_MOTOR), ref FDD_FLAG_MOTOR);
ser.Sync(nameof(DiskDriveIndex), ref _diskDriveIndex);
// set active drive
DiskDriveIndex = _diskDriveIndex;
for (int i = 0; i < 4; i++)
{
ser.BeginSection("HITDrive_" + i);
DriveStates[i].SyncState(ser);
ser.EndSection();
}
#endregion
ser.Sync(nameof(DiskDriveIndex), ref _diskDriveIndex);
// set active drive
DiskDriveIndex = _diskDriveIndex;
#region Registers
#endregion
ser.Sync("_RegMain", ref StatusMain);
ser.Sync("_Reg0", ref Status0);
ser.Sync("_Reg1", ref Status1);
ser.Sync("_Reg2", ref Status2);
ser.Sync("_Reg3", ref Status3);
#region Registers
#endregion
ser.Sync("_RegMain", ref StatusMain);
ser.Sync("_Reg0", ref Status0);
ser.Sync("_Reg1", ref Status1);
ser.Sync("_Reg2", ref Status2);
ser.Sync("_Reg3", ref Status3);
#region Controller state
#endregion
ser.Sync(nameof(DriveLight), ref DriveLight);
ser.SyncEnum(nameof(ActivePhase), ref ActivePhase);
//ser.SyncEnum(nameof(ActiveDirection), ref ActiveDirection);
ser.SyncEnum(nameof(ActiveInterrupt), ref ActiveInterrupt);
ser.Sync(nameof(CommBuffer), ref CommBuffer, false);
ser.Sync(nameof(CommCounter), ref CommCounter);
ser.Sync(nameof(ResBuffer), ref ResBuffer, false);
ser.Sync(nameof(ExecBuffer), ref ExecBuffer, false);
ser.Sync(nameof(ExecCounter), ref ExecCounter);
ser.Sync(nameof(ExecLength), ref ExecLength);
ser.Sync(nameof(InterruptResultBuffer), ref InterruptResultBuffer, false);
ser.Sync(nameof(ResCounter), ref ResCounter);
ser.Sync(nameof(ResLength), ref ResLength);
ser.Sync(nameof(LastSectorDataWriteByte), ref LastSectorDataWriteByte);
ser.Sync(nameof(LastSectorDataReadByte), ref LastSectorDataReadByte);
ser.Sync(nameof(LastByteReceived), ref LastByteReceived);
ser.Sync(nameof(_cmdIndex), ref _cmdIndex);
// resync the ActiveCommand
CMDIndex = _cmdIndex;
#region Controller state
ActiveCommandParams.SyncState(ser);
ser.Sync(nameof(IndexPulseCounter), ref IndexPulseCounter);
//ser.SyncEnum(nameof(_activeStatus), ref _activeStatus);
//ser.SyncEnum(nameof(_statusRaised), ref _statusRaised);
ser.Sync(nameof(DriveLight), ref DriveLight);
ser.SyncEnum(nameof(ActivePhase), ref ActivePhase);
//ser.SyncEnum(nameof(ActiveDirection), ref ActiveDirection);
ser.SyncEnum(nameof(ActiveInterrupt), ref ActiveInterrupt);
ser.Sync(nameof(CommBuffer), ref CommBuffer, false);
ser.Sync(nameof(CommCounter), ref CommCounter);
ser.Sync(nameof(ResBuffer), ref ResBuffer, false);
ser.Sync(nameof(ExecBuffer), ref ExecBuffer, false);
ser.Sync(nameof(ExecCounter), ref ExecCounter);
ser.Sync(nameof(ExecLength), ref ExecLength);
ser.Sync(nameof(InterruptResultBuffer), ref InterruptResultBuffer, false);
ser.Sync(nameof(ResCounter), ref ResCounter);
ser.Sync(nameof(ResLength), ref ResLength);
ser.Sync(nameof(LastSectorDataWriteByte), ref LastSectorDataWriteByte);
ser.Sync(nameof(LastSectorDataReadByte), ref LastSectorDataReadByte);
ser.Sync(nameof(LastByteReceived), ref LastByteReceived);
ser.Sync(nameof(CMD_FLAG_MT), ref CMD_FLAG_MT);
ser.Sync(nameof(CMD_FLAG_MF), ref CMD_FLAG_MF);
ser.Sync(nameof(CMD_FLAG_SK), ref CMD_FLAG_SK);
ser.Sync(nameof(SRT), ref SRT);
ser.Sync(nameof(HUT), ref HUT);
ser.Sync(nameof(HLT), ref HLT);
ser.Sync(nameof(ND), ref ND);
ser.Sync(nameof(SRT_Counter), ref SRT_Counter);
ser.Sync(nameof(HUT_Counter), ref HUT_Counter);
ser.Sync(nameof(HLT_Counter), ref HLT_Counter);
ser.Sync(nameof(_cmdIndex), ref _cmdIndex);
// resync the ActiveCommand
CMDIndex = _cmdIndex;
ser.Sync(nameof(SectorDelayCounter), ref SectorDelayCounter);
ser.Sync(nameof(SectorID), ref SectorID);
ActiveCommandParams.SyncState(ser);
#endregion
ser.Sync(nameof(IndexPulseCounter), ref IndexPulseCounter);
//ser.SyncEnum(nameof(_activeStatus), ref _activeStatus);
//ser.SyncEnum(nameof(_statusRaised), ref _statusRaised);
#region Timing
ser.Sync(nameof(CMD_FLAG_MT), ref CMD_FLAG_MT);
ser.Sync(nameof(CMD_FLAG_MF), ref CMD_FLAG_MF);
ser.Sync(nameof(CMD_FLAG_SK), ref CMD_FLAG_SK);
ser.Sync(nameof(SRT), ref SRT);
ser.Sync(nameof(HUT), ref HUT);
ser.Sync(nameof(HLT), ref HLT);
ser.Sync(nameof(ND), ref ND);
ser.Sync(nameof(SRT_Counter), ref SRT_Counter);
ser.Sync(nameof(HUT_Counter), ref HUT_Counter);
ser.Sync(nameof(HLT_Counter), ref HLT_Counter);
ser.Sync(nameof(LastCPUCycle), ref LastCPUCycle);
ser.Sync(nameof(StatusDelay), ref StatusDelay);
ser.Sync(nameof(TickCounter), ref TickCounter);
ser.Sync(nameof(DriveCycleCounter), ref DriveCycleCounter);
ser.Sync(nameof(SectorDelayCounter), ref SectorDelayCounter);
ser.Sync(nameof(SectorID), ref SectorID);
#endregion
#endregion
ser.EndSection();
}
#region Timing
#endregion
}
ser.Sync(nameof(LastCPUCycle), ref LastCPUCycle);
ser.Sync(nameof(StatusDelay), ref StatusDelay);
ser.Sync(nameof(TickCounter), ref TickCounter);
ser.Sync(nameof(DriveCycleCounter), ref DriveCycleCounter);
#endregion
ser.EndSection();
}
#endregion
}
}

View File

@ -3,104 +3,104 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Cursor joystick
/// Maps to a combination of 0xf7fe and 0xeffe
/// </summary>
public class CursorJoystick : IJoystick
{
//private int _joyLine;
private SpectrumBase _machine;
/// <summary>
/// Cursor joystick
/// Maps to a combination of 0xf7fe and 0xeffe
/// </summary>
public class CursorJoystick : IJoystick
{
//private int _joyLine;
private SpectrumBase _machine;
#region Construction
#region Construction
public CursorJoystick(SpectrumBase machine, int playerNumber)
{
_machine = machine;
//_joyLine = 0;
_playerNumber = playerNumber;
public CursorJoystick(SpectrumBase machine, int playerNumber)
{
_machine = machine;
//_joyLine = 0;
_playerNumber = playerNumber;
ButtonCollection = new List<string>
{
"P" + _playerNumber + " Left",
"P" + _playerNumber + " Right",
"P" + _playerNumber + " Down",
"P" + _playerNumber + " Up",
"P" + _playerNumber + " Button",
}.ToArray();
}
ButtonCollection = new List<string>
{
"P" + _playerNumber + " Left",
"P" + _playerNumber + " Right",
"P" + _playerNumber + " Down",
"P" + _playerNumber + " Up",
"P" + _playerNumber + " Button",
}.ToArray();
}
private List<string> btnLookups = new List<string>
{
"Key 5", // left
private List<string> btnLookups = new List<string>
{
"Key 5", // left
"Key 8", // right
"Key 6", // down
"Key 7", // up
"Key 0", // fire
};
#endregion
#endregion
#region IJoystick
#region IJoystick
public JoystickType JoyType => JoystickType.Cursor;
public JoystickType JoyType => JoystickType.Cursor;
public string[] ButtonCollection { get; set; }
public string[] ButtonCollection { get; set; }
private int _playerNumber;
public int PlayerNumber
{
get { return _playerNumber; }
set { _playerNumber = value; }
}
private int _playerNumber;
public int PlayerNumber
{
get { return _playerNumber; }
set { _playerNumber = value; }
}
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
public void SetJoyInput(string key, bool isPressed)
{
var pos = GetBitPos(key);
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
public void SetJoyInput(string key, bool isPressed)
{
var pos = GetBitPos(key);
if (isPressed)
{
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true);
}
else
{
if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]))
{
// key is already pressed elswhere - leave it as is
}
else
{
// key is safe to unpress
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false);
}
}
}
if (isPressed)
{
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true);
}
else
{
if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]))
{
// key is already pressed elswhere - leave it as is
}
else
{
// key is safe to unpress
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false);
}
}
}
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
public bool GetJoyInput(string key)
{
var pos = GetBitPos(key);
if (_machine == null)
return false;
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
public bool GetJoyInput(string key)
{
var pos = GetBitPos(key);
if (_machine == null)
return false;
var l = _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]);
return l;
}
#endregion
var l = _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]);
return l;
}
/// <summary>
/// Gets the bit position of a particular joystick binding from the matrix
/// </summary>
public int GetBitPos(string key)
{
int index = Array.IndexOf(ButtonCollection, key);
return index;
}
}
#endregion
/// <summary>
/// Gets the bit position of a particular joystick binding from the matrix
/// </summary>
public int GetBitPos(string key)
{
int index = Array.IndexOf(ButtonCollection, key);
return index;
}
}
}

View File

@ -3,88 +3,88 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
public class KempstonJoystick : IJoystick
{
private int _joyLine;
private SpectrumBase _machine;
public class KempstonJoystick : IJoystick
{
private int _joyLine;
private SpectrumBase _machine;
#region Construction
#region Construction
public KempstonJoystick(SpectrumBase machine, int playerNumber)
{
_machine = machine;
_joyLine = 0;
_playerNumber = playerNumber;
public KempstonJoystick(SpectrumBase machine, int playerNumber)
{
_machine = machine;
_joyLine = 0;
_playerNumber = playerNumber;
ButtonCollection = new List<string>
{
"P" + _playerNumber + " Right",
"P" + _playerNumber + " Left",
"P" + _playerNumber + " Down",
"P" + _playerNumber + " Up",
"P" + _playerNumber + " Button",
}.ToArray();
}
ButtonCollection = new List<string>
{
"P" + _playerNumber + " Right",
"P" + _playerNumber + " Left",
"P" + _playerNumber + " Down",
"P" + _playerNumber + " Up",
"P" + _playerNumber + " Button",
}.ToArray();
}
#endregion
#endregion
#region IJoystick
#region IJoystick
public JoystickType JoyType => JoystickType.Kempston;
public JoystickType JoyType => JoystickType.Kempston;
public string[] ButtonCollection { get; set; }
public string[] ButtonCollection { get; set; }
private int _playerNumber;
public int PlayerNumber
{
get { return _playerNumber; }
set { _playerNumber = value; }
}
private int _playerNumber;
public int PlayerNumber
{
get { return _playerNumber; }
set { _playerNumber = value; }
}
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
public void SetJoyInput(string key, bool isPressed)
{
var pos = GetBitPos(key);
if (isPressed)
_joyLine |= (1 << pos);
else
_joyLine &= ~(1 << pos);
}
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
public void SetJoyInput(string key, bool isPressed)
{
var pos = GetBitPos(key);
if (isPressed)
_joyLine |= (1 << pos);
else
_joyLine &= ~(1 << pos);
}
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
public bool GetJoyInput(string key)
{
var pos = GetBitPos(key);
return (_joyLine & (1 << pos)) != 0;
}
#endregion
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
public bool GetJoyInput(string key)
{
var pos = GetBitPos(key);
return (_joyLine & (1 << pos)) != 0;
}
/// <summary>
/// Active bits high
/// 0 0 0 F U D L R
/// </summary>
public int JoyLine
{
get { return _joyLine; }
set { _joyLine = value; }
}
#endregion
/// <summary>
/// Gets the bit position of a particular joystick binding from the matrix
/// </summary>
public int GetBitPos(string key)
{
int index = Array.IndexOf(ButtonCollection, key);
return index;
}
/// <summary>
/// Active bits high
/// 0 0 0 F U D L R
/// </summary>
public int JoyLine
{
get { return _joyLine; }
set { _joyLine = value; }
}
/// <summary>
/// Gets the bit position of a particular joystick binding from the matrix
/// </summary>
public int GetBitPos(string key)
{
int index = Array.IndexOf(ButtonCollection, key);
return index;
}
/*
/*
public readonly string[] _bitPos = new string[]
{
"P1 Right",
@ -94,5 +94,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
"P1 Button"
};
*/
}
}
}

View File

@ -3,87 +3,87 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// A null joystick object
/// </summary>
public class NullJoystick : IJoystick
{
private int _joyLine;
private SpectrumBase _machine;
/// <summary>
/// A null joystick object
/// </summary>
public class NullJoystick : IJoystick
{
private int _joyLine;
private SpectrumBase _machine;
#region Construction
#region Construction
public NullJoystick(SpectrumBase machine, int playerNumber)
{
_machine = machine;
_joyLine = 0;
_playerNumber = playerNumber;
public NullJoystick(SpectrumBase machine, int playerNumber)
{
_machine = machine;
_joyLine = 0;
_playerNumber = playerNumber;
ButtonCollection = new List<string>
{
ButtonCollection = new List<string>
{
}.ToArray();
}
}.ToArray();
}
#endregion
#endregion
#region IJoystick
#region IJoystick
public JoystickType JoyType => JoystickType.NULL;
public JoystickType JoyType => JoystickType.NULL;
public string[] ButtonCollection { get; set; }
public string[] ButtonCollection { get; set; }
private int _playerNumber;
public int PlayerNumber
{
get { return _playerNumber; }
set { _playerNumber = value; }
}
private int _playerNumber;
public int PlayerNumber
{
get { return _playerNumber; }
set { _playerNumber = value; }
}
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
public void SetJoyInput(string key, bool isPressed)
{
var pos = GetBitPos(key);
if (isPressed)
_joyLine |= (1 << pos);
else
_joyLine &= ~(1 << pos);
}
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
public void SetJoyInput(string key, bool isPressed)
{
var pos = GetBitPos(key);
if (isPressed)
_joyLine |= (1 << pos);
else
_joyLine &= ~(1 << pos);
}
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
public bool GetJoyInput(string key)
{
var pos = GetBitPos(key);
return (_joyLine & (1 << pos)) != 0;
}
#endregion
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
public bool GetJoyInput(string key)
{
var pos = GetBitPos(key);
return (_joyLine & (1 << pos)) != 0;
}
/// <summary>
/// Active bits high
/// 0 0 0 F U D L R
/// </summary>
public int JoyLine
{
get { return _joyLine; }
set { _joyLine = value; }
}
#endregion
/// <summary>
/// Gets the bit position of a particular joystick binding from the matrix
/// </summary>
public int GetBitPos(string key)
{
int index = Array.IndexOf(ButtonCollection, key);
return index;
}
/// <summary>
/// Active bits high
/// 0 0 0 F U D L R
/// </summary>
public int JoyLine
{
get { return _joyLine; }
set { _joyLine = value; }
}
/// <summary>
/// Gets the bit position of a particular joystick binding from the matrix
/// </summary>
public int GetBitPos(string key)
{
int index = Array.IndexOf(ButtonCollection, key);
return index;
}
/*
/*
public readonly string[] _bitPos = new string[]
{
"P1 Right",
@ -93,5 +93,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
"P1 Button"
};
*/
}
}
}

View File

@ -3,103 +3,103 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Sinclair Joystick LEFT
/// Just maps to the standard keyboard and is read the same (from port 0xf7fe)
/// </summary>
public class SinclairJoystick1 : IJoystick
{
//private int _joyLine;
private SpectrumBase _machine;
/// <summary>
/// Sinclair Joystick LEFT
/// Just maps to the standard keyboard and is read the same (from port 0xf7fe)
/// </summary>
public class SinclairJoystick1 : IJoystick
{
//private int _joyLine;
private SpectrumBase _machine;
#region Construction
#region Construction
public SinclairJoystick1(SpectrumBase machine, int playerNumber)
{
_machine = machine;
//_joyLine = 0;
_playerNumber = playerNumber;
public SinclairJoystick1(SpectrumBase machine, int playerNumber)
{
_machine = machine;
//_joyLine = 0;
_playerNumber = playerNumber;
ButtonCollection = new List<string>
{
"P" + _playerNumber + " Left",
"P" + _playerNumber + " Right",
"P" + _playerNumber + " Down",
"P" + _playerNumber + " Up",
"P" + _playerNumber + " Button",
}.ToArray();
}
ButtonCollection = new List<string>
{
"P" + _playerNumber + " Left",
"P" + _playerNumber + " Right",
"P" + _playerNumber + " Down",
"P" + _playerNumber + " Up",
"P" + _playerNumber + " Button",
}.ToArray();
}
private List<string> btnLookups = new List<string>
{
"Key 1", // left
private List<string> btnLookups = new List<string>
{
"Key 1", // left
"Key 2", // right
"Key 3", // down
"Key 4", // up
"Key 5", // fire
};
#endregion
#endregion
#region IJoystick
#region IJoystick
public JoystickType JoyType => JoystickType.SinclairLEFT;
public JoystickType JoyType => JoystickType.SinclairLEFT;
public string[] ButtonCollection { get; set; }
public string[] ButtonCollection { get; set; }
private int _playerNumber;
public int PlayerNumber
{
get { return _playerNumber; }
set { _playerNumber = value; }
}
private int _playerNumber;
public int PlayerNumber
{
get { return _playerNumber; }
set { _playerNumber = value; }
}
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
public void SetJoyInput(string key, bool isPressed)
{
var pos = GetBitPos(key);
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
public void SetJoyInput(string key, bool isPressed)
{
var pos = GetBitPos(key);
if (isPressed)
{
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true);
}
else
{
if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]))
{
// key is already pressed elswhere - leave it as is
}
else
{
// key is safe to unpress
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false);
}
}
}
if (isPressed)
{
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true);
}
else
{
if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]))
{
// key is already pressed elswhere - leave it as is
}
else
{
// key is safe to unpress
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false);
}
}
}
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
public bool GetJoyInput(string key)
{
var pos = GetBitPos(key);
if (_machine == null)
return false;
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
public bool GetJoyInput(string key)
{
var pos = GetBitPos(key);
if (_machine == null)
return false;
return _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]);
}
#endregion
return _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]);
}
/// <summary>
/// Gets the bit position of a particular joystick binding from the matrix
/// </summary>
public int GetBitPos(string key)
{
int index = Array.IndexOf(ButtonCollection, key);
return index;
}
}
#endregion
/// <summary>
/// Gets the bit position of a particular joystick binding from the matrix
/// </summary>
public int GetBitPos(string key)
{
int index = Array.IndexOf(ButtonCollection, key);
return index;
}
}
}

View File

@ -3,103 +3,103 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Sinclair Joystick RIGHT
/// Just maps to the standard keyboard and is read the same (from port 0xeffe)
/// </summary>
public class SinclairJoystick2 : IJoystick
{
//private int _joyLine;
private SpectrumBase _machine;
/// <summary>
/// Sinclair Joystick RIGHT
/// Just maps to the standard keyboard and is read the same (from port 0xeffe)
/// </summary>
public class SinclairJoystick2 : IJoystick
{
//private int _joyLine;
private SpectrumBase _machine;
#region Construction
#region Construction
public SinclairJoystick2(SpectrumBase machine, int playerNumber)
{
_machine = machine;
//_joyLine = 0;
_playerNumber = playerNumber;
public SinclairJoystick2(SpectrumBase machine, int playerNumber)
{
_machine = machine;
//_joyLine = 0;
_playerNumber = playerNumber;
ButtonCollection = new List<string>
{
"P" + _playerNumber + " Left",
"P" + _playerNumber + " Right",
"P" + _playerNumber + " Down",
"P" + _playerNumber + " Up",
"P" + _playerNumber + " Button",
}.ToArray();
}
ButtonCollection = new List<string>
{
"P" + _playerNumber + " Left",
"P" + _playerNumber + " Right",
"P" + _playerNumber + " Down",
"P" + _playerNumber + " Up",
"P" + _playerNumber + " Button",
}.ToArray();
}
private List<string> btnLookups = new List<string>
{
"Key 6", // left
private List<string> btnLookups = new List<string>
{
"Key 6", // left
"Key 7", // right
"Key 8", // down
"Key 9", // up
"Key 0", // fire
};
#endregion
#endregion
#region IJoystick
#region IJoystick
public JoystickType JoyType => JoystickType.SinclairRIGHT;
public JoystickType JoyType => JoystickType.SinclairRIGHT;
public string[] ButtonCollection { get; set; }
public string[] ButtonCollection { get; set; }
private int _playerNumber;
public int PlayerNumber
{
get { return _playerNumber; }
set { _playerNumber = value; }
}
private int _playerNumber;
public int PlayerNumber
{
get { return _playerNumber; }
set { _playerNumber = value; }
}
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
public void SetJoyInput(string key, bool isPressed)
{
var pos = GetBitPos(key);
/// <summary>
/// Sets the joystick line based on key pressed
/// </summary>
public void SetJoyInput(string key, bool isPressed)
{
var pos = GetBitPos(key);
if (isPressed)
{
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true);
}
else
{
if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]))
{
// key is already pressed elswhere - leave it as is
}
else
{
// key is safe to unpress
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false);
}
}
}
if (isPressed)
{
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true);
}
else
{
if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]))
{
// key is already pressed elswhere - leave it as is
}
else
{
// key is safe to unpress
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false);
}
}
}
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
public bool GetJoyInput(string key)
{
var pos = GetBitPos(key);
if (_machine == null)
return false;
/// <summary>
/// Gets the state of a particular joystick binding
/// </summary>
public bool GetJoyInput(string key)
{
var pos = GetBitPos(key);
if (_machine == null)
return false;
return _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]);
}
#endregion
return _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]);
}
/// <summary>
/// Gets the bit position of a particular joystick binding from the matrix
/// </summary>
public int GetBitPos(string key)
{
int index = Array.IndexOf(ButtonCollection, key);
return index;
}
}
#endregion
/// <summary>
/// Gets the bit position of a particular joystick binding from the matrix
/// </summary>
public int GetBitPos(string key)
{
int index = Array.IndexOf(ButtonCollection, key);
return index;
}
}
}

View File

@ -5,48 +5,48 @@ using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// The 48k keyboard device
/// </summary>
public class StandardKeyboard : IKeyboard
{
public SpectrumBase _machine { get; set; }
private byte[] LineStatus;
private string[] _keyboardMatrix;
private int[] _keyLine;
private bool _isIssue2Keyboard;
private string[] _nonMatrixKeys;
/// <summary>
/// The 48k keyboard device
/// </summary>
public class StandardKeyboard : IKeyboard
{
public SpectrumBase _machine { get; set; }
private byte[] LineStatus;
private string[] _keyboardMatrix;
private int[] _keyLine;
private bool _isIssue2Keyboard;
private string[] _nonMatrixKeys;
public bool IsIssue2Keyboard
{
get { return _isIssue2Keyboard; }
set { _isIssue2Keyboard = value; }
}
public bool IsIssue2Keyboard
{
get { return _isIssue2Keyboard; }
set { _isIssue2Keyboard = value; }
}
public int[] KeyLine
{
get { return _keyLine; }
set { _keyLine = value; }
}
public int[] KeyLine
{
get { return _keyLine; }
set { _keyLine = value; }
}
public string[] KeyboardMatrix
{
get { return _keyboardMatrix; }
set { _keyboardMatrix = value; }
}
public string[] KeyboardMatrix
{
get { return _keyboardMatrix; }
set { _keyboardMatrix = value; }
}
public string[] NonMatrixKeys
{
get { return _nonMatrixKeys; }
set { _nonMatrixKeys = value; }
}
public string[] NonMatrixKeys
{
get { return _nonMatrixKeys; }
set { _nonMatrixKeys = value; }
}
public StandardKeyboard(SpectrumBase machine)
{
_machine = machine;
public StandardKeyboard(SpectrumBase machine)
{
_machine = machine;
KeyboardMatrix = new string[]
{
KeyboardMatrix = new string[]
{
// 0xfefe - 0 - 4
"Key Caps Shift", "Key Z", "Key X", "Key C", "Key V",
// 0xfdfe - 5 - 9
@ -63,134 +63,134 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
"Key Return", "Key L", "Key K", "Key J", "Key H",
// 0x7ffe - 35 - 39
"Key Space", "Key Symbol Shift", "Key M", "Key N", "Key B"
};
};
var nonMatrix = new List<string>();
foreach (var key in _machine.Spectrum.ZXSpectrumControllerDefinition.BoolButtons)
{
if (!KeyboardMatrix.Any(s => s == key))
nonMatrix.Add(key);
}
NonMatrixKeys = nonMatrix.ToArray();
var nonMatrix = new List<string>();
LineStatus = new byte[8];
_keyLine = new int[] { 255, 255, 255, 255, 255, 255, 255, 255 };
IsIssue2Keyboard = true;
}
foreach (var key in _machine.Spectrum.ZXSpectrumControllerDefinition.BoolButtons)
{
if (!KeyboardMatrix.Any(s => s == key))
nonMatrix.Add(key);
}
public void SetKeyStatus(string key, bool isPressed)
{
int k = GetByteFromKeyMatrix(key);
if (k != 255)
{
var lineIndex = k / 5;
var lineMask = 1 << k % 5;
NonMatrixKeys = nonMatrix.ToArray();
_keyLine[lineIndex] = isPressed
? (byte)(_keyLine[lineIndex] & ~lineMask)
: (byte)(_keyLine[lineIndex] | lineMask);
}
LineStatus = new byte[8];
_keyLine = new int[] { 255, 255, 255, 255, 255, 255, 255, 255 };
IsIssue2Keyboard = true;
}
// Combination keys that are not in the keyboard matrix
// but are available on the Spectrum+, 128k +2 & +3
// (GetByteFromKeyMatrix() should return 255)
// Processed after the matrix keys - only presses handled (unpressed get done above)
if (k == 255)
{
if (isPressed)
{
switch (key)
{
// Delete key (simulates Caps Shift + 0)
case "Key Delete":
_keyLine[0] = _keyLine[0] & ~(0x1);
_keyLine[4] = _keyLine[4] & ~(0x1);
break;
// Cursor left (simulates Caps Shift + 5)
case "Key Left Cursor":
_keyLine[0] = _keyLine[0] & ~(0x1);
_keyLine[3] = _keyLine[3] & ~(0x10);
break;
// Cursor right (simulates Caps Shift + 8)
case "Key Right Cursor":
_keyLine[0] = _keyLine[0] & ~(0x1);
_keyLine[4] = _keyLine[4] & ~(0x04);
break;
// Cursor up (simulates Caps Shift + 7)
case "Key Up Cursor":
_keyLine[0] = _keyLine[0] & ~(0x1);
_keyLine[4] = _keyLine[4] & ~(0x08);
break;
// Cursor down (simulates Caps Shift + 6)
case "Key Down Cursor":
_keyLine[0] = _keyLine[0] & ~(0x1);
_keyLine[4] = _keyLine[4] & ~(0x10);
break;
}
}
}
}
public void SetKeyStatus(string key, bool isPressed)
{
int k = GetByteFromKeyMatrix(key);
public bool GetKeyStatus(string key)
{
byte keyByte = GetByteFromKeyMatrix(key);
var lineIndex = keyByte / 5;
var lineMask = 1 << keyByte % 5;
if (k != 255)
{
var lineIndex = k / 5;
var lineMask = 1 << k % 5;
return (_keyLine[lineIndex] & lineMask) == 0;
}
_keyLine[lineIndex] = isPressed
? (byte)(_keyLine[lineIndex] & ~lineMask)
: (byte)(_keyLine[lineIndex] | lineMask);
}
public void ResetLineStatus()
{
lock (this)
{
for (int i = 0; i < KeyLine.Length; i++)
KeyLine[i] = 255;
}
}
// Combination keys that are not in the keyboard matrix
// but are available on the Spectrum+, 128k +2 & +3
// (GetByteFromKeyMatrix() should return 255)
// Processed after the matrix keys - only presses handled (unpressed get done above)
if (k == 255)
{
if (isPressed)
{
switch (key)
{
// Delete key (simulates Caps Shift + 0)
case "Key Delete":
_keyLine[0] = _keyLine[0] & ~(0x1);
_keyLine[4] = _keyLine[4] & ~(0x1);
break;
// Cursor left (simulates Caps Shift + 5)
case "Key Left Cursor":
_keyLine[0] = _keyLine[0] & ~(0x1);
_keyLine[3] = _keyLine[3] & ~(0x10);
break;
// Cursor right (simulates Caps Shift + 8)
case "Key Right Cursor":
_keyLine[0] = _keyLine[0] & ~(0x1);
_keyLine[4] = _keyLine[4] & ~(0x04);
break;
// Cursor up (simulates Caps Shift + 7)
case "Key Up Cursor":
_keyLine[0] = _keyLine[0] & ~(0x1);
_keyLine[4] = _keyLine[4] & ~(0x08);
break;
// Cursor down (simulates Caps Shift + 6)
case "Key Down Cursor":
_keyLine[0] = _keyLine[0] & ~(0x1);
_keyLine[4] = _keyLine[4] & ~(0x10);
break;
}
}
}
}
public byte GetLineStatus(byte lines)
{
lock(this)
{
byte status = 0;
lines = (byte)~lines;
var lineIndex = 0;
while (lines > 0)
{
if ((lines & 0x01) != 0)
status |= (byte)_keyLine[lineIndex];
lineIndex++;
lines >>= 1;
}
var result = (byte)status;
public bool GetKeyStatus(string key)
{
byte keyByte = GetByteFromKeyMatrix(key);
var lineIndex = keyByte / 5;
var lineMask = 1 << keyByte % 5;
return result;
}
}
return (_keyLine[lineIndex] & lineMask) == 0;
}
public byte ReadKeyboardByte(ushort addr)
{
return GetLineStatus((byte)(addr >> 8));
}
public void ResetLineStatus()
{
lock (this)
{
for (int i = 0; i < KeyLine.Length; i++)
KeyLine[i] = 255;
}
}
public byte GetByteFromKeyMatrix(string key)
{
int index = Array.IndexOf(KeyboardMatrix, key);
return (byte)index;
}
public byte GetLineStatus(byte lines)
{
lock (this)
{
byte status = 0;
lines = (byte)~lines;
var lineIndex = 0;
while (lines > 0)
{
if ((lines & 0x01) != 0)
status |= (byte)_keyLine[lineIndex];
lineIndex++;
lines >>= 1;
}
var result = (byte)status;
#region IPortIODevice
return result;
}
}
/// <summary>
/// Device responds to an IN instruction
/// </summary>
public bool ReadPort(ushort port, ref int result)
{
/*
public byte ReadKeyboardByte(ushort addr)
{
return GetLineStatus((byte)(addr >> 8));
}
public byte GetByteFromKeyMatrix(string key)
{
int index = Array.IndexOf(KeyboardMatrix, key);
return (byte)index;
}
#region IPortIODevice
/// <summary>
/// Device responds to an IN instruction
/// </summary>
public bool ReadPort(ushort port, ref int result)
{
/*
The high byte indicates which half-row of keys is being polled
A zero on one of these lines selects a particular half-row of five keys:
@ -206,75 +206,75 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
(for instance by XOR A/IN A,(FE)) is one, no key is pressed
*/
if ((port & 0x0001) != 0)
return false;
if ((port & 0x0001) != 0)
return false;
if ((port & 0x8000) == 0)
{
result &= KeyLine[7];
}
if ((port & 0x8000) == 0)
{
result &= KeyLine[7];
}
if ((port & 0x4000) == 0)
{
result &= KeyLine[6];
}
if ((port & 0x4000) == 0)
{
result &= KeyLine[6];
}
if ((port & 0x2000) == 0)
{
result &= KeyLine[5];
}
if ((port & 0x2000) == 0)
{
result &= KeyLine[5];
}
if ((port & 0x1000) == 0)
{
result &= KeyLine[4];
}
if ((port & 0x1000) == 0)
{
result &= KeyLine[4];
}
if ((port & 0x800) == 0)
{
result &= KeyLine[3];
}
if ((port & 0x800) == 0)
{
result &= KeyLine[3];
}
if ((port & 0x400) == 0)
{
result &= KeyLine[2];
}
if ((port & 0x400) == 0)
{
result &= KeyLine[2];
}
if ((port & 0x200) == 0)
{
result &= KeyLine[1];
}
if ((port & 0x200) == 0)
{
result &= KeyLine[1];
}
if ((port & 0x100) == 0)
{
result &= KeyLine[0];
}
if ((port & 0x100) == 0)
{
result &= KeyLine[0];
}
// mask out lower 4 bits
result = result & 0x1f;
// mask out lower 4 bits
result = result & 0x1f;
// set bit 5 & 7 to 1
result = result | 0xa0;
// set bit 5 & 7 to 1
result = result | 0xa0;
return true;
}
return true;
}
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
public bool WritePort(ushort port, int result)
{
// not implemented
return false;
}
/// <summary>
/// Device responds to an OUT instruction
/// </summary>
public bool WritePort(ushort port, int result)
{
// not implemented
return false;
}
#endregion
#endregion
public void SyncState(Serializer ser)
{
ser.BeginSection("Keyboard");
ser.Sync(nameof(LineStatus), ref LineStatus, false);
ser.Sync(nameof(_keyLine), ref _keyLine, false);
ser.EndSection();
}
}
public void SyncState(Serializer ser)
{
ser.BeginSection("Keyboard");
ser.Sync(nameof(LineStatus), ref LineStatus, false);
ser.Sync(nameof(_keyLine), ref _keyLine, false);
ser.EndSection();
}
}
}

View File

@ -1,83 +1,83 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Information about spectrum ROM
/// </summary>
public class RomData
{
/// <summary>
/// ROM Contents
/// </summary>
public byte[] RomBytes
{
get { return _romBytes; }
set { _romBytes = value; }
}
/// <summary>
/// Information about spectrum ROM
/// </summary>
public class RomData
{
/// <summary>
/// ROM Contents
/// </summary>
public byte[] RomBytes
{
get { return _romBytes; }
set { _romBytes = value; }
}
/// <summary>
/// Useful ROM addresses that are needed during tape operations
/// </summary>
public ushort SaveBytesRoutineAddress
{
get { return _saveBytesRoutineAddress; }
set { _saveBytesRoutineAddress = value; }
}
public ushort LoadBytesRoutineAddress
{
get { return _loadBytesRoutineAddress; }
set { _loadBytesRoutineAddress = value; }
}
public ushort SaveBytesResumeAddress
{
get { return _saveBytesResumeAddress; }
set { _saveBytesResumeAddress = value; }
}
public ushort LoadBytesResumeAddress
{
get { return _loadBytesResumeAddress; }
set { _loadBytesResumeAddress = value; }
}
public ushort LoadBytesInvalidHeaderAddress
{
get { return _loadBytesInvalidHeaderAddress; }
set { _loadBytesInvalidHeaderAddress = value; }
}
/// <summary>
/// Useful ROM addresses that are needed during tape operations
/// </summary>
public ushort SaveBytesRoutineAddress
{
get { return _saveBytesRoutineAddress; }
set { _saveBytesRoutineAddress = value; }
}
public ushort LoadBytesRoutineAddress
{
get { return _loadBytesRoutineAddress; }
set { _loadBytesRoutineAddress = value; }
}
public ushort SaveBytesResumeAddress
{
get { return _saveBytesResumeAddress; }
set { _saveBytesResumeAddress = value; }
}
public ushort LoadBytesResumeAddress
{
get { return _loadBytesResumeAddress; }
set { _loadBytesResumeAddress = value; }
}
public ushort LoadBytesInvalidHeaderAddress
{
get { return _loadBytesInvalidHeaderAddress; }
set { _loadBytesInvalidHeaderAddress = value; }
}
private byte[] _romBytes;
private ushort _saveBytesRoutineAddress;
private ushort _loadBytesRoutineAddress;
private ushort _saveBytesResumeAddress;
private ushort _loadBytesResumeAddress;
private ushort _loadBytesInvalidHeaderAddress;
private byte[] _romBytes;
private ushort _saveBytesRoutineAddress;
private ushort _loadBytesRoutineAddress;
private ushort _saveBytesResumeAddress;
private ushort _loadBytesResumeAddress;
private ushort _loadBytesInvalidHeaderAddress;
public static RomData InitROM(MachineType machineType, byte[] rom)
{
RomData RD = new RomData();
RD.RomBytes = new byte[rom.Length];
RD.RomBytes = rom;
public static RomData InitROM(MachineType machineType, byte[] rom)
{
RomData RD = new RomData();
RD.RomBytes = new byte[rom.Length];
RD.RomBytes = rom;
switch (machineType)
{
case MachineType.ZXSpectrum48:
RD.SaveBytesRoutineAddress = 0x04C2;
RD.SaveBytesResumeAddress = 0x0000;
RD.LoadBytesRoutineAddress = 0x0808; //0x0556; //0x056C;
RD.LoadBytesResumeAddress = 0x05E2;
RD.LoadBytesInvalidHeaderAddress = 0x05B6;
break;
switch (machineType)
{
case MachineType.ZXSpectrum48:
RD.SaveBytesRoutineAddress = 0x04C2;
RD.SaveBytesResumeAddress = 0x0000;
RD.LoadBytesRoutineAddress = 0x0808; //0x0556; //0x056C;
RD.LoadBytesResumeAddress = 0x05E2;
RD.LoadBytesInvalidHeaderAddress = 0x05B6;
break;
case MachineType.ZXSpectrum128:
RD.SaveBytesRoutineAddress = 0x04C2;
RD.SaveBytesResumeAddress = 0x0000;
RD.LoadBytesRoutineAddress = 0x0808; //0x0556; //0x056C;
RD.LoadBytesResumeAddress = 0x05E2;
RD.LoadBytesInvalidHeaderAddress = 0x05B6;
break;
}
case MachineType.ZXSpectrum128:
RD.SaveBytesRoutineAddress = 0x04C2;
RD.SaveBytesResumeAddress = 0x0000;
RD.LoadBytesRoutineAddress = 0x0808; //0x0556; //0x056C;
RD.LoadBytesResumeAddress = 0x05E2;
RD.LoadBytesInvalidHeaderAddress = 0x05B6;
break;
}
return RD;
}
}
return RD;
}
}
}

View File

@ -3,402 +3,402 @@ using BizHawk.Emulation.Cores.Components.Z80A;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// An intermediary class that manages cycling the ULA and CPU
/// along with inherent Port and Memory contention
/// </summary>
public class CPUMonitor
{
#region Devices
/// <summary>
/// An intermediary class that manages cycling the ULA and CPU
/// along with inherent Port and Memory contention
/// </summary>
public class CPUMonitor
{
#region Devices
private SpectrumBase _machine;
private Z80A _cpu;
public MachineType machineType = MachineType.ZXSpectrum48;
private SpectrumBase _machine;
private Z80A _cpu;
public MachineType machineType = MachineType.ZXSpectrum48;
#endregion
#endregion
#region Lookups
#region Lookups
/// <summary>
/// CPU total executes t-states
/// </summary>
public long TotalExecutedCycles => _cpu.TotalExecutedCycles;
/// <summary>
/// CPU total executes t-states
/// </summary>
public long TotalExecutedCycles => _cpu.TotalExecutedCycles;
/// <summary>
/// Current BUSRQ line array
/// </summary>
public ushort BUSRQ
{
get
{
switch (machineType)
{
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
return _cpu.MEMRQ[_cpu.bus_pntr];
default:
/// <summary>
/// Current BUSRQ line array
/// </summary>
public ushort BUSRQ
{
get
{
switch (machineType)
{
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
return _cpu.MEMRQ[_cpu.bus_pntr];
default:
return _cpu.BUSRQ[_cpu.mem_pntr];
}
}
}
}
}
}
#endregion
#endregion
#region Construction
#region Construction
public CPUMonitor(SpectrumBase machine)
{
_machine = machine;
_cpu = _machine.CPU;
}
public CPUMonitor(SpectrumBase machine)
{
_machine = machine;
_cpu = _machine.CPU;
}
#endregion
#endregion
#region State
#region State
/// <summary>
/// The last 16-bit port address that was detected
/// </summary>
public ushort lastPortAddr;
/// <summary>
/// The last 16-bit port address that was detected
/// </summary>
public ushort lastPortAddr;
/// <summary>
/// If true, the next read memory operation has been contended
/// </summary>
public bool NextMemReadContended;
/// <summary>
/// If true, the next read memory operation has been contended
/// </summary>
public bool NextMemReadContended;
#endregion
#endregion
#region Methods
#region Methods
/// <summary>
/// Handles the ULA and CPU cycle clocks, along with any memory and port contention
/// </summary>
public void ExecuteCycle()
{
// simulate the ULA clock cycle before the CPU cycle
_machine.ULADevice.CycleClock(TotalExecutedCycles);
/// <summary>
/// Handles the ULA and CPU cycle clocks, along with any memory and port contention
/// </summary>
public void ExecuteCycle()
{
// simulate the ULA clock cycle before the CPU cycle
_machine.ULADevice.CycleClock(TotalExecutedCycles);
// is the next CPU cycle causing a BUSRQ or IORQ?
if (BUSRQ > 0)
{
// check for IORQ
if (!CheckIO())
{
// is the memory address of the BUSRQ potentially contended?
if (_machine.IsContended(AscertainBUSRQAddress()))
{
var cont = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle);
if (cont > 0)
{
_cpu.TotalExecutedCycles += cont;
NextMemReadContended = true;
}
}
}
}
// is the next CPU cycle causing a BUSRQ or IORQ?
if (BUSRQ > 0)
{
// check for IORQ
if (!CheckIO())
{
// is the memory address of the BUSRQ potentially contended?
if (_machine.IsContended(AscertainBUSRQAddress()))
{
var cont = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle);
if (cont > 0)
{
_cpu.TotalExecutedCycles += cont;
NextMemReadContended = true;
}
}
}
}
_cpu.ExecuteOne();
}
}
/// <summary>
/// Looks up the current BUSRQ address that is about to be signalled on the upcoming cycle
/// </summary>
private ushort AscertainBUSRQAddress()
{
ushort addr = 0;
switch (BUSRQ)
{
// PCh
case 1:
addr = (ushort)(_cpu.Regs[_cpu.PCl] | _cpu.Regs[_cpu.PCh] << 8);
break;
// SPh
case 3:
addr = (ushort)(_cpu.Regs[_cpu.SPl] | _cpu.Regs[_cpu.SPh] << 8);
break;
// A
case 4:
addr = (ushort)(_cpu.Regs[_cpu.F] | _cpu.Regs[_cpu.A] << 8);
break;
// B
case 6:
addr = (ushort)(_cpu.Regs[_cpu.C] | _cpu.Regs[_cpu.B] << 8);
break;
// D
case 8:
addr = (ushort)(_cpu.Regs[_cpu.E] | _cpu.Regs[_cpu.D] << 8);
break;
// H
case 10:
addr = (ushort)(_cpu.Regs[_cpu.L] | _cpu.Regs[_cpu.H] << 8);
break;
// W
case 12:
addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8);
break;
// Ixh
case 16:
addr = (ushort)(_cpu.Regs[_cpu.Ixl] | _cpu.Regs[_cpu.Ixh] << 8);
break;
// Iyh
case 18:
addr = (ushort)(_cpu.Regs[_cpu.Iyl] | _cpu.Regs[_cpu.Iyh] << 8);
break;
// I
case 21:
addr = (ushort)(_cpu.Regs[_cpu.R] | _cpu.Regs[_cpu.I] << 8);
break;
// BC
case Z80A.BIO1:
case Z80A.BIO2:
case Z80A.BIO3:
case Z80A.BIO4:
addr = (ushort)(_cpu.Regs[_cpu.C] | _cpu.Regs[_cpu.B] << 8);
break;
// WZ
case Z80A.WIO1:
case Z80A.WIO2:
case Z80A.WIO3:
case Z80A.WIO4:
addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8);
break;
}
/// <summary>
/// Looks up the current BUSRQ address that is about to be signalled on the upcoming cycle
/// </summary>
private ushort AscertainBUSRQAddress()
{
ushort addr = 0;
switch (BUSRQ)
{
// PCh
case 1:
addr = (ushort)(_cpu.Regs[_cpu.PCl] | _cpu.Regs[_cpu.PCh] << 8);
break;
// SPh
case 3:
addr = (ushort)(_cpu.Regs[_cpu.SPl] | _cpu.Regs[_cpu.SPh] << 8);
break;
// A
case 4:
addr = (ushort)(_cpu.Regs[_cpu.F] | _cpu.Regs[_cpu.A] << 8);
break;
// B
case 6:
addr = (ushort)(_cpu.Regs[_cpu.C] | _cpu.Regs[_cpu.B] << 8);
break;
// D
case 8:
addr = (ushort)(_cpu.Regs[_cpu.E] | _cpu.Regs[_cpu.D] << 8);
break;
// H
case 10:
addr = (ushort)(_cpu.Regs[_cpu.L] | _cpu.Regs[_cpu.H] << 8);
break;
// W
case 12:
addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8);
break;
// Ixh
case 16:
addr = (ushort)(_cpu.Regs[_cpu.Ixl] | _cpu.Regs[_cpu.Ixh] << 8);
break;
// Iyh
case 18:
addr = (ushort)(_cpu.Regs[_cpu.Iyl] | _cpu.Regs[_cpu.Iyh] << 8);
break;
// I
case 21:
addr = (ushort)(_cpu.Regs[_cpu.R] | _cpu.Regs[_cpu.I] << 8);
break;
// BC
case Z80A.BIO1:
case Z80A.BIO2:
case Z80A.BIO3:
case Z80A.BIO4:
addr = (ushort)(_cpu.Regs[_cpu.C] | _cpu.Regs[_cpu.B] << 8);
break;
// WZ
case Z80A.WIO1:
case Z80A.WIO2:
case Z80A.WIO3:
case Z80A.WIO4:
addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8);
break;
}
return addr;
}
return addr;
}
/// <summary>
/// Running every cycle, this determines whether the upcoming BUSRQ is for an IO operation
/// Also processes any contention
/// </summary>
private bool CheckIO()
{
bool isIO = false;
/// <summary>
/// Running every cycle, this determines whether the upcoming BUSRQ is for an IO operation
/// Also processes any contention
/// </summary>
private bool CheckIO()
{
bool isIO = false;
switch (BUSRQ)
{
// BC: T1
case Z80A.BIO1:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(1))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// BC: T2
case Z80A.BIO2:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(2))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// BC: T3
case Z80A.BIO3:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(3))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// BC: T4
case Z80A.BIO4:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(4))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
switch (BUSRQ)
{
// BC: T1
case Z80A.BIO1:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(1))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// BC: T2
case Z80A.BIO2:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(2))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// BC: T3
case Z80A.BIO3:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(3))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// BC: T4
case Z80A.BIO4:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(4))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// WZ: T1
case Z80A.WIO1:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(1))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// WZ: T2
case Z80A.WIO2:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(2))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// WZ: T3
case Z80A.WIO3:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(3))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// WZ: T4
case Z80A.WIO4:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(4))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
}
// WZ: T1
case Z80A.WIO1:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(1))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// WZ: T2
case Z80A.WIO2:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(2))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// WZ: T3
case Z80A.WIO3:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(3))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
// WZ: T4
case Z80A.WIO4:
lastPortAddr = AscertainBUSRQAddress();
isIO = true;
if (IsIOCycleContended(4))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle);
break;
}
return isIO;
}
return isIO;
}
/// <summary>
/// Returns TRUE if the supplied T-cycle within an IO operation has the possibility of being contended
/// This can be different based on the emulated ZX Spectrum model
/// </summary>
private bool IsIOCycleContended(int T)
{
bool lowBitSet = (lastPortAddr & 0x0001) != 0;
bool highByte407f = false;
/// <summary>
/// Returns TRUE if the supplied T-cycle within an IO operation has the possibility of being contended
/// This can be different based on the emulated ZX Spectrum model
/// </summary>
private bool IsIOCycleContended(int T)
{
bool lowBitSet = (lastPortAddr & 0x0001) != 0;
bool highByte407f = false;
switch (machineType)
{
case MachineType.ZXSpectrum16:
case MachineType.ZXSpectrum48:
switch (machineType)
{
case MachineType.ZXSpectrum16:
case MachineType.ZXSpectrum48:
if ((lastPortAddr & 0xc000) == 0x4000)
highByte407f = true;
if ((lastPortAddr & 0xc000) == 0x4000)
highByte407f = true;
if (highByte407f)
{
// high byte 40-7f
if (lowBitSet)
{
// high byte 40-7f
// low bit set
// C:1, C:1, C:1, C:1
switch (T)
{
case 1:
case 2:
case 3:
case 4:
return true;
}
}
else
{
// high byte 40-7f
// low bit reset
// C:1, C:3
switch (T)
{
case 1:
case 2:
return true;
}
}
}
else
{
// high byte not 40-7f
if (lowBitSet)
{
// high byte not 40-7f
// low bit set
// N:4
}
else
{
// high byte not 40-7f
// low bit reset
// N:1, C:3
switch (T)
{
case 2:
return true;
}
}
}
break;
if (highByte407f)
{
// high byte 40-7f
if (lowBitSet)
{
// high byte 40-7f
// low bit set
// C:1, C:1, C:1, C:1
switch (T)
{
case 1:
case 2:
case 3:
case 4:
return true;
}
}
else
{
// high byte 40-7f
// low bit reset
// C:1, C:3
switch (T)
{
case 1:
case 2:
return true;
}
}
}
else
{
// high byte not 40-7f
if (lowBitSet)
{
// high byte not 40-7f
// low bit set
// N:4
}
else
{
// high byte not 40-7f
// low bit reset
// N:1, C:3
switch (T)
{
case 2:
return true;
}
}
}
break;
case MachineType.ZXSpectrum128:
case MachineType.ZXSpectrum128Plus2:
if ((lastPortAddr & 0xc000) == 0x4000 || (lastPortAddr & 0xc000) == 0xc000 && _machine.ContendedBankPaged())
highByte407f = true;
case MachineType.ZXSpectrum128:
case MachineType.ZXSpectrum128Plus2:
if ((lastPortAddr & 0xc000) == 0x4000 || (lastPortAddr & 0xc000) == 0xc000 && _machine.ContendedBankPaged())
highByte407f = true;
if (highByte407f)
{
// high byte 40-7f
if (lowBitSet)
{
// high byte 40-7f
// low bit set
// C:1, C:1, C:1, C:1
switch (T)
{
case 1:
case 2:
case 3:
case 4:
return true;
}
}
else
{
// high byte 40-7f
// low bit reset
// C:1, C:3
switch (T)
{
case 1:
case 2:
return true;
}
}
}
else
{
// high byte not 40-7f
if (lowBitSet)
{
// high byte not 40-7f
// low bit set
// N:4
}
else
{
// high byte not 40-7f
// low bit reset
// N:1, C:3
switch (T)
{
case 2:
return true;
}
}
}
break;
if (highByte407f)
{
// high byte 40-7f
if (lowBitSet)
{
// high byte 40-7f
// low bit set
// C:1, C:1, C:1, C:1
switch (T)
{
case 1:
case 2:
case 3:
case 4:
return true;
}
}
else
{
// high byte 40-7f
// low bit reset
// C:1, C:3
switch (T)
{
case 1:
case 2:
return true;
}
}
}
else
{
// high byte not 40-7f
if (lowBitSet)
{
// high byte not 40-7f
// low bit set
// N:4
}
else
{
// high byte not 40-7f
// low bit reset
// N:1, C:3
switch (T)
{
case 2:
return true;
}
}
}
break;
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
// No contention occurs as the ULA only applies contention when the Z80 MREQ line is active
// (which is not during an IO operation)
break;
}
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
// No contention occurs as the ULA only applies contention when the Z80 MREQ line is active
// (which is not during an IO operation)
break;
}
return false;
}
return false;
}
/// <summary>
/// Called when the first byte of an instruction is fetched
/// </summary>
public void OnExecFetch(ushort firstByte)
{
// fetch instruction without incrementing pc
//_cpu.FetchInstruction(_cpu.FetchMemory(firstByte));
}
/// <summary>
/// Called when the first byte of an instruction is fetched
/// </summary>
public void OnExecFetch(ushort firstByte)
{
// fetch instruction without incrementing pc
//_cpu.FetchInstruction(_cpu.FetchMemory(firstByte));
#endregion
}
#region Serialization
#endregion
public void SyncState(Serializer ser)
{
ser.BeginSection(nameof(CPUMonitor));
ser.Sync(nameof(lastPortAddr), ref lastPortAddr);
ser.Sync(nameof(NextMemReadContended), ref NextMemReadContended);
ser.EndSection();
}
#region Serialization
#endregion
}
public void SyncState(Serializer ser)
{
ser.BeginSection(nameof(CPUMonitor));
ser.Sync(nameof(lastPortAddr), ref lastPortAddr);
ser.Sync(nameof(NextMemReadContended), ref NextMemReadContended);
ser.EndSection();
}
#endregion
}
}

View File

@ -1,44 +1,44 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// The various spectrum models ZXHawk emulates
/// </summary>
public enum MachineType
{
/// <summary>
/// Original Sinclair Spectrum 16K model
/// </summary>
ZXSpectrum16,
/// <summary>
/// The various spectrum models ZXHawk emulates
/// </summary>
public enum MachineType
{
/// <summary>
/// Original Sinclair Spectrum 16K model
/// </summary>
ZXSpectrum16,
/// <summary>
/// Sinclair Spectrum 48K model
/// </summary>
ZXSpectrum48,
/// <summary>
/// Sinclair Spectrum 48K model
/// </summary>
ZXSpectrum48,
/// <summary>
/// Sinclair Spectrum 128K model
/// </summary>
ZXSpectrum128,
/// <summary>
/// Sinclair Spectrum 128K model
/// </summary>
ZXSpectrum128,
/// <summary>
/// Sinclair Spectrum 128 +2 model
/// </summary>
ZXSpectrum128Plus2,
/// <summary>
/// Sinclair Spectrum 128 +2 model
/// </summary>
ZXSpectrum128Plus2,
/// <summary>
/// Sinclair Spectrum 128 +2a model (same as the +3 just without disk drive)
/// </summary>
ZXSpectrum128Plus2a,
/// <summary>
/// Sinclair Spectrum 128 +2a model (same as the +3 just without disk drive)
/// </summary>
ZXSpectrum128Plus2a,
/// <summary>
/// Sinclair Spectrum 128 +3 model
/// </summary>
ZXSpectrum128Plus3,
/// <summary>
/// Sinclair Spectrum 128 +3 model
/// </summary>
ZXSpectrum128Plus3,
/// <summary>
/// Russian 128k pentagon clone
/// </summary>
Pentagon128,
}
}
}

View File

@ -1,48 +1,48 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// 128K/+2 ULA
/// </summary>
class ScreenPentagon128 : ULA
{
#region Construction
/// <summary>
/// 128K/+2 ULA
/// </summary>
class ScreenPentagon128 : ULA
{
#region Construction
public ScreenPentagon128(SpectrumBase machine)
public ScreenPentagon128(SpectrumBase machine)
: base(machine)
{
{
// interrupt
InterruptStartTime = 0;// 3;
InterruptLength = 32;
// offsets
RenderTableOffset = 58;
ContentionOffset = 6;
FloatingBusOffset = 1;
// timing
ClockSpeed = 3546900;
FrameCycleLength = 71680;
ScanlineTime = 224;
BorderLeftTime = 24;
BorderRightTime = 24;
FirstPaperLine = 80;
FirstPaperTState = 68;
// screen layout
Border4T = false;
Border4TStage = 1;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48; // 55; // 48;
BorderBottomHeight = 48; // 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
InterruptLength = 32;
// offsets
RenderTableOffset = 58;
ContentionOffset = 6;
FloatingBusOffset = 1;
// timing
ClockSpeed = 3546900;
FrameCycleLength = 71680;
ScanlineTime = 224;
BorderLeftTime = 24;
BorderRightTime = 24;
FirstPaperLine = 80;
FirstPaperTState = 68;
// screen layout
Border4T = false;
Border4TStage = 1;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48; // 55; // 48;
BorderBottomHeight = 48; // 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum128);
RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum128);
SetupScreenSize();
}
SetupScreenSize();
}
#endregion
}
#endregion
}
}

View File

@ -4,48 +4,48 @@ using BizHawk.Emulation.Cores.Sound;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// 128K Constructor
/// </summary>
public partial class Pentagon128 : SpectrumBase
{
#region Construction
/// <summary>
/// 128K Constructor
/// </summary>
public partial class Pentagon128 : SpectrumBase
{
#region Construction
/// <summary>
/// Main constructor
/// </summary>
public Pentagon128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{
Spectrum = spectrum;
CPU = cpu;
/// <summary>
/// Main constructor
/// </summary>
public Pentagon128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{
Spectrum = spectrum;
CPU = cpu;
CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.Pentagon128;
CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.Pentagon128;
ROMPaged = 0;
SHADOWPaged = false;
RAMPaged = 0;
PagingDisabled = false;
ULADevice = new ScreenPentagon128(this);
ROMPaged = 0;
SHADOWPaged = false;
RAMPaged = 0;
PagingDisabled = false;
ULADevice = new ScreenPentagon128(this);
BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer");
TapeBuzzer = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "TapeBuzzer");
TapeBuzzer = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "TapeBuzzer");
AYDevice = new AY38912(this);
AYDevice.Init(44100, ULADevice.FrameLength);
AYDevice = new AY38912(this);
AYDevice.Init(44100, ULADevice.FrameLength);
KeyboardDevice = new StandardKeyboard(this);
KeyboardDevice = new StandardKeyboard(this);
InitJoysticks(joysticks);
InitJoysticks(joysticks);
TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this);
TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this);
InitializeMedia(files);
}
InitializeMedia(files);
}
#endregion
}
#endregion
}
}

View File

@ -4,166 +4,166 @@ using BizHawk.Emulation.Cores.Sound;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Main properties / fields / contruction*
/// </summary>
public abstract partial class SpectrumBase
{
#region Devices
/// <summary>
/// The abstract class that all emulated models will inherit from
/// * Main properties / fields / contruction*
/// </summary>
public abstract partial class SpectrumBase
{
#region Devices
/// <summary>
/// The calling ZXSpectrum class (piped in via constructor)
/// </summary>
public ZXSpectrum Spectrum { get; set; }
/// <summary>
/// The calling ZXSpectrum class (piped in via constructor)
/// </summary>
public ZXSpectrum Spectrum { get; set; }
/// <summary>
/// Reference to the instantiated Z80 cpu (piped in via constructor)
/// </summary>
public Z80A CPU { get; set; }
/// <summary>
/// Reference to the instantiated Z80 cpu (piped in via constructor)
/// </summary>
public Z80A CPU { get; set; }
/// <summary>
/// ROM and extended info
/// </summary>
public RomData RomData { get; set; }
/// <summary>
/// ROM and extended info
/// </summary>
public RomData RomData { get; set; }
/// <summary>
/// The emulated ULA device
/// </summary>
//public ULABase ULADevice { get; set; }
public ULA ULADevice { get; set; }
/// <summary>
/// The emulated ULA device
/// </summary>
//public ULABase ULADevice { get; set; }
public ULA ULADevice { get; set; }
/// <summary>
/// Monitors CPU cycles
/// </summary>
public CPUMonitor CPUMon { get; set; }
/// <summary>
/// Monitors CPU cycles
/// </summary>
public CPUMonitor CPUMon { get; set; }
/// <summary>
/// The spectrum buzzer/beeper
/// </summary>
public OneBitBeeper BuzzerDevice { get; set; }
/// <summary>
/// The spectrum buzzer/beeper
/// </summary>
public OneBitBeeper BuzzerDevice { get; set; }
/// <summary>
/// A second beeper for the tape
/// </summary>
public OneBitBeeper TapeBuzzer { get; set; }
/// <summary>
/// A second beeper for the tape
/// </summary>
public OneBitBeeper TapeBuzzer { get; set; }
/// <summary>
/// Device representing the AY-3-8912 chip found in the 128k and up spectrums
/// </summary>
public IPSG AYDevice { get; set; }
/// <summary>
/// Device representing the AY-3-8912 chip found in the 128k and up spectrums
/// </summary>
public IPSG AYDevice { get; set; }
/// <summary>
/// The spectrum keyboard
/// </summary>
public virtual IKeyboard KeyboardDevice { get; set; }
/// <summary>
/// The spectrum keyboard
/// </summary>
public virtual IKeyboard KeyboardDevice { get; set; }
/// <summary>
/// The spectrum datacorder device
/// </summary>
public virtual DatacorderDevice TapeDevice { get; set; }
/// <summary>
/// The spectrum datacorder device
/// </summary>
public virtual DatacorderDevice TapeDevice { get; set; }
/// <summary>
/// The +3 built-in disk drive
/// </summary>
public virtual NECUPD765 UPDDiskDevice { get; set; }
/// <summary>
/// The +3 built-in disk drive
/// </summary>
public virtual NECUPD765 UPDDiskDevice { get; set; }
/// <summary>
/// Holds the currently selected joysticks
/// </summary>
public virtual IJoystick[] JoystickCollection { get; set; }
/// <summary>
/// Holds the currently selected joysticks
/// </summary>
public virtual IJoystick[] JoystickCollection { get; set; }
/// <summary>
/// +3/2a printer port strobe
/// </summary>
protected bool PrinterPortStrobe;
/// <summary>
/// +3/2a printer port strobe
/// </summary>
protected bool PrinterPortStrobe;
#endregion
#endregion
#region Emulator State
#region Emulator State
/// <summary>
/// Signs whether the frame has ended
/// </summary>
public bool FrameCompleted;
/// <summary>
/// Signs whether the frame has ended
/// </summary>
public bool FrameCompleted;
/// <summary>
/// Overflow from the previous frame (in Z80 cycles)
/// </summary>
public int OverFlow;
/// <summary>
/// Overflow from the previous frame (in Z80 cycles)
/// </summary>
public int OverFlow;
/// <summary>
/// The total number of frames rendered
/// </summary>
public int FrameCount;
/// <summary>
/// The total number of frames rendered
/// </summary>
public int FrameCount;
/// <summary>
/// The current cycle (T-State) that we are at in the frame
/// </summary>
public long _frameCycles;
/// <summary>
/// The current cycle (T-State) that we are at in the frame
/// </summary>
public long _frameCycles;
/// <summary>
/// Stores where we are in the frame after each CPU cycle
/// </summary>
public long LastFrameStartCPUTick;
/// <summary>
/// Stores where we are in the frame after each CPU cycle
/// </summary>
public long LastFrameStartCPUTick;
/// <summary>
/// Gets the current frame cycle according to the CPU tick count
/// </summary>
public virtual long CurrentFrameCycle => CPU.TotalExecutedCycles - LastFrameStartCPUTick;
/// <summary>
/// Gets the current frame cycle according to the CPU tick count
/// </summary>
public virtual long CurrentFrameCycle => CPU.TotalExecutedCycles - LastFrameStartCPUTick;
/// <summary>
/// Non-Deterministic bools
/// </summary>
public bool _render;
public bool _renderSound;
/// <summary>
/// Non-Deterministic bools
/// </summary>
public bool _render;
public bool _renderSound;
#endregion
#endregion
#region Constants
#region Constants
/// <summary>
/// Mask constants & misc
/// </summary>
protected const int BORDER_BIT = 0x07;
protected const int EAR_BIT = 0x10;
protected const int MIC_BIT = 0x08;
protected const int TAPE_BIT = 0x40;
protected const int AY_SAMPLE_RATE = 16;
/// <summary>
/// Mask constants & misc
/// </summary>
protected const int BORDER_BIT = 0x07;
protected const int EAR_BIT = 0x10;
protected const int MIC_BIT = 0x08;
protected const int TAPE_BIT = 0x40;
protected const int AY_SAMPLE_RATE = 16;
#endregion
#endregion
#region Emulation Loop
#region Emulation Loop
/// <summary>
/// Executes a single frame
/// </summary>
public virtual void ExecuteFrame(bool render, bool renderSound)
{
ULADevice.FrameEnd = false;
ULADevice.ULACycleCounter = CurrentFrameCycle;
/// <summary>
/// Executes a single frame
/// </summary>
public virtual void ExecuteFrame(bool render, bool renderSound)
{
ULADevice.FrameEnd = false;
ULADevice.ULACycleCounter = CurrentFrameCycle;
InputRead = false;
_render = render;
_renderSound = renderSound;
InputRead = false;
_render = render;
_renderSound = renderSound;
FrameCompleted = false;
FrameCompleted = false;
//if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
//TapeDevice.StartFrame();
//if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
//TapeDevice.StartFrame();
if (_renderSound)
{
if (AYDevice != null)
AYDevice.StartFrame();
}
if (_renderSound)
{
if (AYDevice != null)
AYDevice.StartFrame();
}
PollInput();
PollInput();
for (;;)
{
// run the CPU Monitor cycle
CPUMon.ExecuteCycle();
for (; ; )
{
// run the CPU Monitor cycle
CPUMon.ExecuteCycle();
// clock the beepers
TapeBuzzer.SetClock((int)CurrentFrameCycle);
@ -171,229 +171,229 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// cycle the tape device
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.TapeCycle();
TapeDevice.TapeCycle();
// has frame end been reached?
if (ULADevice.FrameEnd)
break;
}
// has frame end been reached?
if (ULADevice.FrameEnd)
break;
}
OverFlow = (int)CurrentFrameCycle - ULADevice.FrameLength;
OverFlow = (int)CurrentFrameCycle - ULADevice.FrameLength;
// we have reached the end of a frame
LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow;
// we have reached the end of a frame
LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow;
ULADevice.LastTState = 0;
ULADevice.LastTState = 0;
if (AYDevice != null)
AYDevice.EndFrame();
if (AYDevice != null)
AYDevice.EndFrame();
FrameCount++;
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.EndFrame();
FrameCount++;
FrameCompleted = true;
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.EndFrame();
// is this a lag frame?
Spectrum.IsLagFrame = !InputRead;
FrameCompleted = true;
// FDC debug
if (UPDDiskDevice != null && UPDDiskDevice.writeDebug)
{
// only write UPD log every second
if (FrameCount % 10 == 0)
{
System.IO.File.AppendAllLines(UPDDiskDevice.outputfile, UPDDiskDevice.dLog);
UPDDiskDevice.dLog = new System.Collections.Generic.List<string>();
//System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString);
}
}
}
// is this a lag frame?
Spectrum.IsLagFrame = !InputRead;
#endregion
// FDC debug
if (UPDDiskDevice != null && UPDDiskDevice.writeDebug)
{
// only write UPD log every second
if (FrameCount % 10 == 0)
{
System.IO.File.AppendAllLines(UPDDiskDevice.outputfile, UPDDiskDevice.dLog);
UPDDiskDevice.dLog = new System.Collections.Generic.List<string>();
//System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString);
}
}
}
#region Reset Functions
#endregion
/// <summary>
/// Hard reset of the emulated machine
/// </summary>
public virtual void HardReset()
{
//ULADevice.ResetInterrupt();
ROMPaged = 0;
SpecialPagingMode = false;
RAMPaged = 0;
CPU.RegPC = 0;
#region Reset Functions
Spectrum.SetCpuRegister("SP", 0xFFFF);
Spectrum.SetCpuRegister("IY", 0xFFFF);
Spectrum.SetCpuRegister("IX", 0xFFFF);
Spectrum.SetCpuRegister("AF", 0xFFFF);
Spectrum.SetCpuRegister("BC", 0xFFFF);
Spectrum.SetCpuRegister("DE", 0xFFFF);
Spectrum.SetCpuRegister("HL", 0xFFFF);
Spectrum.SetCpuRegister("SP", 0xFFFF);
Spectrum.SetCpuRegister("Shadow AF", 0xFFFF);
Spectrum.SetCpuRegister("Shadow BC", 0xFFFF);
Spectrum.SetCpuRegister("Shadow DE", 0xFFFF);
Spectrum.SetCpuRegister("Shadow HL", 0xFFFF);
/// <summary>
/// Hard reset of the emulated machine
/// </summary>
public virtual void HardReset()
{
//ULADevice.ResetInterrupt();
ROMPaged = 0;
SpecialPagingMode = false;
RAMPaged = 0;
CPU.RegPC = 0;
CPU.Regs[CPU.I] = 0;
CPU.Regs[CPU.R] = 0;
Spectrum.SetCpuRegister("SP", 0xFFFF);
Spectrum.SetCpuRegister("IY", 0xFFFF);
Spectrum.SetCpuRegister("IX", 0xFFFF);
Spectrum.SetCpuRegister("AF", 0xFFFF);
Spectrum.SetCpuRegister("BC", 0xFFFF);
Spectrum.SetCpuRegister("DE", 0xFFFF);
Spectrum.SetCpuRegister("HL", 0xFFFF);
Spectrum.SetCpuRegister("SP", 0xFFFF);
Spectrum.SetCpuRegister("Shadow AF", 0xFFFF);
Spectrum.SetCpuRegister("Shadow BC", 0xFFFF);
Spectrum.SetCpuRegister("Shadow DE", 0xFFFF);
Spectrum.SetCpuRegister("Shadow HL", 0xFFFF);
TapeDevice.Reset();
if (AYDevice != null)
AYDevice.Reset();
CPU.Regs[CPU.I] = 0;
CPU.Regs[CPU.R] = 0;
byte[][] rams = new byte[][]
{
RAM0,
RAM1,
RAM2,
RAM3,
RAM4,
RAM5,
RAM6,
RAM7
};
TapeDevice.Reset();
if (AYDevice != null)
AYDevice.Reset();
foreach (var r in rams)
{
for (int i = 0; i < r.Length; i++)
{
r[i] = 0x00;
}
}
}
byte[][] rams = new byte[][]
{
RAM0,
RAM1,
RAM2,
RAM3,
RAM4,
RAM5,
RAM6,
RAM7
};
/// <summary>
/// Soft reset of the emulated machine
/// </summary>
public virtual void SoftReset()
{
//ULADevice.ResetInterrupt();
ROMPaged = 0;
SpecialPagingMode = false;
RAMPaged = 0;
CPU.RegPC = 0;
foreach (var r in rams)
{
for (int i = 0; i < r.Length; i++)
{
r[i] = 0x00;
}
}
}
Spectrum.SetCpuRegister("SP", 0xFFFF);
Spectrum.SetCpuRegister("IY", 0xFFFF);
Spectrum.SetCpuRegister("IX", 0xFFFF);
Spectrum.SetCpuRegister("AF", 0xFFFF);
Spectrum.SetCpuRegister("BC", 0xFFFF);
Spectrum.SetCpuRegister("DE", 0xFFFF);
Spectrum.SetCpuRegister("HL", 0xFFFF);
Spectrum.SetCpuRegister("SP", 0xFFFF);
Spectrum.SetCpuRegister("Shadow AF", 0xFFFF);
Spectrum.SetCpuRegister("Shadow BC", 0xFFFF);
Spectrum.SetCpuRegister("Shadow DE", 0xFFFF);
Spectrum.SetCpuRegister("Shadow HL", 0xFFFF);
/// <summary>
/// Soft reset of the emulated machine
/// </summary>
public virtual void SoftReset()
{
//ULADevice.ResetInterrupt();
ROMPaged = 0;
SpecialPagingMode = false;
RAMPaged = 0;
CPU.RegPC = 0;
CPU.Regs[CPU.I] = 0;
CPU.Regs[CPU.R] = 0;
Spectrum.SetCpuRegister("SP", 0xFFFF);
Spectrum.SetCpuRegister("IY", 0xFFFF);
Spectrum.SetCpuRegister("IX", 0xFFFF);
Spectrum.SetCpuRegister("AF", 0xFFFF);
Spectrum.SetCpuRegister("BC", 0xFFFF);
Spectrum.SetCpuRegister("DE", 0xFFFF);
Spectrum.SetCpuRegister("HL", 0xFFFF);
Spectrum.SetCpuRegister("SP", 0xFFFF);
Spectrum.SetCpuRegister("Shadow AF", 0xFFFF);
Spectrum.SetCpuRegister("Shadow BC", 0xFFFF);
Spectrum.SetCpuRegister("Shadow DE", 0xFFFF);
Spectrum.SetCpuRegister("Shadow HL", 0xFFFF);
TapeDevice.Reset();
if (AYDevice != null)
AYDevice.Reset();
CPU.Regs[CPU.I] = 0;
CPU.Regs[CPU.R] = 0;
byte[][] rams = new byte[][]
{
RAM0,
RAM1,
RAM2,
RAM3,
RAM4,
RAM5,
RAM6,
RAM7
};
TapeDevice.Reset();
if (AYDevice != null)
AYDevice.Reset();
foreach (var r in rams)
{
for (int i = 0; i < r.Length; i++)
{
r[i] = 0x00;
}
}
}
byte[][] rams = new byte[][]
{
RAM0,
RAM1,
RAM2,
RAM3,
RAM4,
RAM5,
RAM6,
RAM7
};
#endregion
foreach (var r in rams)
{
for (int i = 0; i < r.Length; i++)
{
r[i] = 0x00;
}
}
}
#region IStatable
#endregion
public void SyncState(Serializer ser)
{
ser.BeginSection("ZXMachine");
ser.Sync(nameof(FrameCompleted), ref FrameCompleted);
ser.Sync(nameof(OverFlow), ref OverFlow);
ser.Sync(nameof(FrameCount), ref FrameCount);
ser.Sync(nameof(_frameCycles), ref _frameCycles);
ser.Sync(nameof(inputRead), ref inputRead);
ser.Sync(nameof(LastFrameStartCPUTick), ref LastFrameStartCPUTick);
ser.Sync(nameof(LastULAOutByte), ref LastULAOutByte);
ser.Sync(nameof(ROM0), ref ROM0, false);
ser.Sync(nameof(ROM1), ref ROM1, false);
ser.Sync(nameof(ROM2), ref ROM2, false);
ser.Sync(nameof(ROM3), ref ROM3, false);
ser.Sync(nameof(RAM0), ref RAM0, false);
ser.Sync(nameof(RAM1), ref RAM1, false);
ser.Sync(nameof(RAM2), ref RAM2, false);
ser.Sync(nameof(RAM3), ref RAM3, false);
ser.Sync(nameof(RAM4), ref RAM4, false);
ser.Sync(nameof(RAM5), ref RAM5, false);
ser.Sync(nameof(RAM6), ref RAM6, false);
ser.Sync(nameof(RAM7), ref RAM7, false);
ser.Sync(nameof(ROMPaged), ref ROMPaged);
ser.Sync(nameof(SHADOWPaged), ref SHADOWPaged);
ser.Sync(nameof(RAMPaged), ref RAMPaged);
ser.Sync(nameof(PagingDisabled), ref PagingDisabled);
ser.Sync(nameof(SpecialPagingMode), ref SpecialPagingMode);
ser.Sync(nameof(PagingConfiguration), ref PagingConfiguration);
ser.Sync(nameof(ROMhigh), ref ROMhigh);
ser.Sync(nameof(ROMlow), ref ROMlow);
ser.Sync(nameof(LastContendedReadByte), ref LastContendedReadByte);
#region IStatable
KeyboardDevice.SyncState(ser);
BuzzerDevice.SyncState(ser);
TapeBuzzer.SyncState(ser);
ULADevice.SyncState(ser);
CPUMon.SyncState(ser);
public void SyncState(Serializer ser)
{
ser.BeginSection("ZXMachine");
ser.Sync(nameof(FrameCompleted), ref FrameCompleted);
ser.Sync(nameof(OverFlow), ref OverFlow);
ser.Sync(nameof(FrameCount), ref FrameCount);
ser.Sync(nameof(_frameCycles), ref _frameCycles);
ser.Sync(nameof(inputRead), ref inputRead);
ser.Sync(nameof(LastFrameStartCPUTick), ref LastFrameStartCPUTick);
ser.Sync(nameof(LastULAOutByte), ref LastULAOutByte);
ser.Sync(nameof(ROM0), ref ROM0, false);
ser.Sync(nameof(ROM1), ref ROM1, false);
ser.Sync(nameof(ROM2), ref ROM2, false);
ser.Sync(nameof(ROM3), ref ROM3, false);
ser.Sync(nameof(RAM0), ref RAM0, false);
ser.Sync(nameof(RAM1), ref RAM1, false);
ser.Sync(nameof(RAM2), ref RAM2, false);
ser.Sync(nameof(RAM3), ref RAM3, false);
ser.Sync(nameof(RAM4), ref RAM4, false);
ser.Sync(nameof(RAM5), ref RAM5, false);
ser.Sync(nameof(RAM6), ref RAM6, false);
ser.Sync(nameof(RAM7), ref RAM7, false);
ser.Sync(nameof(ROMPaged), ref ROMPaged);
ser.Sync(nameof(SHADOWPaged), ref SHADOWPaged);
ser.Sync(nameof(RAMPaged), ref RAMPaged);
ser.Sync(nameof(PagingDisabled), ref PagingDisabled);
ser.Sync(nameof(SpecialPagingMode), ref SpecialPagingMode);
ser.Sync(nameof(PagingConfiguration), ref PagingConfiguration);
ser.Sync(nameof(ROMhigh), ref ROMhigh);
ser.Sync(nameof(ROMlow), ref ROMlow);
ser.Sync(nameof(LastContendedReadByte), ref LastContendedReadByte);
if (AYDevice != null)
{
AYDevice.SyncState(ser);
((AY38912)AYDevice as AY38912).PanningConfiguration = Spectrum.Settings.AYPanConfig;
}
KeyboardDevice.SyncState(ser);
BuzzerDevice.SyncState(ser);
TapeBuzzer.SyncState(ser);
ULADevice.SyncState(ser);
CPUMon.SyncState(ser);
ser.Sync(nameof(tapeMediaIndex), ref tapeMediaIndex);
if (ser.IsReader)
{
IsLoadState = true;
TapeMediaIndex = tapeMediaIndex;
IsLoadState = false;
}
if (AYDevice != null)
{
AYDevice.SyncState(ser);
((AY38912)AYDevice as AY38912).PanningConfiguration = Spectrum.Settings.AYPanConfig;
}
TapeDevice.SyncState(ser);
ser.Sync(nameof(tapeMediaIndex), ref tapeMediaIndex);
if (ser.IsReader)
{
IsLoadState = true;
TapeMediaIndex = tapeMediaIndex;
IsLoadState = false;
}
ser.Sync(nameof(diskMediaIndex), ref diskMediaIndex);
if (ser.IsReader)
{
IsLoadState = true;
DiskMediaIndex = diskMediaIndex;
IsLoadState = false;
}
if (UPDDiskDevice != null)
{
UPDDiskDevice.SyncState(ser);
}
TapeDevice.SyncState(ser);
ser.EndSection();
}
ser.Sync(nameof(diskMediaIndex), ref diskMediaIndex);
if (ser.IsReader)
{
IsLoadState = true;
DiskMediaIndex = diskMediaIndex;
IsLoadState = false;
}
#endregion
}
if (UPDDiskDevice != null)
{
UPDDiskDevice.SyncState(ser);
}
ser.EndSection();
}
#endregion
}
}

View File

@ -1,48 +1,48 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// 128K/+2 ULA
/// </summary>
class Screen128 : ULA
{
#region Construction
/// <summary>
/// 128K/+2 ULA
/// </summary>
class Screen128 : ULA
{
#region Construction
public Screen128(SpectrumBase machine)
public Screen128(SpectrumBase machine)
: base(machine)
{
// interrupt
InterruptStartTime = 3;
InterruptLength = 36;
// offsets
RenderTableOffset = 58;
ContentionOffset = 6;
FloatingBusOffset = 1;
// timing
ClockSpeed = 3546900;
FrameCycleLength = 70908;
ScanlineTime = 228;
BorderLeftTime = 24;
BorderRightTime = 24;
FirstPaperLine = 63;
FirstPaperTState = 64;
// screen layout
Border4T = true;
Border4TStage = 2;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48; // 55; // 48;
BorderBottomHeight = 48; // 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
{
// interrupt
InterruptStartTime = 3;
InterruptLength = 36;
// offsets
RenderTableOffset = 58;
ContentionOffset = 6;
FloatingBusOffset = 1;
// timing
ClockSpeed = 3546900;
FrameCycleLength = 70908;
ScanlineTime = 228;
BorderLeftTime = 24;
BorderRightTime = 24;
FirstPaperLine = 63;
FirstPaperTState = 64;
// screen layout
Border4T = true;
Border4TStage = 2;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48; // 55; // 48;
BorderBottomHeight = 48; // 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum128);
RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum128);
SetupScreenSize();
}
SetupScreenSize();
}
#endregion
}
#endregion
}
}

View File

@ -4,48 +4,48 @@ using BizHawk.Emulation.Cores.Sound;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// 128K Constructor
/// </summary>
public partial class ZX128 : SpectrumBase
{
#region Construction
/// <summary>
/// 128K Constructor
/// </summary>
public partial class ZX128 : SpectrumBase
{
#region Construction
/// <summary>
/// Main constructor
/// </summary>
public ZX128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{
Spectrum = spectrum;
CPU = cpu;
/// <summary>
/// Main constructor
/// </summary>
public ZX128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{
Spectrum = spectrum;
CPU = cpu;
CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128;
CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128;
ROMPaged = 0;
SHADOWPaged = false;
RAMPaged = 0;
PagingDisabled = false;
ULADevice = new Screen128(this);
ROMPaged = 0;
SHADOWPaged = false;
RAMPaged = 0;
PagingDisabled = false;
ULADevice = new Screen128(this);
BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer");
TapeBuzzer = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "TapeBuzzer");
AYDevice = new AY38912(this);
AYDevice.Init(44100, ULADevice.FrameLength);
AYDevice.Init(44100, ULADevice.FrameLength);
KeyboardDevice = new StandardKeyboard(this);
KeyboardDevice = new StandardKeyboard(this);
InitJoysticks(joysticks);
InitJoysticks(joysticks);
TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this);
TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this);
InitializeMedia(files);
}
InitializeMedia(files);
}
#endregion
}
#endregion
}
}

View File

@ -3,23 +3,23 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// The +2 is almost identical to the 128k from an emulation point of view
/// There are just a few small changes in the ROMs
/// </summary>
public partial class ZX128Plus2 : ZX128
{
#region Construction
/// <summary>
/// The +2 is almost identical to the 128k from an emulation point of view
/// There are just a few small changes in the ROMs
/// </summary>
public partial class ZX128Plus2 : ZX128
{
#region Construction
/// <summary>
/// Main constructor
/// </summary>
public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
: base(spectrum, cpu, borderType, files, joysticks)
{
}
/// <summary>
/// Main constructor
/// </summary>
public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
: base(spectrum, cpu, borderType, files, joysticks)
{
#endregion
}
}
#endregion
}
}

View File

@ -1,50 +1,50 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// +2A/+3 ULA
/// </summary>
class Screen128Plus2a : ULA
{
#region Construction
/// <summary>
/// +2A/+3 ULA
/// </summary>
class Screen128Plus2a : ULA
{
#region Construction
public Screen128Plus2a(SpectrumBase machine)
public Screen128Plus2a(SpectrumBase machine)
: base(machine)
{
// interrupt
InterruptStartTime = 0;
InterruptLength = 32;
// offsets
RenderTableOffset = 58;
ContentionOffset = 9;
FloatingBusOffset = 0;
// timing
ClockSpeed = 3546900;
FrameCycleLength = 70908;
ScanlineTime = 228;
BorderLeftTime = 24;
BorderRightTime = 24;
FirstPaperLine = 63;
FirstPaperTState = 64;
// screen layout
Border4T = true;
Border4TStage = 2;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48;// 55;
BorderBottomHeight = 48; // 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
{
// interrupt
InterruptStartTime = 0;
InterruptLength = 32;
// offsets
RenderTableOffset = 58;
ContentionOffset = 9;
FloatingBusOffset = 0;
// timing
ClockSpeed = 3546900;
FrameCycleLength = 70908;
ScanlineTime = 228;
BorderLeftTime = 24;
BorderRightTime = 24;
FirstPaperLine = 63;
FirstPaperTState = 64;
// screen layout
Border4T = true;
Border4TStage = 2;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48;// 55;
BorderBottomHeight = 48; // 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum128Plus2a);
RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum128Plus2a);
SetupScreenSize();
SetupScreenSize();
GenerateP3PortTable();
}
GenerateP3PortTable();
}
#endregion
}
#endregion
}
}

View File

@ -4,48 +4,48 @@ using BizHawk.Emulation.Cores.Sound;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// +2A Constructor
/// </summary>
public partial class ZX128Plus2a : SpectrumBase
{
#region Construction
/// <summary>
/// +2A Constructor
/// </summary>
public partial class ZX128Plus2a : SpectrumBase
{
#region Construction
/// <summary>
/// Main constructor
/// </summary>
public ZX128Plus2a(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{
Spectrum = spectrum;
CPU = cpu;
/// <summary>
/// Main constructor
/// </summary>
public ZX128Plus2a(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{
Spectrum = spectrum;
CPU = cpu;
CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128Plus2a;
CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128Plus2a;
ROMPaged = 0;
SHADOWPaged = false;
RAMPaged = 0;
PagingDisabled = false;
ULADevice = new Screen128Plus2a(this);
ROMPaged = 0;
SHADOWPaged = false;
RAMPaged = 0;
PagingDisabled = false;
ULADevice = new Screen128Plus2a(this);
BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer");
TapeBuzzer = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "TapeBuzzer");
AYDevice = new AY38912(this);
AYDevice.Init(44100, ULADevice.FrameLength);
AYDevice.Init(44100, ULADevice.FrameLength);
KeyboardDevice = new StandardKeyboard(this);
KeyboardDevice = new StandardKeyboard(this);
InitJoysticks(joysticks);
InitJoysticks(joysticks);
TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this);
TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this);
InitializeMedia(files);
}
InitializeMedia(files);
}
#endregion
}
#endregion
}
}

View File

@ -4,51 +4,51 @@ using BizHawk.Emulation.Cores.Sound;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// +3 Constructor
/// </summary>
public partial class ZX128Plus3 : SpectrumBase
{
#region Construction
/// <summary>
/// +3 Constructor
/// </summary>
public partial class ZX128Plus3 : SpectrumBase
{
#region Construction
/// <summary>
/// Main constructor
/// </summary>
public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{
Spectrum = spectrum;
CPU = cpu;
/// <summary>
/// Main constructor
/// </summary>
public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{
Spectrum = spectrum;
CPU = cpu;
CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128Plus3;
CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128Plus3;
ROMPaged = 0;
SHADOWPaged = false;
RAMPaged = 0;
PagingDisabled = false;
ULADevice = new Screen128Plus2a(this);
ROMPaged = 0;
SHADOWPaged = false;
RAMPaged = 0;
PagingDisabled = false;
ULADevice = new Screen128Plus2a(this);
BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer");
TapeBuzzer = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "TapeBuzzer");
AYDevice = new AY38912(this);
AYDevice.Init(44100, ULADevice.FrameLength);
AYDevice.Init(44100, ULADevice.FrameLength);
KeyboardDevice = new StandardKeyboard(this);
KeyboardDevice = new StandardKeyboard(this);
InitJoysticks(joysticks);
InitJoysticks(joysticks);
TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this);
TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this);
UPDDiskDevice = new NECUPD765();
UPDDiskDevice.Init(this);
UPDDiskDevice = new NECUPD765();
UPDDiskDevice.Init(this);
InitializeMedia(files);
}
InitializeMedia(files);
}
#endregion
}
#endregion
}
}

View File

@ -3,27 +3,27 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// 16K is idential to 48K, just without the top 32KB of RAM
/// </summary>
public class ZX16 : ZX48
{
#region Construction
/// <summary>
/// 16K is idential to 48K, just without the top 32KB of RAM
/// </summary>
public class ZX16 : ZX48
{
#region Construction
/// <summary>
/// Main constructor
/// </summary>
public ZX16(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
: base(spectrum, cpu, borderType, files, joysticks)
{
/// <summary>
/// Main constructor
/// </summary>
public ZX16(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
: base(spectrum, cpu, borderType, files, joysticks)
{
}
}
#endregion
#endregion
#region Memory
#region Memory
/* 48K Spectrum has NO memory paging
/* 48K Spectrum has NO memory paging
*
*
| Bank 0 |
@ -38,101 +38,101 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
0x0000 +--------+
*/
/// <summary>
/// Simulates reading from the bus (no contention)
/// Paging should be handled here
/// </summary>
public override byte ReadBus(ushort addr)
{
int divisor = addr / 0x4000;
var index = addr % 0x4000;
/// <summary>
/// Simulates reading from the bus (no contention)
/// Paging should be handled here
/// </summary>
public override byte ReadBus(ushort addr)
{
int divisor = addr / 0x4000;
var index = addr % 0x4000;
// paging logic goes here
// paging logic goes here
switch (divisor)
{
case 0:
TestForTapeTraps(addr % 0x4000);
return ROM0[index];
case 1: return RAM0[index];
default:
// memory does not exist
return 0xff;
}
}
switch (divisor)
{
case 0:
TestForTapeTraps(addr % 0x4000);
return ROM0[index];
case 1: return RAM0[index];
default:
// memory does not exist
return 0xff;
}
}
/// <summary>
/// Simulates writing to the bus (no contention)
/// Paging should be handled here
/// </summary>
public override void WriteBus(ushort addr, byte value)
{
int divisor = addr / 0x4000;
var index = addr % 0x4000;
/// <summary>
/// Simulates writing to the bus (no contention)
/// Paging should be handled here
/// </summary>
public override void WriteBus(ushort addr, byte value)
{
int divisor = addr / 0x4000;
var index = addr % 0x4000;
// paging logic goes here
// paging logic goes here
switch (divisor)
{
case 0:
// cannot write to ROM
break;
case 1:
//ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM0[index] = value;
break;
}
}
switch (divisor)
{
case 0:
// cannot write to ROM
break;
case 1:
//ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM0[index] = value;
break;
}
}
/// <summary>
/// Reads a byte of data from a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public override byte ReadMemory(ushort addr)
{
var data = ReadBus(addr);
return data;
}
/// <summary>
/// Reads a byte of data from a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public override byte ReadMemory(ushort addr)
{
var data = ReadBus(addr);
return data;
}
/// <summary>
/// Returns the ROM/RAM enum that relates to this particular memory read operation
/// </summary>
public override ZXSpectrum.CDLResult ReadCDL(ushort addr)
{
var res = new ZXSpectrum.CDLResult();
/// <summary>
/// Returns the ROM/RAM enum that relates to this particular memory read operation
/// </summary>
public override ZXSpectrum.CDLResult ReadCDL(ushort addr)
{
var res = new ZXSpectrum.CDLResult();
int divisor = addr / 0x4000;
res.Address = addr % 0x4000;
int divisor = addr / 0x4000;
res.Address = addr % 0x4000;
// paging logic goes here
switch (divisor)
{
case 0: res.Type = ZXSpectrum.CDLType.ROM0; break;
case 1: res.Type = ZXSpectrum.CDLType.RAM0; break;
}
// paging logic goes here
switch (divisor)
{
case 0: res.Type = ZXSpectrum.CDLType.ROM0; break;
case 1: res.Type = ZXSpectrum.CDLType.RAM0; break;
}
return res;
}
return res;
}
/// <summary>
/// Writes a byte of data to a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public override void WriteMemory(ushort addr, byte value)
{
WriteBus(addr, value);
}
/// <summary>
/// Sets up the ROM
/// </summary>
public override void InitROM(RomData romData)
{
RomData = romData;
// for 16/48k machines only ROM0 is used (no paging)
RomData.RomBytes?.CopyTo(ROM0, 0);
}
/// <summary>
/// Writes a byte of data to a specified memory address
/// (with memory contention if appropriate)
/// </summary>
public override void WriteMemory(ushort addr, byte value)
{
WriteBus(addr, value);
}
#endregion
}
/// <summary>
/// Sets up the ROM
/// </summary>
public override void InitROM(RomData romData)
{
RomData = romData;
// for 16/48k machines only ROM0 is used (no paging)
RomData.RomBytes?.CopyTo(ROM0, 0);
}
#endregion
}
}

View File

@ -1,48 +1,48 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// 48K ULA
/// </summary>
class Screen48 : ULA
{
#region Construction
/// <summary>
/// 48K ULA
/// </summary>
class Screen48 : ULA
{
#region Construction
public Screen48(SpectrumBase machine)
: base(machine)
{
// interrupt
InterruptStartTime = 3;
InterruptLength = 32;
// offsets
RenderTableOffset = 56;
ContentionOffset = 6;
FloatingBusOffset = 1;
// timing
ClockSpeed = 3500000;
FrameCycleLength = 69888;
ScanlineTime = 224;
BorderLeftTime = 24;
BorderRightTime = 24;
FirstPaperLine = 64;
FirstPaperTState = 64;
// screen layout
Border4T = true;
Border4TStage = 0;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48;// 55;// 48;
BorderBottomHeight = 48;// 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
{
// interrupt
InterruptStartTime = 3;
InterruptLength = 32;
// offsets
RenderTableOffset = 56;
ContentionOffset = 6;
FloatingBusOffset = 1;
// timing
ClockSpeed = 3500000;
FrameCycleLength = 69888;
ScanlineTime = 224;
BorderLeftTime = 24;
BorderRightTime = 24;
FirstPaperLine = 64;
FirstPaperTState = 64;
// screen layout
Border4T = true;
Border4TStage = 0;
ScreenWidth = 256;
ScreenHeight = 192;
BorderTopHeight = 48;// 55;// 48;
BorderBottomHeight = 48;// 56;
BorderLeftWidth = 48;
BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum48);
RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum48);
SetupScreenSize();
}
SetupScreenSize();
}
#endregion
}
#endregion
}
}

View File

@ -5,23 +5,23 @@ using BizHawk.Emulation.Cores.Sound;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// 48K construction
/// </summary>
public partial class ZX48 : SpectrumBase
{
#region Construction
/// <summary>
/// 48K construction
/// </summary>
public partial class ZX48 : SpectrumBase
{
#region Construction
/// <summary>
/// Main constructor
/// </summary>
public ZX48(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{
Spectrum = spectrum;
CPU = cpu;
/// <summary>
/// Main constructor
/// </summary>
public ZX48(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{
Spectrum = spectrum;
CPU = cpu;
CPUMon = new CPUMonitor(this);
ULADevice = new Screen48(this);
CPUMon = new CPUMonitor(this);
ULADevice = new Screen48(this);
BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer");
@ -29,29 +29,29 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
KeyboardDevice = new StandardKeyboard(this);
InitJoysticks(joysticks);
InitJoysticks(joysticks);
TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this);
TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this);
InitializeMedia(files);
}
InitializeMedia(files);
}
#endregion
#endregion
#region Reset
#region Reset
public override void HardReset()
{
base.HardReset();
public override void HardReset()
{
base.HardReset();
Random rn = new Random();
for (int d = 0; d < 6912; d++)
{
RAM0[d] = (byte)rn.Next(255);
}
}
#endregion
}
Random rn = new Random();
for (int d = 0; d < 6912; d++)
{
RAM0[d] = (byte)rn.Next(255);
}
}
#endregion
}
}

View File

@ -5,260 +5,260 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Logical object representing a standard +3 disk image
/// </summary>
public class CPCExtendedFloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.CPCExtended;
/// <summary>
/// Logical object representing a standard +3 disk image
/// </summary>
public class CPCExtendedFloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.CPCExtended;
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("EXTENDED CPC DSK"))
{
// incorrect format
return false;
}
if (!ident.ToUpper().Contains("EXTENDED CPC DSK"))
{
// incorrect format
return false;
}
// read the disk information block
DiskHeader.DiskIdent = ident;
DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14);
DiskHeader.NumberOfTracks = data[0x30];
DiskHeader.NumberOfSides = data[0x31];
DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskData = data;
int pos = 0x34;
// read the disk information block
DiskHeader.DiskIdent = ident;
DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14);
DiskHeader.NumberOfTracks = data[0x30];
DiskHeader.NumberOfSides = data[0x31];
DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskData = data;
int pos = 0x34;
if (DiskHeader.NumberOfSides > 1)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk image contains multiple sides.");
sbm.AppendLine("This is NOT currently supported in ZXHawk.");
sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk).");
throw new System.NotImplementedException(sbm.ToString());
}
if (DiskHeader.NumberOfSides > 1)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk image contains multiple sides.");
sbm.AppendLine("This is NOT currently supported in ZXHawk.");
sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk).");
throw new System.NotImplementedException(sbm.ToString());
}
if (DiskHeader.NumberOfTracks > 42)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk is an " + DiskHeader.NumberOfTracks + " track disk image.");
sbm.AppendLine("This is currently incompatible with the emulated +3 disk drive (42 tracks).");
sbm.AppendLine("Likely the disk image is an 80 track betadisk or opus image, the drives and controllers for which are not currently emulated in ZXHawk");
throw new System.NotImplementedException(sbm.ToString());
}
if (DiskHeader.NumberOfTracks > 42)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk is an " + DiskHeader.NumberOfTracks + " track disk image.");
sbm.AppendLine("This is currently incompatible with the emulated +3 disk drive (42 tracks).");
sbm.AppendLine("Likely the disk image is an 80 track betadisk or opus image, the drives and controllers for which are not currently emulated in ZXHawk");
throw new System.NotImplementedException(sbm.ToString());
}
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
DiskHeader.TrackSizes[i] = data[pos++] * 256;
}
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
DiskHeader.TrackSizes[i] = data[pos++] * 256;
}
// move to first track information block
pos = 0x100;
// move to first track information block
pos = 0x100;
// parse each track
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
// check for unformatted track
if (DiskHeader.TrackSizes[i] == 0)
{
DiskTracks[i] = new Track();
DiskTracks[i].Sectors = new Sector[0];
continue;
}
// parse each track
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
// check for unformatted track
if (DiskHeader.TrackSizes[i] == 0)
{
DiskTracks[i] = new Track();
DiskTracks[i].Sectors = new Sector[0];
continue;
}
int p = pos;
DiskTracks[i] = new Track();
int p = pos;
DiskTracks[i] = new Track();
// track info block
DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12);
p += 16;
DiskTracks[i].TrackNumber = data[p++];
DiskTracks[i].SideNumber = data[p++];
DiskTracks[i].DataRate = data[p++];
DiskTracks[i].RecordingMode = data[p++];
DiskTracks[i].SectorSize = data[p++];
DiskTracks[i].NumberOfSectors = data[p++];
DiskTracks[i].GAP3Length = data[p++];
DiskTracks[i].FillerByte = data[p++];
// track info block
DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12);
p += 16;
DiskTracks[i].TrackNumber = data[p++];
DiskTracks[i].SideNumber = data[p++];
DiskTracks[i].DataRate = data[p++];
DiskTracks[i].RecordingMode = data[p++];
DiskTracks[i].SectorSize = data[p++];
DiskTracks[i].NumberOfSectors = data[p++];
DiskTracks[i].GAP3Length = data[p++];
DiskTracks[i].FillerByte = data[p++];
int dpos = pos + 0x100;
int dpos = pos + 0x100;
// sector info list
DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors];
for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++)
{
DiskTracks[i].Sectors[s] = new Sector();
// sector info list
DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors];
for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++)
{
DiskTracks[i].Sectors[s] = new Sector();
DiskTracks[i].Sectors[s].TrackNumber = data[p++];
DiskTracks[i].Sectors[s].SideNumber = data[p++];
DiskTracks[i].Sectors[s].SectorID = data[p++];
DiskTracks[i].Sectors[s].SectorSize = data[p++];
DiskTracks[i].Sectors[s].Status1 = data[p++];
DiskTracks[i].Sectors[s].Status2 = data[p++];
DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p);
p += 2;
DiskTracks[i].Sectors[s].TrackNumber = data[p++];
DiskTracks[i].Sectors[s].SideNumber = data[p++];
DiskTracks[i].Sectors[s].SectorID = data[p++];
DiskTracks[i].Sectors[s].SectorSize = data[p++];
DiskTracks[i].Sectors[s].Status1 = data[p++];
DiskTracks[i].Sectors[s].Status2 = data[p++];
DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p);
p += 2;
// sector data - begins at 0x100 offset from the start of the track info block (in this case dpos)
DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength];
// sector data - begins at 0x100 offset from the start of the track info block (in this case dpos)
DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength];
// copy the data
for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++)
{
DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b];
}
// copy the data
for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++)
{
DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b];
}
// check for multiple weak/random sectors stored
if (DiskTracks[i].Sectors[s].SectorSize <= 7)
{
// sectorsize n=8 is equivilent to n=0 - FDC will use DTL for length
int specifiedSize = 0x80 << DiskTracks[i].Sectors[s].SectorSize;
// check for multiple weak/random sectors stored
if (DiskTracks[i].Sectors[s].SectorSize <= 7)
{
// sectorsize n=8 is equivilent to n=0 - FDC will use DTL for length
int specifiedSize = 0x80 << DiskTracks[i].Sectors[s].SectorSize;
if (specifiedSize < DiskTracks[i].Sectors[s].ActualDataByteLength)
{
// more data stored than sectorsize defines
// check for multiple weak/random copies
if (DiskTracks[i].Sectors[s].ActualDataByteLength % specifiedSize != 0)
{
DiskTracks[i].Sectors[s].ContainsMultipleWeakSectors = true;
}
}
}
if (specifiedSize < DiskTracks[i].Sectors[s].ActualDataByteLength)
{
// more data stored than sectorsize defines
// check for multiple weak/random copies
if (DiskTracks[i].Sectors[s].ActualDataByteLength % specifiedSize != 0)
{
DiskTracks[i].Sectors[s].ContainsMultipleWeakSectors = true;
}
}
}
// move dpos to the next sector data postion
dpos += DiskTracks[i].Sectors[s].ActualDataByteLength;
}
// move dpos to the next sector data postion
dpos += DiskTracks[i].Sectors[s].ActualDataByteLength;
}
// move to the next track info block
pos += DiskHeader.TrackSizes[i];
}
// move to the next track info block
pos += DiskHeader.TrackSizes[i];
}
// run protection scheme detector
ParseProtection();
// run protection scheme detector
ParseProtection();
return true;
}
return true;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("EXTENDED CPC DSK"))
{
// incorrect format
return false;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("EXTENDED CPC DSK"))
{
// incorrect format
return false;
}
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
// disk info block
Array.Copy(data, 0, S0, 0, 0x100);
Array.Copy(data, 0, S1, 0, 0x100);
// change side number
S0[0x31] = 1;
S1[0x31] = 1;
// disk info block
Array.Copy(data, 0, S0, 0, 0x100);
Array.Copy(data, 0, S1, 0, 0x100);
// change side number
S0[0x31] = 1;
S1[0x31] = 1;
// extended format can have different track sizes
int[] trkSizes = new int[data[0x30] * data[0x31]];
// extended format can have different track sizes
int[] trkSizes = new int[data[0x30] * data[0x31]];
int pos = 0x34;
for (int i = 0; i < data[0x30] * data[0x31]; i++)
{
trkSizes[i] = data[pos] * 256;
// clear destination trk sizes (will be added later)
S0[pos] = 0;
S1[pos] = 0;
pos++;
}
int pos = 0x34;
for (int i = 0; i < data[0x30] * data[0x31]; i++)
{
trkSizes[i] = data[pos] * 256;
// clear destination trk sizes (will be added later)
S0[pos] = 0;
S1[pos] = 0;
pos++;
}
// start at track info blocks
int mPos = 0x100;
int s0Pos = 0x100;
int s0tCount = 0;
int s1tCount = 0;
int s1Pos = 0x100;
int tCount = 0;
// start at track info blocks
int mPos = 0x100;
int s0Pos = 0x100;
int s0tCount = 0;
int s1tCount = 0;
int s1Pos = 0x100;
int tCount = 0;
while (tCount < data[0x30] * data[0x31])
{
// which side is this?
var side = data[mPos + 0x11];
if (side == 0)
{
// side 1
Array.Copy(data, mPos, S0, s0Pos, trkSizes[tCount]);
s0Pos += trkSizes[tCount];
// trk size table
S0[0x34 + s0tCount++] = (byte)(trkSizes[tCount] / 256);
}
else if (side == 1)
{
// side 2
Array.Copy(data, mPos, S1, s1Pos, trkSizes[tCount]);
s1Pos += trkSizes[tCount];
// trk size table
S1[0x34 + s1tCount++] = (byte)(trkSizes[tCount] / 256);
}
while (tCount < data[0x30] * data[0x31])
{
// which side is this?
var side = data[mPos + 0x11];
if (side == 0)
{
// side 1
Array.Copy(data, mPos, S0, s0Pos, trkSizes[tCount]);
s0Pos += trkSizes[tCount];
// trk size table
S0[0x34 + s0tCount++] = (byte)(trkSizes[tCount] / 256);
}
else if (side == 1)
{
// side 2
Array.Copy(data, mPos, S1, s1Pos, trkSizes[tCount]);
s1Pos += trkSizes[tCount];
// trk size table
S1[0x34 + s1tCount++] = (byte)(trkSizes[tCount] / 256);
}
mPos += trkSizes[tCount++];
}
mPos += trkSizes[tCount++];
}
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
results.Add(s0final);
results.Add(s1final);
results.Add(s0final);
results.Add(s1final);
return true;
}
return true;
}
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
}
}
// sync deterministic track and sector counters
ser.Sync(nameof( _randomCounter), ref _randomCounter);
RandomCounter = _randomCounter;
// sync deterministic track and sector counters
ser.Sync(nameof(_randomCounter), ref _randomCounter);
RandomCounter = _randomCounter;
ser.EndSection();
}
}
ser.EndSection();
}
}
}

View File

@ -5,250 +5,250 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Logical object representing a standard +3 disk image
/// </summary>
public class CPCFloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.CPC;
/// <summary>
/// Logical object representing a standard +3 disk image
/// </summary>
public class CPCFloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.CPC;
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("MV - CPC"))
{
// incorrect format
return false;
}
if (!ident.ToUpper().Contains("MV - CPC"))
{
// incorrect format
return false;
}
// read the disk information block
DiskHeader.DiskIdent = ident;
DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14);
DiskHeader.NumberOfTracks = data[0x30];
DiskHeader.NumberOfSides = data[0x31];
DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskData = data;
int pos = 0x32;
// read the disk information block
DiskHeader.DiskIdent = ident;
DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14);
DiskHeader.NumberOfTracks = data[0x30];
DiskHeader.NumberOfSides = data[0x31];
DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskData = data;
int pos = 0x32;
if (DiskHeader.NumberOfSides > 1)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk image contains multiple sides.");
sbm.AppendLine("This is NOT currently supported in ZXHawk.");
sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk).");
throw new System.NotImplementedException(sbm.ToString());
}
if (DiskHeader.NumberOfSides > 1)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk image contains multiple sides.");
sbm.AppendLine("This is NOT currently supported in ZXHawk.");
sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk).");
throw new System.NotImplementedException(sbm.ToString());
}
if (DiskHeader.NumberOfTracks > 42)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk is an " + DiskHeader.NumberOfTracks + " track disk image.");
sbm.AppendLine("This is currently incompatible with the emulated +3 disk drive (42 tracks).");
sbm.AppendLine("Likely the disk image is an 80 track betadisk or opus image, the drives and controllers for which are not currently emulated in ZXHawk");
throw new System.NotImplementedException(sbm.ToString());
}
if (DiskHeader.NumberOfTracks > 42)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk is an " + DiskHeader.NumberOfTracks + " track disk image.");
sbm.AppendLine("This is currently incompatible with the emulated +3 disk drive (42 tracks).");
sbm.AppendLine("Likely the disk image is an 80 track betadisk or opus image, the drives and controllers for which are not currently emulated in ZXHawk");
throw new System.NotImplementedException(sbm.ToString());
}
// standard CPC format all track sizes are the same in the image
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
DiskHeader.TrackSizes[i] = MediaConverter.GetWordValue(data, pos);
}
// standard CPC format all track sizes are the same in the image
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
DiskHeader.TrackSizes[i] = MediaConverter.GetWordValue(data, pos);
}
// move to first track information block
pos = 0x100;
// move to first track information block
pos = 0x100;
// parse each track
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
// check for unformatted track
if (DiskHeader.TrackSizes[i] == 0)
{
DiskTracks[i] = new Track();
DiskTracks[i].Sectors = new Sector[0];
continue;
}
// parse each track
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
// check for unformatted track
if (DiskHeader.TrackSizes[i] == 0)
{
DiskTracks[i] = new Track();
DiskTracks[i].Sectors = new Sector[0];
continue;
}
int p = pos;
DiskTracks[i] = new Track();
int p = pos;
DiskTracks[i] = new Track();
// track info block
DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12);
p += 16;
DiskTracks[i].TrackNumber = data[p++];
DiskTracks[i].SideNumber = data[p++];
p += 2;
DiskTracks[i].SectorSize = data[p++];
DiskTracks[i].NumberOfSectors = data[p++];
DiskTracks[i].GAP3Length = data[p++];
DiskTracks[i].FillerByte = data[p++];
// track info block
DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12);
p += 16;
DiskTracks[i].TrackNumber = data[p++];
DiskTracks[i].SideNumber = data[p++];
p += 2;
DiskTracks[i].SectorSize = data[p++];
DiskTracks[i].NumberOfSectors = data[p++];
DiskTracks[i].GAP3Length = data[p++];
DiskTracks[i].FillerByte = data[p++];
int dpos = pos + 0x100;
int dpos = pos + 0x100;
// sector info list
DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors];
for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++)
{
DiskTracks[i].Sectors[s] = new Sector();
// sector info list
DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors];
for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++)
{
DiskTracks[i].Sectors[s] = new Sector();
DiskTracks[i].Sectors[s].TrackNumber = data[p++];
DiskTracks[i].Sectors[s].SideNumber = data[p++];
DiskTracks[i].Sectors[s].SectorID = data[p++];
DiskTracks[i].Sectors[s].SectorSize = data[p++];
DiskTracks[i].Sectors[s].Status1 = data[p++];
DiskTracks[i].Sectors[s].Status2 = data[p++];
DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p);
p += 2;
DiskTracks[i].Sectors[s].TrackNumber = data[p++];
DiskTracks[i].Sectors[s].SideNumber = data[p++];
DiskTracks[i].Sectors[s].SectorID = data[p++];
DiskTracks[i].Sectors[s].SectorSize = data[p++];
DiskTracks[i].Sectors[s].Status1 = data[p++];
DiskTracks[i].Sectors[s].Status2 = data[p++];
DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p);
p += 2;
// actualdatabytelength value is calculated now
if (DiskTracks[i].Sectors[s].SectorSize == 0)
{
// no sectorsize specified - DTL will be used at runtime
DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i];
}
else if (DiskTracks[i].Sectors[s].SectorSize > 6)
{
// invalid - wrap around to 0
DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i];
}
else if (DiskTracks[i].Sectors[s].SectorSize == 6)
{
// only 0x1800 bytes are stored
DiskTracks[i].Sectors[s].ActualDataByteLength = 0x1800;
}
else
{
// valid sector size for this format
DiskTracks[i].Sectors[s].ActualDataByteLength = 0x80 << DiskTracks[i].Sectors[s].SectorSize;
}
// actualdatabytelength value is calculated now
if (DiskTracks[i].Sectors[s].SectorSize == 0)
{
// no sectorsize specified - DTL will be used at runtime
DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i];
}
else if (DiskTracks[i].Sectors[s].SectorSize > 6)
{
// invalid - wrap around to 0
DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i];
}
else if (DiskTracks[i].Sectors[s].SectorSize == 6)
{
// only 0x1800 bytes are stored
DiskTracks[i].Sectors[s].ActualDataByteLength = 0x1800;
}
else
{
// valid sector size for this format
DiskTracks[i].Sectors[s].ActualDataByteLength = 0x80 << DiskTracks[i].Sectors[s].SectorSize;
}
// sector data - begins at 0x100 offset from the start of the track info block (in this case dpos)
DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength];
// sector data - begins at 0x100 offset from the start of the track info block (in this case dpos)
DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength];
// copy the data
for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++)
{
DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b];
}
// copy the data
for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++)
{
DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b];
}
// move dpos to the next sector data postion
dpos += DiskTracks[i].Sectors[s].ActualDataByteLength;
}
// move dpos to the next sector data postion
dpos += DiskTracks[i].Sectors[s].ActualDataByteLength;
}
// move to the next track info block
pos += DiskHeader.TrackSizes[i];
}
// move to the next track info block
pos += DiskHeader.TrackSizes[i];
}
// run protection scheme detector
ParseProtection();
// run protection scheme detector
ParseProtection();
return true;
}
return true;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("MV - CPC"))
{
// incorrect format
return false;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("MV - CPC"))
{
// incorrect format
return false;
}
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
// disk info block
Array.Copy(data, 0, S0, 0, 0x100);
Array.Copy(data, 0, S1, 0, 0x100);
// change side number
S0[0x31] = 1;
S1[0x31] = 1;
// disk info block
Array.Copy(data, 0, S0, 0, 0x100);
Array.Copy(data, 0, S1, 0, 0x100);
// change side number
S0[0x31] = 1;
S1[0x31] = 1;
var trkSize = MediaConverter.GetWordValue(data, 0x32);
var trkSize = MediaConverter.GetWordValue(data, 0x32);
// start at track info blocks
int mPos = 0x100;
int s0Pos = 0x100;
int s1Pos = 0x100;
// start at track info blocks
int mPos = 0x100;
int s0Pos = 0x100;
int s1Pos = 0x100;
var numTrks = data[0x30];
var numSides = data[0x31];
var numTrks = data[0x30];
var numSides = data[0x31];
while (mPos < trkSize * data[0x30] * data[0x31])
{
// which side is this?
var side = data[mPos + 0x11];
if (side == 0)
{
// side 1
Array.Copy(data, mPos, S0, s0Pos, trkSize);
s0Pos += trkSize;
}
else if (side == 1)
{
// side 2
Array.Copy(data, mPos, S1, s1Pos, trkSize);
s1Pos += trkSize;
}
else
{
while (mPos < trkSize * data[0x30] * data[0x31])
{
// which side is this?
var side = data[mPos + 0x11];
if (side == 0)
{
// side 1
Array.Copy(data, mPos, S0, s0Pos, trkSize);
s0Pos += trkSize;
}
else if (side == 1)
{
// side 2
Array.Copy(data, mPos, S1, s1Pos, trkSize);
s1Pos += trkSize;
}
else
{
}
}
mPos += trkSize;
}
mPos += trkSize;
}
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
results.Add(s0final);
results.Add(s1final);
results.Add(s0final);
results.Add(s1final);
return true;
}
return true;
}
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
}
}
ser.EndSection();
}
}
ser.EndSection();
}
}
}

View File

@ -1,34 +1,34 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// The different disk formats ZXHawk currently supports
/// </summary>
public enum DiskType
{
/// <summary>
/// Standard CPCEMU disk format (used in the built-in +3 disk drive)
/// </summary>
CPC,
/// <summary>
/// The different disk formats ZXHawk currently supports
/// </summary>
public enum DiskType
{
/// <summary>
/// Standard CPCEMU disk format (used in the built-in +3 disk drive)
/// </summary>
CPC,
/// <summary>
/// Extended CPCEMU disk format (used in the built-in +3 disk drive)
/// </summary>
CPCExtended,
/// <summary>
/// Extended CPCEMU disk format (used in the built-in +3 disk drive)
/// </summary>
CPCExtended,
/// <summary>
/// Interchangeable Preservation Format
/// </summary>
IPF,
/// <summary>
/// Interchangeable Preservation Format
/// </summary>
IPF,
/// <summary>
/// Ultra Disk Image Format (v1.0)
/// </summary>
UDI,
/// <summary>
/// Ultra Disk Image Format (v1.0)
/// </summary>
UDI,
/// <summary>
/// Ultra Disk Image Format (v1.1)
/// </summary>
UDIv1_1
}
/// <summary>
/// Ultra Disk Image Format (v1.1)
/// </summary>
UDIv1_1
}
}

View File

@ -8,452 +8,452 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
public class IPFFloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.IPF;
public class IPFFloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.IPF;
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("CAPS"))
{
// incorrect format
return false;
}
if (!ident.ToUpper().Contains("CAPS"))
{
// incorrect format
return false;
}
int pos = 0;
int pos = 0;
List<IPFBlock> blocks = new List<IPFBlock>();
List<IPFBlock> blocks = new List<IPFBlock>();
while (pos < data.Length)
{
try
{
var block = IPFBlock.ParseNextBlock(ref pos, this, data, blocks);
while (pos < data.Length)
{
try
{
var block = IPFBlock.ParseNextBlock(ref pos, this, data, blocks);
if (block == null)
{
// EOF
break;
}
if (block == null)
{
// EOF
break;
}
if (block.RecordType == RecordHeaderType.None)
{
// unknown block
}
if (block.RecordType == RecordHeaderType.None)
{
// unknown block
}
blocks.Add(block);
}
catch (Exception ex)
{
blocks.Add(block);
}
catch (Exception ex)
{
var e = ex.ToString();
}
}
}
}
// now process the blocks
var infoBlock = blocks.Where(a => a.RecordType == RecordHeaderType.INFO).FirstOrDefault();
var IMGEblocks = blocks.Where(a => a.RecordType == RecordHeaderType.IMGE).ToList();
var DATAblocks = blocks.Where(a => a.RecordType == RecordHeaderType.DATA);
// now process the blocks
var infoBlock = blocks.Where(a => a.RecordType == RecordHeaderType.INFO).FirstOrDefault();
var IMGEblocks = blocks.Where(a => a.RecordType == RecordHeaderType.IMGE).ToList();
var DATAblocks = blocks.Where(a => a.RecordType == RecordHeaderType.DATA);
DiskHeader.NumberOfTracks = (byte)(IMGEblocks.Count());
DiskHeader.NumberOfSides = (byte)(infoBlock.INFOmaxSide + 1);
DiskTracks = new Track[DiskHeader.NumberOfTracks];
DiskHeader.NumberOfTracks = (byte)(IMGEblocks.Count());
DiskHeader.NumberOfSides = (byte)(infoBlock.INFOmaxSide + 1);
DiskTracks = new Track[DiskHeader.NumberOfTracks];
for (int t = 0; t < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; t++)
{
// each imge block represents one track
var img = IMGEblocks[t];
DiskTracks[t] = new Track();
var trk = DiskTracks[t];
for (int t = 0; t < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; t++)
{
// each imge block represents one track
var img = IMGEblocks[t];
DiskTracks[t] = new Track();
var trk = DiskTracks[t];
var blockCount = img.IMGEblockCount;
var dataBlock = DATAblocks.Where(a => a.DATAdataKey == img.IMGEdataKey).FirstOrDefault();
var blockCount = img.IMGEblockCount;
var dataBlock = DATAblocks.Where(a => a.DATAdataKey == img.IMGEdataKey).FirstOrDefault();
trk.SideNumber = (byte)img.IMGEside;
trk.TrackNumber = (byte)img.IMGEtrack;
trk.SideNumber = (byte)img.IMGEside;
trk.TrackNumber = (byte)img.IMGEtrack;
trk.Sectors = new Sector[blockCount];
trk.Sectors = new Sector[blockCount];
// process data block descriptors
int p = 0;
for (int d = 0; d < blockCount; d++)
{
var extraDataAreaStart = 32 * blockCount;
trk.Sectors[d] = new Sector();
var sector = trk.Sectors[d];
// process data block descriptors
int p = 0;
for (int d = 0; d < blockCount; d++)
{
var extraDataAreaStart = 32 * blockCount;
trk.Sectors[d] = new Sector();
var sector = trk.Sectors[d];
int dataBits = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int gapBits = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int dataBytes;
int gapBytes;
int gapOffset;
int cellType;
if (infoBlock.INFOencoderType == 1)
{
dataBytes = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
gapBytes = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
}
else if (infoBlock.INFOencoderType == 2)
{
gapOffset = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
cellType = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
}
int encoderType = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int? blockFlags = null;
if (infoBlock.INFOencoderType == 2)
{
blockFlags = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p);
}
p += 4;
int dataBits = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int gapBits = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int dataBytes;
int gapBytes;
int gapOffset;
int cellType;
if (infoBlock.INFOencoderType == 1)
{
dataBytes = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
gapBytes = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
}
else if (infoBlock.INFOencoderType == 2)
{
gapOffset = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
cellType = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
}
int encoderType = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int? blockFlags = null;
if (infoBlock.INFOencoderType == 2)
{
blockFlags = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p);
}
p += 4;
int gapDefault = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int dataOffset = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int gapDefault = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int dataOffset = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
// gap stream elements
if (infoBlock.INFOencoderType == 2 && gapBits != 0 && blockFlags != null)
{
if (!blockFlags.Value.Bit(1) && !blockFlags.Value.Bit(0))
{
// no gap stream
}
if (!blockFlags.Value.Bit(1) && blockFlags.Value.Bit(0))
{
// Forward gap stream list only
}
if (blockFlags.Value.Bit(1) && !blockFlags.Value.Bit(0))
{
// Backward gap stream list only
}
if (blockFlags.Value.Bit(1) && blockFlags.Value.Bit(0))
{
// Forward and Backward stream lists
}
}
// gap stream elements
if (infoBlock.INFOencoderType == 2 && gapBits != 0 && blockFlags != null)
{
if (!blockFlags.Value.Bit(1) && !blockFlags.Value.Bit(0))
{
// no gap stream
}
if (!blockFlags.Value.Bit(1) && blockFlags.Value.Bit(0))
{
// Forward gap stream list only
}
if (blockFlags.Value.Bit(1) && !blockFlags.Value.Bit(0))
{
// Backward gap stream list only
}
if (blockFlags.Value.Bit(1) && blockFlags.Value.Bit(0))
{
// Forward and Backward stream lists
}
}
// data stream elements
if (dataBits != 0)
{
var dsLocation = dataOffset;
// data stream elements
if (dataBits != 0)
{
var dsLocation = dataOffset;
for (;;)
{
byte dataHead = dataBlock.DATAextraDataRaw[dsLocation++];
if (dataHead == 0)
{
// end of data stream list
break;
}
for (; ; )
{
byte dataHead = dataBlock.DATAextraDataRaw[dsLocation++];
if (dataHead == 0)
{
// end of data stream list
break;
}
var sampleSize = ((dataHead & 0xE0) >> 5);
var dataType = dataHead & 0x1F;
byte[] dSize = new byte[sampleSize];
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dSize, 0, sampleSize);
var dataSize = MediaConverter.GetBEInt32FromByteArray(dSize);
dsLocation += dSize.Length;
int dataLen;
byte[] dataStream = new byte[0];
var sampleSize = ((dataHead & 0xE0) >> 5);
var dataType = dataHead & 0x1F;
byte[] dSize = new byte[sampleSize];
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dSize, 0, sampleSize);
var dataSize = MediaConverter.GetBEInt32FromByteArray(dSize);
dsLocation += dSize.Length;
int dataLen;
byte[] dataStream = new byte[0];
if (blockFlags != null && blockFlags.Value.Bit(2))
{
// bits
if (dataType != 5)
{
dataLen = dataSize / 8;
if (dataSize % 8 != 0)
{
// bits left over
}
dataStream = new byte[dataLen];
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dataStream, 0, dataLen);
}
}
else
{
// bytes
if (dataType != 5)
{
dataStream = new byte[dataSize];
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dataStream, 0, dataSize);
}
}
if (blockFlags != null && blockFlags.Value.Bit(2))
{
// bits
if (dataType != 5)
{
dataLen = dataSize / 8;
if (dataSize % 8 != 0)
{
// bits left over
}
dataStream = new byte[dataLen];
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dataStream, 0, dataLen);
}
}
else
{
// bytes
if (dataType != 5)
{
dataStream = new byte[dataSize];
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dataStream, 0, dataSize);
}
}
// dataStream[] now contains the data
switch (dataType)
{
// SYNC
case 1:
break;
// DATA
case 2:
if (dataStream.Length == 7)
{
// ID
// first byte IAM
sector.TrackNumber = dataStream[1];
sector.SideNumber = dataStream[2];
sector.SectorID = dataStream[3];
sector.SectorSize = dataStream[4];
}
else if (dataStream.Length > 255)
{
// DATA
// first byte DAM
if (dataStream[0] == 0xF8)
{
// deleted address mark
//sector.Status1
}
sector.SectorData = new byte[dataStream.Length - 1 - 2];
Array.Copy(dataStream, 1, sector.SectorData, 0, dataStream.Length - 1 - 2);
}
break;
// GAP
case 3:
break;
// RAW
case 4:
break;
// FUZZY
case 5:
break;
default:
break;
}
// dataStream[] now contains the data
switch (dataType)
{
// SYNC
case 1:
break;
// DATA
case 2:
if (dataStream.Length == 7)
{
// ID
// first byte IAM
sector.TrackNumber = dataStream[1];
sector.SideNumber = dataStream[2];
sector.SectorID = dataStream[3];
sector.SectorSize = dataStream[4];
}
else if (dataStream.Length > 255)
{
// DATA
// first byte DAM
if (dataStream[0] == 0xF8)
{
// deleted address mark
//sector.Status1
}
sector.SectorData = new byte[dataStream.Length - 1 - 2];
Array.Copy(dataStream, 1, sector.SectorData, 0, dataStream.Length - 1 - 2);
}
break;
// GAP
case 3:
break;
// RAW
case 4:
break;
// FUZZY
case 5:
break;
default:
break;
}
dsLocation += dataStream.Length;
}
}
}
}
dsLocation += dataStream.Length;
}
}
}
}
return true;
}
return true;
}
public class IPFBlock
{
public RecordHeaderType RecordType;
public int BlockLength;
public int CRC;
public byte[] RawBlockData;
public int StartPos;
public class IPFBlock
{
public RecordHeaderType RecordType;
public int BlockLength;
public int CRC;
public byte[] RawBlockData;
public int StartPos;
#region INFO
#region INFO
public int INFOmediaType;
public int INFOencoderType;
public int INFOencoderRev;
public int INFOfileKey;
public int INFOfileRev;
public int INFOorigin;
public int INFOminTrack;
public int INFOmaxTrack;
public int INFOminSide;
public int INFOmaxSide;
public int INFOcreationDate;
public int INFOcreationTime;
public int INFOplatform1;
public int INFOplatform2;
public int INFOplatform3;
public int INFOplatform4;
public int INFOdiskNumber;
public int INFOcreatorId;
public int INFOmediaType;
public int INFOencoderType;
public int INFOencoderRev;
public int INFOfileKey;
public int INFOfileRev;
public int INFOorigin;
public int INFOminTrack;
public int INFOmaxTrack;
public int INFOminSide;
public int INFOmaxSide;
public int INFOcreationDate;
public int INFOcreationTime;
public int INFOplatform1;
public int INFOplatform2;
public int INFOplatform3;
public int INFOplatform4;
public int INFOdiskNumber;
public int INFOcreatorId;
#endregion
#endregion
#region IMGE
#region IMGE
public int IMGEtrack;
public int IMGEside;
public int IMGEdensity;
public int IMGEsignalType;
public int IMGEtrackBytes;
public int IMGEstartBytePos;
public int IMGEstartBitPos;
public int IMGEdataBits;
public int IMGEgapBits;
public int IMGEtrackBits;
public int IMGEblockCount;
public int IMGEencoderProcess;
public int IMGEtrackFlags;
public int IMGEdataKey;
public int IMGEtrack;
public int IMGEside;
public int IMGEdensity;
public int IMGEsignalType;
public int IMGEtrackBytes;
public int IMGEstartBytePos;
public int IMGEstartBitPos;
public int IMGEdataBits;
public int IMGEgapBits;
public int IMGEtrackBits;
public int IMGEblockCount;
public int IMGEencoderProcess;
public int IMGEtrackFlags;
public int IMGEdataKey;
#endregion
#endregion
#region DATA
#region DATA
public int DATAlength;
public int DATAbitSize;
public int DATAcrc;
public int DATAdataKey;
public byte[] DATAextraDataRaw;
public int DATAlength;
public int DATAbitSize;
public int DATAcrc;
public int DATAdataKey;
public byte[] DATAextraDataRaw;
#endregion
#endregion
public static IPFBlock ParseNextBlock(ref int startPos, FloppyDisk disk, byte[] data, List<IPFBlock> blockCollection)
{
IPFBlock ipf = new IPFBlock();
ipf.StartPos = startPos;
public static IPFBlock ParseNextBlock(ref int startPos, FloppyDisk disk, byte[] data, List<IPFBlock> blockCollection)
{
IPFBlock ipf = new IPFBlock();
ipf.StartPos = startPos;
if (startPos >= data.Length)
{
// EOF
return null;
}
if (startPos >= data.Length)
{
// EOF
return null;
}
// assume the startPos passed in is actually the start of a new block
// look for record header ident
string ident = Encoding.ASCII.GetString(data, startPos, 4);
startPos += 4;
try
{
ipf.RecordType = (RecordHeaderType)Enum.Parse(typeof(RecordHeaderType), ident);
}
catch
{
ipf.RecordType = RecordHeaderType.None;
}
// assume the startPos passed in is actually the start of a new block
// look for record header ident
string ident = Encoding.ASCII.GetString(data, startPos, 4);
startPos += 4;
try
{
ipf.RecordType = (RecordHeaderType)Enum.Parse(typeof(RecordHeaderType), ident);
}
catch
{
ipf.RecordType = RecordHeaderType.None;
}
// setup for actual block size
ipf.BlockLength = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.CRC = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.RawBlockData = new byte[ipf.BlockLength];
Array.Copy(data, ipf.StartPos, ipf.RawBlockData, 0, ipf.BlockLength);
// setup for actual block size
ipf.BlockLength = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.CRC = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.RawBlockData = new byte[ipf.BlockLength];
Array.Copy(data, ipf.StartPos, ipf.RawBlockData, 0, ipf.BlockLength);
switch (ipf.RecordType)
{
// Nothing to process / unknown
// just move ahead
case RecordHeaderType.CAPS:
case RecordHeaderType.TRCK:
case RecordHeaderType.DUMP:
case RecordHeaderType.CTEI:
case RecordHeaderType.CTEX:
default:
startPos = ipf.StartPos + ipf.BlockLength;
break;
switch (ipf.RecordType)
{
// Nothing to process / unknown
// just move ahead
case RecordHeaderType.CAPS:
case RecordHeaderType.TRCK:
case RecordHeaderType.DUMP:
case RecordHeaderType.CTEI:
case RecordHeaderType.CTEX:
default:
startPos = ipf.StartPos + ipf.BlockLength;
break;
// INFO block
case RecordHeaderType.INFO:
// INFO header is followed immediately by an INFO block
ipf.INFOmediaType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOencoderType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOencoderRev = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOfileKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOfileRev = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOorigin = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOminTrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOmaxTrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOminSide = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOmaxSide = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOcreationDate = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOcreationTime = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform1 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform2 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform3 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform4 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOdiskNumber = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOcreatorId = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
startPos += 12; // reserved
break;
// INFO block
case RecordHeaderType.INFO:
// INFO header is followed immediately by an INFO block
ipf.INFOmediaType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOencoderType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOencoderRev = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOfileKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOfileRev = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOorigin = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOminTrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOmaxTrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOminSide = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOmaxSide = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOcreationDate = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOcreationTime = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform1 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform2 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform3 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform4 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOdiskNumber = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOcreatorId = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
startPos += 12; // reserved
break;
case RecordHeaderType.IMGE:
ipf.IMGEtrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEside = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEdensity = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEsignalType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEtrackBytes = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEstartBytePos = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEstartBitPos = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEdataBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEgapBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEtrackBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEblockCount = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEencoderProcess = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEtrackFlags = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEdataKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
startPos += 12; // reserved
break;
case RecordHeaderType.IMGE:
ipf.IMGEtrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEside = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEdensity = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEsignalType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEtrackBytes = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEstartBytePos = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEstartBitPos = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEdataBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEgapBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEtrackBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEblockCount = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEencoderProcess = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEtrackFlags = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEdataKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
startPos += 12; // reserved
break;
case RecordHeaderType.DATA:
ipf.DATAlength = MediaConverter.GetBEInt32(data, startPos);
if (ipf.DATAlength == 0)
{
ipf.DATAextraDataRaw = new byte[0];
ipf.DATAlength = 0;
}
else
{
ipf.DATAextraDataRaw = new byte[ipf.DATAlength];
}
startPos += 4;
ipf.DATAbitSize = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.DATAcrc = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.DATAdataKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
case RecordHeaderType.DATA:
ipf.DATAlength = MediaConverter.GetBEInt32(data, startPos);
if (ipf.DATAlength == 0)
{
ipf.DATAextraDataRaw = new byte[0];
ipf.DATAlength = 0;
}
else
{
ipf.DATAextraDataRaw = new byte[ipf.DATAlength];
}
startPos += 4;
ipf.DATAbitSize = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.DATAcrc = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.DATAdataKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
if (ipf.DATAlength != 0)
{
Array.Copy(data, startPos, ipf.DATAextraDataRaw, 0, ipf.DATAlength);
}
if (ipf.DATAlength != 0)
{
Array.Copy(data, startPos, ipf.DATAextraDataRaw, 0, ipf.DATAlength);
}
startPos += ipf.DATAlength;
break;
}
startPos += ipf.DATAlength;
break;
}
return ipf;
}
}
return ipf;
}
}
public enum RecordHeaderType
{
None,
CAPS,
DUMP,
DATA,
TRCK,
INFO,
IMGE,
CTEI,
CTEX,
}
public enum RecordHeaderType
{
None,
CAPS,
DUMP,
DATA,
TRCK,
INFO,
IMGE,
CTEI,
CTEX,
}
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
}
}
// sync deterministic track and sector counters
ser.Sync(nameof( _randomCounter), ref _randomCounter);
RandomCounter = _randomCounter;
// sync deterministic track and sector counters
ser.Sync(nameof(_randomCounter), ref _randomCounter);
RandomCounter = _randomCounter;
ser.EndSection();
}
}
ser.EndSection();
}
}
}

View File

@ -8,205 +8,205 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
public class UDI1_0FloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.UDI;
public class UDI1_0FloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.UDI;
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 4);
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 4);
if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!"))
{
// incorrect format
return false;
}
if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!"))
{
// incorrect format
return false;
}
if (data[0x08] != 0)
{
// wrong version
return false;
}
if (data[0x08] != 0)
{
// wrong version
return false;
}
if (ident == "udi!")
{
// cant handle compression yet
return false;
}
if (ident == "udi!")
{
// cant handle compression yet
return false;
}
DiskHeader.DiskIdent = ident;
DiskHeader.NumberOfTracks = (byte)(data[0x09] + 1);
DiskHeader.NumberOfSides = (byte)(data[0x0A] + 1);
DiskHeader.DiskIdent = ident;
DiskHeader.NumberOfTracks = (byte)(data[0x09] + 1);
DiskHeader.NumberOfSides = (byte)(data[0x0A] + 1);
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
int fileSize = MediaConverter.GetInt32(data, 4); // not including the final 4-byte checksum
int fileSize = MediaConverter.GetInt32(data, 4); // not including the final 4-byte checksum
// ignore extended header
var extHdrSize = MediaConverter.GetInt32(data, 0x0C);
int pos = 0x10 + extHdrSize;
// ignore extended header
var extHdrSize = MediaConverter.GetInt32(data, 0x0C);
int pos = 0x10 + extHdrSize;
// process track information
for (int t = 0; t < DiskHeader.NumberOfTracks; t++)
{
DiskTracks[t] = new UDIv1Track();
DiskTracks[t].TrackNumber = (byte)t;
DiskTracks[t].SideNumber = 0;
DiskTracks[t].TrackType = data[pos++];
DiskTracks[t].TLEN = MediaConverter.GetWordValue(data, pos); pos += 2;
DiskTracks[t].TrackData = new byte[DiskTracks[t].TLEN + DiskTracks[t].CLEN];
Array.Copy(data, pos, DiskTracks[t].TrackData, 0, DiskTracks[t].TLEN + DiskTracks[t].CLEN);
pos += DiskTracks[t].TLEN + DiskTracks[t].CLEN;
}
// process track information
for (int t = 0; t < DiskHeader.NumberOfTracks; t++)
{
DiskTracks[t] = new UDIv1Track();
DiskTracks[t].TrackNumber = (byte)t;
DiskTracks[t].SideNumber = 0;
DiskTracks[t].TrackType = data[pos++];
DiskTracks[t].TLEN = MediaConverter.GetWordValue(data, pos); pos += 2;
DiskTracks[t].TrackData = new byte[DiskTracks[t].TLEN + DiskTracks[t].CLEN];
Array.Copy(data, pos, DiskTracks[t].TrackData, 0, DiskTracks[t].TLEN + DiskTracks[t].CLEN);
pos += DiskTracks[t].TLEN + DiskTracks[t].CLEN;
}
return true;
}
return true;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 4);
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 4);
if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!"))
{
// incorrect format
return false;
}
if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!"))
{
// incorrect format
return false;
}
if (data[0x08] != 0)
{
// wrong version
return false;
}
if (data[0x08] != 0)
{
// wrong version
return false;
}
if (ident == "udi!")
{
// cant handle compression yet
return false;
}
if (ident == "udi!")
{
// cant handle compression yet
return false;
}
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
// header
var extHdr = MediaConverter.GetInt32(data, 0x0C);
Array.Copy(data, 0, S0, 0, 0x10 + extHdr);
Array.Copy(data, 0, S1, 0, 0x10 + extHdr);
// change side number
S0[0x0A] = 0;
S1[0x0A] = 0;
// header
var extHdr = MediaConverter.GetInt32(data, 0x0C);
Array.Copy(data, 0, S0, 0, 0x10 + extHdr);
Array.Copy(data, 0, S1, 0, 0x10 + extHdr);
// change side number
S0[0x0A] = 0;
S1[0x0A] = 0;
int pos = 0x10 + extHdr;
int fileSize = MediaConverter.GetInt32(data, 4); // not including the final 4-byte checksum
int pos = 0x10 + extHdr;
int fileSize = MediaConverter.GetInt32(data, 4); // not including the final 4-byte checksum
int s0Pos = pos;
int s1Pos = pos;
int s0Pos = pos;
int s1Pos = pos;
// process track information
for (int t = 0; t < (data[0x09] + 1) * 2; t++)
{
var TLEN = MediaConverter.GetWordValue(data, pos + 1);
var CLEN = TLEN / 8 + (TLEN % 8 / 7) / 8;
var blockSize = TLEN + CLEN + 3;
// process track information
for (int t = 0; t < (data[0x09] + 1) * 2; t++)
{
var TLEN = MediaConverter.GetWordValue(data, pos + 1);
var CLEN = TLEN / 8 + (TLEN % 8 / 7) / 8;
var blockSize = TLEN + CLEN + 3;
// 2 sided image: side 0 tracks will all have t as an even number
try
{
if (t == 0 || t % 2 == 0)
{
Array.Copy(data, pos, S0, s0Pos, blockSize);
s0Pos += blockSize;
}
else
{
Array.Copy(data, pos, S1, s1Pos, blockSize);
s1Pos += blockSize;
}
}
catch (Exception)
{
}
// 2 sided image: side 0 tracks will all have t as an even number
try
{
if (t == 0 || t % 2 == 0)
{
Array.Copy(data, pos, S0, s0Pos, blockSize);
s0Pos += blockSize;
}
else
{
Array.Copy(data, pos, S1, s1Pos, blockSize);
s1Pos += blockSize;
}
}
catch (Exception)
{
pos += blockSize;
}
// skip checkum bytes for now
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
results.Add(s0final);
results.Add(s1final);
return true;
}
public class UDIv1Track : Track
{
/// <summary>
/// Parse the UDI TrackData byte[] array into sector objects
/// </summary>
public override Sector[] Sectors
{
get
{
List<UDIv1Sector> secs = new List<UDIv1Sector>();
var datas = TrackData.Skip(3).Take(TLEN).ToArray();
var clocks = new BitArray(TrackData.Skip(3 + TLEN).Take(CLEN).ToArray());
return secs.ToArray();
}
}
}
public class UDIv1Sector : Sector
{
}
}
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
pos += blockSize;
}
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
// skip checkum bytes for now
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
}
results.Add(s0final);
results.Add(s1final);
// sync deterministic track and sector counters
ser.Sync(nameof( _randomCounter), ref _randomCounter);
RandomCounter = _randomCounter;
return true;
}
ser.EndSection();
}
}
public class UDIv1Track : Track
{
/// <summary>
/// Parse the UDI TrackData byte[] array into sector objects
/// </summary>
public override Sector[] Sectors
{
get
{
List<UDIv1Sector> secs = new List<UDIv1Sector>();
var datas = TrackData.Skip(3).Take(TLEN).ToArray();
var clocks = new BitArray(TrackData.Skip(3 + TLEN).Take(CLEN).ToArray());
return secs.ToArray();
}
}
}
public class UDIv1Sector : Sector
{
}
/// <summary>
/// State serlialization
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
ser.Sync(nameof(CylinderCount), ref CylinderCount);
ser.Sync(nameof(SideCount), ref SideCount);
ser.Sync(nameof(BytesPerTrack), ref BytesPerTrack);
ser.Sync(nameof(WriteProtected), ref WriteProtected);
ser.SyncEnum(nameof(Protection), ref Protection);
ser.Sync(nameof(DirtyData), ref DirtyData);
if (DirtyData)
{
}
// sync deterministic track and sector counters
ser.Sync(nameof(_randomCounter), ref _randomCounter);
RandomCounter = _randomCounter;
ser.EndSection();
}
}
}

View File

@ -6,205 +6,205 @@ using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Abtract class that represents all Media Converters
/// </summary>
public abstract class MediaConverter
{
/// <summary>
/// The type of serializer
/// </summary>
public abstract MediaConverterType FormatType { get; }
/// <summary>
/// Abtract class that represents all Media Converters
/// </summary>
public abstract class MediaConverter
{
/// <summary>
/// The type of serializer
/// </summary>
public abstract MediaConverterType FormatType { get; }
/// <summary>
/// Signs whether this class can be used to read the data format
/// </summary>
public virtual bool IsReader
{
get
{
return false;
}
}
/// <summary>
/// Signs whether this class can be used to read the data format
/// </summary>
public virtual bool IsReader
{
get
{
return false;
}
}
/// <summary>
/// Signs whether this class can be used to write the data format
/// </summary>
public virtual bool IsWriter
{
get
{
return false;
}
}
/// <summary>
/// Signs whether this class can be used to write the data format
/// </summary>
public virtual bool IsWriter
{
get
{
return false;
}
}
/// <summary>
/// Serialization method
/// </summary>
public virtual void Read(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Read operation is not implemented for this converter");
}
/// <summary>
/// Serialization method
/// </summary>
public virtual void Read(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Read operation is not implemented for this converter");
}
/// <summary>
/// DeSerialization method
/// </summary>
public virtual void Write(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Write operation is not implemented for this converter");
}
/// <summary>
/// DeSerialization method
/// </summary>
public virtual void Write(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Write operation is not implemented for this converter");
}
/// <summary>
/// Serializer does a quick check, returns TRUE if file is detected as this type
/// </summary>
public virtual bool CheckType(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Check type operation is not implemented for this converter");
}
/// <summary>
/// Serializer does a quick check, returns TRUE if file is detected as this type
/// </summary>
public virtual bool CheckType(byte[] data)
{
throw new NotImplementedException(this.GetType().ToString() +
"Check type operation is not implemented for this converter");
}
#region Static Tools
#region Static Tools
/// <summary>
/// Converts an int32 value into a byte array
/// </summary>
public static byte[] GetBytes(int value)
{
byte[] buf = new byte[4];
buf[0] = (byte)value;
buf[1] = (byte)(value >> 8);
buf[2] = (byte)(value >> 16);
buf[3] = (byte)(value >> 24);
return buf;
}
/// <summary>
/// Converts an int32 value into a byte array
/// </summary>
public static byte[] GetBytes(int value)
{
byte[] buf = new byte[4];
buf[0] = (byte)value;
buf[1] = (byte)(value >> 8);
buf[2] = (byte)(value >> 16);
buf[3] = (byte)(value >> 24);
return buf;
}
/// <summary>
/// Returns an int32 from a byte array based on offset
/// </summary>
public static int GetInt32(byte[] buf, int offsetIndex)
{
return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24;
}
/// <summary>
/// Returns an int32 from a byte array based on offset
/// </summary>
public static int GetInt32(byte[] buf, int offsetIndex)
{
return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24;
}
/// <summary>
/// Returns an int32 from a byte array based on offset (in BIG ENDIAN format)
/// </summary>
public static int GetBEInt32(byte[] buf, int offsetIndex)
{
byte[] b = new byte[4];
Array.Copy(buf, offsetIndex, b, 0, 4);
byte[] buffer = b.Reverse().ToArray();
int pos = 0;
return buffer[pos++] | buffer[pos++] << 8 | buffer[pos++] << 16 | buffer[pos++] << 24;
}
/// <summary>
/// Returns an int32 from a byte array based on offset (in BIG ENDIAN format)
/// </summary>
public static int GetBEInt32(byte[] buf, int offsetIndex)
{
byte[] b = new byte[4];
Array.Copy(buf, offsetIndex, b, 0, 4);
byte[] buffer = b.Reverse().ToArray();
int pos = 0;
return buffer[pos++] | buffer[pos++] << 8 | buffer[pos++] << 16 | buffer[pos++] << 24;
}
/// <summary>
/// Returns an int32 from a byte array based on the length of the byte array (in BIG ENDIAN format)
/// </summary>
public static int GetBEInt32FromByteArray(byte[] buf)
{
byte[] b = buf.Reverse().ToArray();
if (b.Length == 0)
return 0;
int res = b[0];
int pos = 1;
switch (b.Length)
{
case 1:
default:
return res;
case 2:
return res | b[pos] << (8 * pos++);
case 3:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 4:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 5:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 6:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 7:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
}
}
/// <summary>
/// Returns an int32 from a byte array based on the length of the byte array (in BIG ENDIAN format)
/// </summary>
public static int GetBEInt32FromByteArray(byte[] buf)
{
byte[] b = buf.Reverse().ToArray();
if (b.Length == 0)
return 0;
int res = b[0];
int pos = 1;
switch (b.Length)
{
case 1:
default:
return res;
case 2:
return res | b[pos] << (8 * pos++);
case 3:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 4:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 5:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 6:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 7:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
}
}
/// <summary>
/// Returns an int32 from a byte array based on offset
/// </summary>
public static uint GetUInt32(byte[] buf, int offsetIndex)
{
return (uint)(buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24);
}
/// <summary>
/// Returns an int32 from a byte array based on offset
/// </summary>
public static uint GetUInt32(byte[] buf, int offsetIndex)
{
return (uint)(buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24);
}
/// <summary>
/// Returns an uint16 from a byte array based on offset
/// </summary>
public static ushort GetWordValue(byte[] buf, int offsetIndex)
{
return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8);
}
/// <summary>
/// Returns an uint16 from a byte array based on offset
/// </summary>
public static ushort GetWordValue(byte[] buf, int offsetIndex)
{
return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8);
}
/// <summary>
/// Updates a byte array with a uint16 value based on offset
/// </summary>
public static void SetWordValue(byte[] buf, int offsetIndex, ushort value)
{
buf[offsetIndex] = (byte)value;
buf[offsetIndex + 1] = (byte)(value >> 8);
}
/// <summary>
/// Updates a byte array with a uint16 value based on offset
/// </summary>
public static void SetWordValue(byte[] buf, int offsetIndex, ushort value)
{
buf[offsetIndex] = (byte)value;
buf[offsetIndex + 1] = (byte)(value >> 8);
}
/// <summary>
/// Takes a PauseInMilliseconds value and returns the value in T-States
/// </summary>
public static int TranslatePause(int pauseInMS)
{
// t-states per millisecond
var tspms = (69888 * 50) / 1000;
// get value
int res = pauseInMS * tspms;
/// <summary>
/// Takes a PauseInMilliseconds value and returns the value in T-States
/// </summary>
public static int TranslatePause(int pauseInMS)
{
// t-states per millisecond
var tspms = (69888 * 50) / 1000;
// get value
int res = pauseInMS * tspms;
return res;
}
return res;
}
/// <summary>
/// Decompresses a byte array that is Z-RLE compressed
/// </summary>
public static void DecompressZRLE(byte[] sourceBuffer, ref byte[] destBuffer)
{
MemoryStream stream = new MemoryStream();
stream.Write(sourceBuffer, 0, sourceBuffer.Length);
stream.Position = 0;
stream.ReadByte();
stream.ReadByte();
DeflateStream ds = new DeflateStream(stream, CompressionMode.Decompress, false);
ds.Read(destBuffer, 0, destBuffer.Length);
}
/// <summary>
/// Decompresses a byte array that is Z-RLE compressed
/// </summary>
public static void DecompressZRLE(byte[] sourceBuffer, ref byte[] destBuffer)
{
MemoryStream stream = new MemoryStream();
stream.Write(sourceBuffer, 0, sourceBuffer.Length);
stream.Position = 0;
stream.ReadByte();
stream.ReadByte();
DeflateStream ds = new DeflateStream(stream, CompressionMode.Decompress, false);
ds.Read(destBuffer, 0, destBuffer.Length);
}
public static byte[] SerializeRaw(object obj)
{
int rSize = Marshal.SizeOf(obj);
IntPtr buff = Marshal.AllocHGlobal(rSize);
Marshal.StructureToPtr(obj, buff, false);
byte[] rData = new byte[rSize];
Marshal.Copy(buff, rData, 0, rSize);
return rData;
}
public static byte[] SerializeRaw(object obj)
{
int rSize = Marshal.SizeOf(obj);
IntPtr buff = Marshal.AllocHGlobal(rSize);
Marshal.StructureToPtr(obj, buff, false);
byte[] rData = new byte[rSize];
Marshal.Copy(buff, rData, 0, rSize);
return rData;
}
public static T DeserializeRaw<T>(byte[] rData, int pos)
{
int rSize = Marshal.SizeOf(typeof(T));
if (rSize > rData.Length - pos)
throw new Exception();
IntPtr buff = Marshal.AllocHGlobal(rSize);
Marshal.Copy(rData, pos, buff, rSize);
T rObj = (T)Marshal.PtrToStructure(buff, typeof(T));
Marshal.FreeHGlobal(buff);
return rObj;
}
public static T DeserializeRaw<T>(byte[] rData, int pos)
{
int rSize = Marshal.SizeOf(typeof(T));
if (rSize > rData.Length - pos)
throw new Exception();
IntPtr buff = Marshal.AllocHGlobal(rSize);
Marshal.Copy(rData, pos, buff, rSize);
T rObj = (T)Marshal.PtrToStructure(buff, typeof(T));
Marshal.FreeHGlobal(buff);
return rObj;
}
#endregion
}
#endregion
}
}

View File

@ -1,17 +1,17 @@

namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Represents the different types of media serializer avaiable
/// </summary>
public enum MediaConverterType
{
NONE,
TZX,
TAP,
PZX,
CSW,
WAV,
DSK
}
/// <summary>
/// Represents the different types of media serializer avaiable
/// </summary>
public enum MediaConverterType
{
NONE,
TZX,
TAP,
PZX,
CSW,
WAV,
DSK
}
}

View File

@ -11,177 +11,177 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// SZX Methods
/// Based on the work done by ArjunNair in ZERO spectrum emulator: https://github.com/ArjunNair/Zero-Emulator/blob/master/Ziggy/Peripherals/SZXFile.cs
/// </summary>
public partial class SZX
{
private SpectrumBase _machine;
/// <summary>
/// SZX Methods
/// Based on the work done by ArjunNair in ZERO spectrum emulator: https://github.com/ArjunNair/Zero-Emulator/blob/master/Ziggy/Peripherals/SZXFile.cs
/// </summary>
public partial class SZX
{
private SpectrumBase _machine;
private Z80A _cpu => _machine.CPU;
private Z80A _cpu => _machine.CPU;
private SZX(SpectrumBase machine)
{
_machine = machine;
}
private SZX(SpectrumBase machine)
{
_machine = machine;
}
/// <summary>
/// Exports state information to a byte array in ZX-State format
/// </summary>
public static byte[] ExportSZX(SpectrumBase machine)
{
var s = new SZX(machine);
/// <summary>
/// Exports state information to a byte array in ZX-State format
/// </summary>
public static byte[] ExportSZX(SpectrumBase machine)
{
var s = new SZX(machine);
byte[] result = null;
byte[] result = null;
using (MemoryStream ms = new MemoryStream())
{
using (BinaryWriter r = new BinaryWriter(ms))
{
// temp buffer
byte[] buff;
// working block
ZXSTBLOCK block = new ZXSTBLOCK();
using (MemoryStream ms = new MemoryStream())
{
using (BinaryWriter r = new BinaryWriter(ms))
{
// temp buffer
byte[] buff;
// working block
ZXSTBLOCK block = new ZXSTBLOCK();
// header
ZXSTHEADER header = new ZXSTHEADER();
header.dwMagic = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("ZXST"), 0);
header.chMajorVersion = 1;
header.chMinorVersion = 4;
header.chFlags = 0;
switch (s._machine.Spectrum.MachineType)
{
case MachineType.ZXSpectrum16: header.chMachineId = (int)MachineIdentifier.ZXSTMID_16K; break;
case MachineType.ZXSpectrum48: header.chMachineId = (int)MachineIdentifier.ZXSTMID_48K; break;
case MachineType.ZXSpectrum128: header.chMachineId = (int)MachineIdentifier.ZXSTMID_128K; break;
case MachineType.ZXSpectrum128Plus2: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2; break;
case MachineType.ZXSpectrum128Plus2a: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2A; break;
case MachineType.ZXSpectrum128Plus3: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS3; break;
}
buff = MediaConverter.SerializeRaw(header);
r.Write(buff);
// header
ZXSTHEADER header = new ZXSTHEADER();
header.dwMagic = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("ZXST"), 0);
header.chMajorVersion = 1;
header.chMinorVersion = 4;
header.chFlags = 0;
switch (s._machine.Spectrum.MachineType)
{
case MachineType.ZXSpectrum16: header.chMachineId = (int)MachineIdentifier.ZXSTMID_16K; break;
case MachineType.ZXSpectrum48: header.chMachineId = (int)MachineIdentifier.ZXSTMID_48K; break;
case MachineType.ZXSpectrum128: header.chMachineId = (int)MachineIdentifier.ZXSTMID_128K; break;
case MachineType.ZXSpectrum128Plus2: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2; break;
case MachineType.ZXSpectrum128Plus2a: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2A; break;
case MachineType.ZXSpectrum128Plus3: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS3; break;
}
buff = MediaConverter.SerializeRaw(header);
r.Write(buff);
// ZXSTCREATOR
var bStruct = s.GetZXSTCREATOR();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("CRTR"), 0);
block.dwSize = (uint)Marshal.SizeOf(bStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(bStruct);
r.Write(buff);
// ZXSTCREATOR
var bStruct = s.GetZXSTCREATOR();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("CRTR"), 0);
block.dwSize = (uint)Marshal.SizeOf(bStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(bStruct);
r.Write(buff);
// ZXSTZ80REGS
var cStruct = s.GetZXSTZ80REGS();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("Z80R"), 0);
block.dwSize = (uint)Marshal.SizeOf(cStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(cStruct);
r.Write(buff);
// ZXSTZ80REGS
var cStruct = s.GetZXSTZ80REGS();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("Z80R"), 0);
block.dwSize = (uint)Marshal.SizeOf(cStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(cStruct);
r.Write(buff);
// ZXSTSPECREGS
var dStruct = s.GetZXSTSPECREGS();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("SPCR"), 0);
block.dwSize = (uint)Marshal.SizeOf(dStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(dStruct);
r.Write(buff);
// ZXSTSPECREGS
var dStruct = s.GetZXSTSPECREGS();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("SPCR"), 0);
block.dwSize = (uint)Marshal.SizeOf(dStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(dStruct);
r.Write(buff);
// ZXSTKEYBOARD
var eStruct = s.GetZXSTKEYBOARD();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("KEYB"), 0);
block.dwSize = (uint)Marshal.SizeOf(eStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(eStruct);
r.Write(buff);
// ZXSTKEYBOARD
var eStruct = s.GetZXSTKEYBOARD();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("KEYB"), 0);
block.dwSize = (uint)Marshal.SizeOf(eStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(eStruct);
r.Write(buff);
// ZXSTJOYSTICK
var fStruct = s.GetZXSTJOYSTICK();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("JOY\0"), 0);
block.dwSize = (uint)Marshal.SizeOf(fStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(fStruct);
r.Write(buff);
// ZXSTJOYSTICK
var fStruct = s.GetZXSTJOYSTICK();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("JOY\0"), 0);
block.dwSize = (uint)Marshal.SizeOf(fStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(fStruct);
r.Write(buff);
// ZXSTAYBLOCK
if (s._machine.Spectrum.MachineType != MachineType.ZXSpectrum16 && s._machine.Spectrum.MachineType != MachineType.ZXSpectrum48)
{
var gStruct = s.GetZXSTAYBLOCK();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("AY\0\0"), 0);
block.dwSize = (uint)Marshal.SizeOf(gStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(gStruct);
r.Write(buff);
}
// ZXSTRAMPAGE
switch (s._machine.Spectrum.MachineType)
{
// For 16k Spectrums, only page 5 (0x4000 - 0x7fff) is saved.
case MachineType.ZXSpectrum16:
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp16 = s.GetZXSTRAMPAGE(5, s._machine.RAM0);
block.dwSize = (uint)Marshal.SizeOf(rp16);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp16);
r.Write(buff);
break;
// For 48k Spectrums and Timex TS/TC models, pages 5, 2 (0x8000 - 0xbfff) and 0 (0xc000 - 0xffff) are saved.
case MachineType.ZXSpectrum48:
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp48_0 = s.GetZXSTRAMPAGE(5, s._machine.RAM0);
block.dwSize = (uint)Marshal.SizeOf(rp48_0);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp48_0);
r.Write(buff);
// ZXSTAYBLOCK
if (s._machine.Spectrum.MachineType != MachineType.ZXSpectrum16 && s._machine.Spectrum.MachineType != MachineType.ZXSpectrum48)
{
var gStruct = s.GetZXSTAYBLOCK();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("AY\0\0"), 0);
block.dwSize = (uint)Marshal.SizeOf(gStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(gStruct);
r.Write(buff);
}
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp48_1 = s.GetZXSTRAMPAGE(5, s._machine.RAM1);
block.dwSize = (uint)Marshal.SizeOf(rp48_1);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp48_1);
r.Write(buff);
// ZXSTRAMPAGE
switch (s._machine.Spectrum.MachineType)
{
// For 16k Spectrums, only page 5 (0x4000 - 0x7fff) is saved.
case MachineType.ZXSpectrum16:
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp16 = s.GetZXSTRAMPAGE(5, s._machine.RAM0);
block.dwSize = (uint)Marshal.SizeOf(rp16);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp16);
r.Write(buff);
break;
// For 48k Spectrums and Timex TS/TC models, pages 5, 2 (0x8000 - 0xbfff) and 0 (0xc000 - 0xffff) are saved.
case MachineType.ZXSpectrum48:
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp48_0 = s.GetZXSTRAMPAGE(5, s._machine.RAM0);
block.dwSize = (uint)Marshal.SizeOf(rp48_0);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp48_0);
r.Write(buff);
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp48_2 = s.GetZXSTRAMPAGE(5, s._machine.RAM2);
block.dwSize = (uint)Marshal.SizeOf(rp48_2);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp48_2);
r.Write(buff);
break;
// For 128k Spectrums and the Pentagon 128, all pages (0-7) are saved.
case MachineType.ZXSpectrum128:
case MachineType.ZXSpectrum128Plus2:
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
List<byte[]> rams = new List<byte[]>
{
s._machine.RAM0, s._machine.RAM1, s._machine.RAM2, s._machine.RAM3,
s._machine.RAM4, s._machine.RAM5, s._machine.RAM6, s._machine.RAM7
};
for (byte i = 0; i < 8; i++)
{
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp = s.GetZXSTRAMPAGE(i, rams[i]);
block.dwSize = (uint)Marshal.SizeOf(rp);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp);
r.Write(buff);
}
break;
}
/*
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp48_1 = s.GetZXSTRAMPAGE(5, s._machine.RAM1);
block.dwSize = (uint)Marshal.SizeOf(rp48_1);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp48_1);
r.Write(buff);
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp48_2 = s.GetZXSTRAMPAGE(5, s._machine.RAM2);
block.dwSize = (uint)Marshal.SizeOf(rp48_2);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp48_2);
r.Write(buff);
break;
// For 128k Spectrums and the Pentagon 128, all pages (0-7) are saved.
case MachineType.ZXSpectrum128:
case MachineType.ZXSpectrum128Plus2:
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
List<byte[]> rams = new List<byte[]>
{
s._machine.RAM0, s._machine.RAM1, s._machine.RAM2, s._machine.RAM3,
s._machine.RAM4, s._machine.RAM5, s._machine.RAM6, s._machine.RAM7
};
for (byte i = 0; i < 8; i++)
{
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp = s.GetZXSTRAMPAGE(i, rams[i]);
block.dwSize = (uint)Marshal.SizeOf(rp);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp);
r.Write(buff);
}
break;
}
/*
// ZXSTPLUS3
if (s._machine.Spectrum.MachineType == MachineType.ZXSpectrum128Plus3)
{
@ -223,201 +223,201 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
r.Write(terminator);
}
*/
}
result = ms.ToArray();
}
}
return result;
}
result = ms.ToArray();
}
private ZXSTRAMPAGE GetZXSTRAMPAGE(byte page, byte[] RAM)
{
var s = new ZXSTRAMPAGE();
s.wFlags = 0; // uncompressed only at the moment
s.chPageNo = page;
s.ramPage = RAM;
return s;
}
return result;
}
private ZXSTCREATOR GetZXSTCREATOR()
{
var s = new ZXSTCREATOR();
var str = "BIZHAWK EMULATOR".ToCharArray();
s.szCreator = new char[32];
for (int i = 0; i < str.Length; i++)
s.szCreator[i] = str[i];
s.chMajorVersion = 1;
s.chMinorVersion = 4;
private ZXSTRAMPAGE GetZXSTRAMPAGE(byte page, byte[] RAM)
{
var s = new ZXSTRAMPAGE();
s.wFlags = 0; // uncompressed only at the moment
s.chPageNo = page;
s.ramPage = RAM;
return s;
}
return s;
}
private ZXSTCREATOR GetZXSTCREATOR()
{
var s = new ZXSTCREATOR();
var str = "BIZHAWK EMULATOR".ToCharArray();
s.szCreator = new char[32];
for (int i = 0; i < str.Length; i++)
s.szCreator[i] = str[i];
s.chMajorVersion = 1;
s.chMinorVersion = 4;
private ZXSTZ80REGS GetZXSTZ80REGS()
{
var s = new ZXSTZ80REGS();
s.AF = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["AF"].Value;
s.BC = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["BC"].Value;
s.DE = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["DE"].Value;
s.HL = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["HL"].Value;
s.AF1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow AF"].Value;
s.BC1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow BC"].Value;
s.DE1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow DE"].Value;
s.HL1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow HL"].Value;
s.IX = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["IX"].Value;
s.IY = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["IY"].Value;
s.SP = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["SP"].Value;
s.PC = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["PC"].Value;
s.I = (byte)_machine.CPU.Regs[_machine.CPU.I];
s.R = (byte)_machine.CPU.Regs[_machine.CPU.R];
s.IFF1 = (byte)(_machine.CPU.IFF1 ? 1 : 0);
s.IFF2 = (byte)(_machine.CPU.IFF2 ? 1 : 0);
s.IM = (byte)_machine.CPU.InterruptMode;
s.dwCyclesStart = (uint)(_machine.CurrentFrameCycle + _machine.ULADevice.InterruptStartTime);
s.wMemPtr = (ushort)(_machine.CPU.Regs[_machine.CPU.Z] + (_machine.CPU.Regs[_machine.CPU.W] << 8));
//s.chHoldIntReqCycles = ?
if (_machine.CPU.EIPending > 0)
{
s.chFlags |= ZXSTZF_EILAST;
}
else if (_machine.CPU.halted)
{
s.chFlags |= ZXSTZF_HALTED;
}
return s;
}
return s;
}
private ZXSTSPECREGS GetZXSTSPECREGS()
{
var s = new ZXSTSPECREGS();
s.chBorder = _machine.ULADevice.BorderColor > 7 ? (byte)0 : (byte)_machine.ULADevice.BorderColor;
s.chFe = _machine.LastFe;
byte x7ffd = (byte)_machine.RAMPaged;
byte x1ffd = 0;
switch (_machine.Spectrum.MachineType)
{
case MachineType.ZXSpectrum16:
case MachineType.ZXSpectrum48:
s.ch7ffd = 0;
s.unionPage = 0;
break;
private ZXSTZ80REGS GetZXSTZ80REGS()
{
var s = new ZXSTZ80REGS();
s.AF = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["AF"].Value;
s.BC = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["BC"].Value;
s.DE = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["DE"].Value;
s.HL = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["HL"].Value;
s.AF1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow AF"].Value;
s.BC1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow BC"].Value;
s.DE1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow DE"].Value;
s.HL1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow HL"].Value;
s.IX = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["IX"].Value;
s.IY = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["IY"].Value;
s.SP = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["SP"].Value;
s.PC = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["PC"].Value;
s.I = (byte)_machine.CPU.Regs[_machine.CPU.I];
s.R = (byte)_machine.CPU.Regs[_machine.CPU.R];
s.IFF1 = (byte)(_machine.CPU.IFF1 ? 1 : 0);
s.IFF2 = (byte)(_machine.CPU.IFF2 ? 1 : 0);
s.IM = (byte)_machine.CPU.InterruptMode;
s.dwCyclesStart = (uint)(_machine.CurrentFrameCycle + _machine.ULADevice.InterruptStartTime);
s.wMemPtr = (ushort)(_machine.CPU.Regs[_machine.CPU.Z] + (_machine.CPU.Regs[_machine.CPU.W] << 8));
//s.chHoldIntReqCycles = ?
case MachineType.ZXSpectrum128:
case MachineType.ZXSpectrum128Plus2:
// 7FFD
if (_machine._ROMpaged == 1)
x7ffd |= 0x10;
if (_machine.SHADOWPaged)
x7ffd |= 0x08;
if (_machine.PagingDisabled)
x7ffd |= 0x20;
break;
if (_machine.CPU.EIPending > 0)
{
s.chFlags |= ZXSTZF_EILAST;
}
else if (_machine.CPU.halted)
{
s.chFlags |= ZXSTZF_HALTED;
}
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
if (_machine.UPDDiskDevice.FDD_FLAG_MOTOR)
x1ffd |= 0x08;
if (_machine.SpecialPagingMode)
{
x1ffd |= 0x01;
switch (_machine.PagingConfiguration)
{
case 1:
x1ffd |= 0x02;
break;
case 2:
x1ffd |= 0x04;
break;
case 3:
x1ffd |= 0x02;
x1ffd |= 0x04;
break;
}
}
else
{
if (_machine.ROMhigh)
x1ffd |= 0x04;
}
if (_machine.ROMlow)
x7ffd |= 0x10;
if (_machine.SHADOWPaged)
x7ffd |= 0x08;
if (_machine.PagingDisabled)
x7ffd |= 0x20;
break;
}
return s;
}
s.ch7ffd = x7ffd;
s.unionPage = x1ffd;
return s;
}
private ZXSTSPECREGS GetZXSTSPECREGS()
{
var s = new ZXSTSPECREGS();
s.chBorder = _machine.ULADevice.BorderColor > 7 ? (byte)0 : (byte)_machine.ULADevice.BorderColor;
s.chFe = _machine.LastFe;
byte x7ffd = (byte)_machine.RAMPaged;
byte x1ffd = 0;
switch (_machine.Spectrum.MachineType)
{
case MachineType.ZXSpectrum16:
case MachineType.ZXSpectrum48:
s.ch7ffd = 0;
s.unionPage = 0;
break;
private ZXSTKEYBOARD GetZXSTKEYBOARD()
{
var s = new ZXSTKEYBOARD();
s.dwFlags = 0; //no issue 2 emulation
s.chKeyboardJoystick |= (byte)JoystickTypes.ZXSTKJT_NONE;
return s;
}
case MachineType.ZXSpectrum128:
case MachineType.ZXSpectrum128Plus2:
// 7FFD
if (_machine._ROMpaged == 1)
x7ffd |= 0x10;
if (_machine.SHADOWPaged)
x7ffd |= 0x08;
if (_machine.PagingDisabled)
x7ffd |= 0x20;
break;
private ZXSTJOYSTICK GetZXSTJOYSTICK()
{
var s = new ZXSTJOYSTICK();
s.dwFlags = 0; //depreciated
s.chTypePlayer1 |= (byte)JoystickTypes.ZXSTKJT_KEMPSTON;
s.chTypePlayer2 |= (byte)JoystickTypes.ZXSTKJT_SINCLAIR1;
return s;
}
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
if (_machine.UPDDiskDevice.FDD_FLAG_MOTOR)
x1ffd |= 0x08;
if (_machine.SpecialPagingMode)
{
x1ffd |= 0x01;
switch (_machine.PagingConfiguration)
{
case 1:
x1ffd |= 0x02;
break;
case 2:
x1ffd |= 0x04;
break;
case 3:
x1ffd |= 0x02;
x1ffd |= 0x04;
break;
}
}
else
{
if (_machine.ROMhigh)
x1ffd |= 0x04;
}
if (_machine.ROMlow)
x7ffd |= 0x10;
if (_machine.SHADOWPaged)
x7ffd |= 0x08;
if (_machine.PagingDisabled)
x7ffd |= 0x20;
break;
}
private ZXSTAYBLOCK GetZXSTAYBLOCK()
{
var s = new ZXSTAYBLOCK();
s.cFlags = 0; // no external units
s.chCurrentRegister = (byte)_machine.AYDevice.SelectedRegister;
var regs = _machine.AYDevice.ExportRegisters();
s.chAyRegs = new byte[16];
for (int i = 0; i < 16; i++)
{
s.chAyRegs[i] = (byte)regs[i];
}
return s;
}
s.ch7ffd = x7ffd;
s.unionPage = x1ffd;
return s;
}
private ZXSTTAPE GetZXSTTAPE()
{
var s = new ZXSTTAPE();
s.wFlags |= (int)CassetteRecorderState.ZXSTTP_EMBEDDED;
s.wCurrentBlockNo = (ushort)_machine.TapeDevice.CurrentDataBlockIndex;
s.dwCompressedSize = _machine.tapeImages[_machine.TapeDevice.CurrentDataBlockIndex].Length;
s.dwUncompressedSize = _machine.tapeImages[_machine.TapeDevice.CurrentDataBlockIndex].Length;
char[] ext = "tzx".ToCharArray();
s.szFileExtension = new char[16];
for (int f = 1; f < ext.Length; f++)
{
s.szFileExtension[f - 1] = ext[f];
}
return s;
}
private ZXSTKEYBOARD GetZXSTKEYBOARD()
{
var s = new ZXSTKEYBOARD();
s.dwFlags = 0; //no issue 2 emulation
s.chKeyboardJoystick |= (byte)JoystickTypes.ZXSTKJT_NONE;
return s;
}
private ZXSTPLUS3 GetZXSTPLUS3()
{
var s = new ZXSTPLUS3();
s.chNumDrives = 1;
s.fMotorOn = _machine.UPDDiskDevice.FDD_FLAG_MOTOR ? (byte)1 : (byte)0;
return s;
}
private ZXSTJOYSTICK GetZXSTJOYSTICK()
{
var s = new ZXSTJOYSTICK();
s.dwFlags = 0; //depreciated
s.chTypePlayer1 |= (byte)JoystickTypes.ZXSTKJT_KEMPSTON;
s.chTypePlayer2 |= (byte)JoystickTypes.ZXSTKJT_SINCLAIR1;
return s;
}
private ZXSTDSKFILE GetZXSTDSKFILE()
{
var s = new ZXSTDSKFILE();
s.wFlags = 0;
s.chDriveNum = 0;
s.dwUncompressedSize = 0;
return s;
}
}
private ZXSTAYBLOCK GetZXSTAYBLOCK()
{
var s = new ZXSTAYBLOCK();
s.cFlags = 0; // no external units
s.chCurrentRegister = (byte)_machine.AYDevice.SelectedRegister;
var regs = _machine.AYDevice.ExportRegisters();
s.chAyRegs = new byte[16];
for (int i = 0; i < 16; i++)
{
s.chAyRegs[i] = (byte)regs[i];
}
return s;
}
private ZXSTTAPE GetZXSTTAPE()
{
var s = new ZXSTTAPE();
s.wFlags |= (int)CassetteRecorderState.ZXSTTP_EMBEDDED;
s.wCurrentBlockNo = (ushort)_machine.TapeDevice.CurrentDataBlockIndex;
s.dwCompressedSize = _machine.tapeImages[_machine.TapeDevice.CurrentDataBlockIndex].Length;
s.dwUncompressedSize = _machine.tapeImages[_machine.TapeDevice.CurrentDataBlockIndex].Length;
char[] ext = "tzx".ToCharArray();
s.szFileExtension = new char[16];
for (int f = 1; f < ext.Length; f++)
{
s.szFileExtension[f - 1] = ext[f];
}
return s;
}
private ZXSTPLUS3 GetZXSTPLUS3()
{
var s = new ZXSTPLUS3();
s.chNumDrives = 1;
s.fMotorOn = _machine.UPDDiskDevice.FDD_FLAG_MOTOR ? (byte)1 : (byte)0;
return s;
}
private ZXSTDSKFILE GetZXSTDSKFILE()
{
var s = new ZXSTDSKFILE();
s.wFlags = 0;
s.chDriveNum = 0;
s.dwUncompressedSize = 0;
return s;
}
}
}

View File

@ -7,404 +7,404 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Structs, Constants and Enums
/// http://www.spectaculator.com/docs/zx-state/intro.shtml
/// </summary>
public partial class SZX
{
#region ZX-State Header
public enum MachineIdentifier : byte
{
ZXSTMID_16K = 0,
ZXSTMID_48K = 1,
ZXSTMID_128K = 2,
ZXSTMID_PLUS2 = 3,
ZXSTMID_PLUS2A = 4,
ZXSTMID_PLUS3 = 5,
ZXSTMID_PLUS3E = 6,
ZXSTMID_PENTAGON128 = 7,
ZXSTMID_TC2048 = 8,
ZXSTMID_TC2068 = 9,
ZXSTMID_SCORPION = 10,
ZXSTMID_SE = 11,
ZXSTMID_TS2068 = 12,
ZXSTMID_PENTAGON512 = 13,
ZXSTMID_PENTAGON1024 = 14,
ZXSTMID_NTSC48K = 15,
ZXSTMID_128KE = 16
}
/// <summary>
/// If set, the emulated Spectrum uses alternate timings (one cycle later than normal timings). If reset, the emulated Spectrum uses standard timings.
/// This flag is only applicable for the ZXSTMID_16K, ZXSTMID_48K and ZXSTMID_128K models.
/// </summary>
public const int ZXSTMF_ALTERNATETIMINGS = 1;
/// <summary>
/// The zx-state header appears right at the start of a zx-state (.szx) file.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTHEADER
{
public uint dwMagic;
public byte chMajorVersion;
public byte chMinorVersion;
public byte chMachineId;
public byte chFlags;
}
#endregion
#region ZXSTBLOCK Header
/// <summary>
/// Block Header. Each real block starts with this header.
/// </summary>
public struct ZXSTBLOCK
{
public uint dwId;
public uint dwSize;
}
#endregion
#region ZXSTCREATOR
/// <summary>
/// This block identifies the program that created this zx-state file.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTCREATOR
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] szCreator;
public short chMajorVersion;
public short chMinorVersion;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] chData;
}
#endregion
#region ZXSTZ80REGS
/// <summary>
/// The last instruction executed was an EI instruction or an invalid $DD or $FD prefix.
/// </summary>
public const int ZXSTZF_EILAST = 1;
/// <summary>
/// The last instruction executed was a HALT instruction. The CPU is currently executing NOPs and will continue to do so until the next interrupt occurs.
/// This flag is mutually exclusive with ZXSTZF_EILAST.
/// </summary>
public const int ZXSTZF_HALTED = 2;
/// <summary>
/// Contains the Z80 registers and other internal state values. It does not contain any specific model registers.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTZ80REGS
{
public ushort AF, BC, DE, HL;
public ushort AF1, BC1, DE1, HL1;
public ushort IX, IY, SP, PC;
public byte I;
public byte R;
public byte IFF1, IFF2;
public byte IM;
public uint dwCyclesStart;
public byte chHoldIntReqCycles;
public byte chFlags;
public ushort wMemPtr;
}
#endregion
#region ZXSTSPECREGS
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTSPECREGS
{
public byte chBorder;
public byte ch7ffd;
public byte unionPage;
public byte chFe;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] chReserved;
}
#endregion
#region ZXSTAYBLOCK
/// <summary>
/// Fuller Box emulation
/// </summary>
public const int ZXSTAYF_FULLERBOX = 1;
/// <summary>
/// Melodik Soundbox emulation.
/// This is essentially an AY chip for older Spectrums that uses the same ports as that found in 128k Spectrums
/// </summary>
public const int ZXSTAYF_128AY = 2;
/// <summary>
/// The state of the AY chip found in all 128k Spectrums, Pentagons, Scorpions and Timex machines.
/// This block may also be present for 16k/48k Spectrums if Fuller Box or Melodik emulation is enabled.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTAYBLOCK
{
public byte cFlags;
public byte chCurrentRegister;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] chAyRegs;
}
#endregion
#region ZXSTRAMPAGE
/// <summary>
/// Ram pages are compressed using Zlib
/// </summary>
public const int ZXSTRF_COMPRESSED = 1;
/// <summary>
/// zx-state files will contain a number of 16KB RAM page blocks, depending on the specific Spectrum model.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTRAMPAGE
{
public ushort wFlags;
public byte chPageNo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000)]
public byte[] ramPage;
}
#endregion
#region ZXSTKEYBOARD
/// <summary>
/// Keyboard state
/// </summary>
public const int ZXSTKF_ISSUE2 = 1;
/// <summary>
/// Supported joystick types
/// </summary>
public enum JoystickTypes
{
ZXSTKJT_KEMPSTON = 0,
ZXSTKJT_FULLER = 1,
ZXSTKJT_CURSOR = 2,
ZXSTKJT_SINCLAIR1 = 3,
ZXSTKJT_SINCLAIR2 = 4,
ZXSTKJT_SPECTRUMPLUS = 5,
ZXSTKJT_TIMEX1 = 6,
ZXSTKJT_TIMEX2 = 7,
ZXSTKJT_NONE = 8
}
/// <summary>
/// The state of the Spectrum keyboard and any keyboard joystick emulation.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTKEYBOARD
{
public uint dwFlags;
public byte chKeyboardJoystick;
}
#endregion
#region ZXSTJOYSTICK
/// <summary>
/// Joystick setup for both players.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTJOYSTICK
{
public uint dwFlags;
public byte chTypePlayer1;
public byte chTypePlayer2;
}
#endregion
#region ZXSTTAPE
/// <summary>
/// Cassette Recorder state
/// </summary>
public enum CassetteRecorderState
{
ZXSTTP_EMBEDDED = 1,
ZXSTTP_COMPRESSED = 2
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTTAPE
{
public ushort wCurrentBlockNo;
public ushort wFlags;
public int dwUncompressedSize;
public int dwCompressedSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public char[] szFileExtension;
}
/// <summary>
/// Structs, Constants and Enums
/// http://www.spectaculator.com/docs/zx-state/intro.shtml
/// </summary>
public partial class SZX
{
#region ZX-State Header
public enum MachineIdentifier : byte
{
ZXSTMID_16K = 0,
ZXSTMID_48K = 1,
ZXSTMID_128K = 2,
ZXSTMID_PLUS2 = 3,
ZXSTMID_PLUS2A = 4,
ZXSTMID_PLUS3 = 5,
ZXSTMID_PLUS3E = 6,
ZXSTMID_PENTAGON128 = 7,
ZXSTMID_TC2048 = 8,
ZXSTMID_TC2068 = 9,
ZXSTMID_SCORPION = 10,
ZXSTMID_SE = 11,
ZXSTMID_TS2068 = 12,
ZXSTMID_PENTAGON512 = 13,
ZXSTMID_PENTAGON1024 = 14,
ZXSTMID_NTSC48K = 15,
ZXSTMID_128KE = 16
}
/// <summary>
/// If set, the emulated Spectrum uses alternate timings (one cycle later than normal timings). If reset, the emulated Spectrum uses standard timings.
/// This flag is only applicable for the ZXSTMID_16K, ZXSTMID_48K and ZXSTMID_128K models.
/// </summary>
public const int ZXSTMF_ALTERNATETIMINGS = 1;
/// <summary>
/// The zx-state header appears right at the start of a zx-state (.szx) file.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTHEADER
{
public uint dwMagic;
public byte chMajorVersion;
public byte chMinorVersion;
public byte chMachineId;
public byte chFlags;
}
#endregion
#region ZXSTBLOCK Header
/// <summary>
/// Block Header. Each real block starts with this header.
/// </summary>
public struct ZXSTBLOCK
{
public uint dwId;
public uint dwSize;
}
#endregion
#region ZXSTCREATOR
/// <summary>
/// This block identifies the program that created this zx-state file.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTCREATOR
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] szCreator;
public short chMajorVersion;
public short chMinorVersion;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] chData;
}
#endregion
#region ZXSTZ80REGS
/// <summary>
/// The last instruction executed was an EI instruction or an invalid $DD or $FD prefix.
/// </summary>
public const int ZXSTZF_EILAST = 1;
/// <summary>
/// The last instruction executed was a HALT instruction. The CPU is currently executing NOPs and will continue to do so until the next interrupt occurs.
/// This flag is mutually exclusive with ZXSTZF_EILAST.
/// </summary>
public const int ZXSTZF_HALTED = 2;
/// <summary>
/// Contains the Z80 registers and other internal state values. It does not contain any specific model registers.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTZ80REGS
{
public ushort AF, BC, DE, HL;
public ushort AF1, BC1, DE1, HL1;
public ushort IX, IY, SP, PC;
public byte I;
public byte R;
public byte IFF1, IFF2;
public byte IM;
public uint dwCyclesStart;
public byte chHoldIntReqCycles;
public byte chFlags;
public ushort wMemPtr;
}
#endregion
#region ZXSTSPECREGS
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTSPECREGS
{
public byte chBorder;
public byte ch7ffd;
public byte unionPage;
public byte chFe;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] chReserved;
}
#endregion
#region ZXSTAYBLOCK
/// <summary>
/// Fuller Box emulation
/// </summary>
public const int ZXSTAYF_FULLERBOX = 1;
/// <summary>
/// Melodik Soundbox emulation.
/// This is essentially an AY chip for older Spectrums that uses the same ports as that found in 128k Spectrums
/// </summary>
public const int ZXSTAYF_128AY = 2;
/// <summary>
/// The state of the AY chip found in all 128k Spectrums, Pentagons, Scorpions and Timex machines.
/// This block may also be present for 16k/48k Spectrums if Fuller Box or Melodik emulation is enabled.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTAYBLOCK
{
public byte cFlags;
public byte chCurrentRegister;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] chAyRegs;
}
#endregion
#region ZXSTRAMPAGE
/// <summary>
/// Ram pages are compressed using Zlib
/// </summary>
public const int ZXSTRF_COMPRESSED = 1;
/// <summary>
/// zx-state files will contain a number of 16KB RAM page blocks, depending on the specific Spectrum model.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTRAMPAGE
{
public ushort wFlags;
public byte chPageNo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000)]
public byte[] ramPage;
}
#endregion
#region ZXSTKEYBOARD
/// <summary>
/// Keyboard state
/// </summary>
public const int ZXSTKF_ISSUE2 = 1;
/// <summary>
/// Supported joystick types
/// </summary>
public enum JoystickTypes
{
ZXSTKJT_KEMPSTON = 0,
ZXSTKJT_FULLER = 1,
ZXSTKJT_CURSOR = 2,
ZXSTKJT_SINCLAIR1 = 3,
ZXSTKJT_SINCLAIR2 = 4,
ZXSTKJT_SPECTRUMPLUS = 5,
ZXSTKJT_TIMEX1 = 6,
ZXSTKJT_TIMEX2 = 7,
ZXSTKJT_NONE = 8
}
/// <summary>
/// The state of the Spectrum keyboard and any keyboard joystick emulation.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTKEYBOARD
{
public uint dwFlags;
public byte chKeyboardJoystick;
}
#endregion
#region ZXSTJOYSTICK
/// <summary>
/// Joystick setup for both players.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTJOYSTICK
{
public uint dwFlags;
public byte chTypePlayer1;
public byte chTypePlayer2;
}
#endregion
#region ZXSTTAPE
/// <summary>
/// Cassette Recorder state
/// </summary>
public enum CassetteRecorderState
{
ZXSTTP_EMBEDDED = 1,
ZXSTTP_COMPRESSED = 2
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTTAPE
{
public ushort wCurrentBlockNo;
public ushort wFlags;
public int dwUncompressedSize;
public int dwCompressedSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public char[] szFileExtension;
}
#endregion
#endregion
#region ZXSTPLUS3
#region ZXSTPLUS3
/// <summary>
/// The number of drives connected to the Spectrum +3 and whether their motors are turned on.
/// Any blocks specifying which disk files are in which drive will follow this one.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTPLUS3
{
public byte chNumDrives;
public byte fMotorOn;
}
/// <summary>
/// The number of drives connected to the Spectrum +3 and whether their motors are turned on.
/// Any blocks specifying which disk files are in which drive will follow this one.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTPLUS3
{
public byte chNumDrives;
public byte fMotorOn;
}
#endregion
#endregion
#region ZXSTDSKFILE
#region ZXSTDSKFILE
/// <summary>
/// Not implemented. All disk images are currently links to external .dsk or .ipf files
/// </summary>
public const int ZXSTDSKF_COMPRESSED = 1;
/// <summary>
/// Not implemented. All disk images are currently links to external .dsk or .ipf files
/// </summary>
public const int ZXSTDSKF_EMBEDDED = 2;
/// <summary>
/// When a double-sided disk is inserted into a single-sided drive, specifies the side being read from/written to.
/// If set, Side B is the active side, otherwise it is Side A.
/// </summary>
public const int ZXSTDSKF_SIDEB = 3;
/// <summary>
/// Not implemented. All disk images are currently links to external .dsk or .ipf files
/// </summary>
public const int ZXSTDSKF_COMPRESSED = 1;
/// <summary>
/// Not implemented. All disk images are currently links to external .dsk or .ipf files
/// </summary>
public const int ZXSTDSKF_EMBEDDED = 2;
/// <summary>
/// When a double-sided disk is inserted into a single-sided drive, specifies the side being read from/written to.
/// If set, Side B is the active side, otherwise it is Side A.
/// </summary>
public const int ZXSTDSKF_SIDEB = 3;
/// <summary>
/// Each +3 disk drive that has a disk inserted in it will have one of these blocks.
/// They follow the ZXSTPLUS3 block which identifies the number of drives.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTDSKFILE
{
public ushort wFlags;
public byte chDriveNum;
public int dwUncompressedSize;
}
/// <summary>
/// Each +3 disk drive that has a disk inserted in it will have one of these blocks.
/// They follow the ZXSTPLUS3 block which identifies the number of drives.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTDSKFILE
{
public ushort wFlags;
public byte chDriveNum;
public int dwUncompressedSize;
}
#endregion
#endregion
#region Not Yet Implemented
#region Not Yet Implemented
#region ZXSTATASP
#region ZXSTATASP
#endregion
#endregion
#region ZXSTATARAM
#region ZXSTATARAM
#endregion
#endregion
#region ZXSTCF
#region ZXSTCF
#endregion
#endregion
#region ZXSTCFRAM
#region ZXSTCFRAM
#endregion
#endregion
#region ZXSTCOVOX
#region ZXSTCOVOX
#endregion
#endregion
#region ZXSTBETA128
#region ZXSTBETA128
#endregion
#endregion
#region ZXSTBETADISK
#region ZXSTBETADISK
#endregion
#endregion
#region ZXSTDOCK
#region ZXSTDOCK
#endregion
#endregion
#region ZXSTGS
#region ZXSTGS
#endregion
#endregion
#region ZXSTGSRAMPAGE
#region ZXSTGSRAMPAGE
#endregion
#endregion
#region ZXSTIF1
#region ZXSTIF1
#endregion
#endregion
#region ZXSTIF2ROM
#region ZXSTIF2ROM
#endregion
#endregion
#region ZXSTMCART
#region ZXSTMCART
#endregion
#endregion
#region ZXSTMOUSE
#region ZXSTMOUSE
#endregion
#endregion
#region ZXSTMULTIFACE
#region ZXSTMULTIFACE
#endregion
#endregion
#region ZXSTOPUS
#region ZXSTOPUS
#endregion
#endregion
#region ZXSTOPUSDISK
#region ZXSTOPUSDISK
#endregion
#endregion
#region ZXSTPLUSD
#region ZXSTPLUSD
#endregion
#endregion
#region ZXSTPLUSDDISK
#region ZXSTPLUSDDISK
#endregion
#endregion
#region ZXSTROM
#region ZXSTROM
#endregion
#endregion
#region ZXSTSCLDREGS
#region ZXSTSCLDREGS
#endregion
#endregion
#region ZXSTSIDE
#region ZXSTSIDE
#endregion
#endregion
#region ZXSTSPECDRUM
#region ZXSTSPECDRUM
#endregion
#endregion
#region ZXSTUSPEECH
#region ZXSTUSPEECH
#endregion
#endregion
#region ZXSTZXPRINTER
#region ZXSTZXPRINTER
#endregion
#endregion
#endregion
}
#endregion
}
}

Some files were not shown because too many files have changed in this diff Show More