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

View File

@ -4,146 +4,146 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// CPCHawk: Core Class /// CPCHawk: Core Class
/// * IDebugggable * /// * IDebugggable *
/// </summary> /// </summary>
public partial class AmstradCPC : IDebuggable public partial class AmstradCPC : IDebuggable
{ {
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters() public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{ {
return new Dictionary<string, RegisterValue> return new Dictionary<string, RegisterValue>
{ {
["A"] = _cpu.Regs[_cpu.A], ["A"] = _cpu.Regs[_cpu.A],
["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8), ["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8),
["B"] = _cpu.Regs[_cpu.B], ["B"] = _cpu.Regs[_cpu.B],
["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8), ["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8),
["C"] = _cpu.Regs[_cpu.C], ["C"] = _cpu.Regs[_cpu.C],
["D"] = _cpu.Regs[_cpu.D], ["D"] = _cpu.Regs[_cpu.D],
["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8), ["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8),
["E"] = _cpu.Regs[_cpu.E], ["E"] = _cpu.Regs[_cpu.E],
["F"] = _cpu.Regs[_cpu.F], ["F"] = _cpu.Regs[_cpu.F],
["H"] = _cpu.Regs[_cpu.H], ["H"] = _cpu.Regs[_cpu.H],
["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8), ["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8),
["I"] = _cpu.Regs[_cpu.I], ["I"] = _cpu.Regs[_cpu.I],
["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8), ["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8),
["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), ["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8),
["L"] = _cpu.Regs[_cpu.L], ["L"] = _cpu.Regs[_cpu.L],
["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8), ["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8),
["R"] = _cpu.Regs[_cpu.R], ["R"] = _cpu.Regs[_cpu.R],
["Shadow AF"] = _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8), ["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 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 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), ["Shadow HL"] = _cpu.Regs[_cpu.L_s] + (_cpu.Regs[_cpu.H_s] << 8),
["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), ["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8),
["Flag C"] = _cpu.FlagC, ["Flag C"] = _cpu.FlagC,
["Flag N"] = _cpu.FlagN, ["Flag N"] = _cpu.FlagN,
["Flag P/V"] = _cpu.FlagP, ["Flag P/V"] = _cpu.FlagP,
["Flag 3rd"] = _cpu.Flag3, ["Flag 3rd"] = _cpu.Flag3,
["Flag H"] = _cpu.FlagH, ["Flag H"] = _cpu.FlagH,
["Flag 5th"] = _cpu.Flag5, ["Flag 5th"] = _cpu.Flag5,
["Flag Z"] = _cpu.FlagZ, ["Flag Z"] = _cpu.FlagZ,
["Flag S"] = _cpu.FlagS ["Flag S"] = _cpu.FlagS
}; };
} }
public void SetCpuRegister(string register, int value) public void SetCpuRegister(string register, int value)
{ {
switch (register) switch (register)
{ {
default: default:
throw new InvalidOperationException(); throw new InvalidOperationException();
case "A": case "A":
_cpu.Regs[_cpu.A] = (ushort)value; _cpu.Regs[_cpu.A] = (ushort)value;
break; break;
case "AF": case "AF":
_cpu.Regs[_cpu.F] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.F] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00);
break; break;
case "B": case "B":
_cpu.Regs[_cpu.B] = (ushort)value; _cpu.Regs[_cpu.B] = (ushort)value;
break; break;
case "BC": case "BC":
_cpu.Regs[_cpu.C] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.C] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00);
break; break;
case "C": case "C":
_cpu.Regs[_cpu.C] = (ushort)value; _cpu.Regs[_cpu.C] = (ushort)value;
break; break;
case "D": case "D":
_cpu.Regs[_cpu.D] = (ushort)value; _cpu.Regs[_cpu.D] = (ushort)value;
break; break;
case "DE": case "DE":
_cpu.Regs[_cpu.E] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.E] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00);
break; break;
case "E": case "E":
_cpu.Regs[_cpu.E] = (ushort)value; _cpu.Regs[_cpu.E] = (ushort)value;
break; break;
case "F": case "F":
_cpu.Regs[_cpu.F] = (ushort)value; _cpu.Regs[_cpu.F] = (ushort)value;
break; break;
case "H": case "H":
_cpu.Regs[_cpu.H] = (ushort)value; _cpu.Regs[_cpu.H] = (ushort)value;
break; break;
case "HL": case "HL":
_cpu.Regs[_cpu.L] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.L] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00);
break; break;
case "I": case "I":
_cpu.Regs[_cpu.I] = (ushort)value; _cpu.Regs[_cpu.I] = (ushort)value;
break; break;
case "IX": case "IX":
_cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00);
break; break;
case "IY": case "IY":
_cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00);
break; break;
case "L": case "L":
_cpu.Regs[_cpu.L] = (ushort)value; _cpu.Regs[_cpu.L] = (ushort)value;
break; break;
case "PC": case "PC":
_cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00);
break; break;
case "R": case "R":
_cpu.Regs[_cpu.R] = (ushort)value; _cpu.Regs[_cpu.R] = (ushort)value;
break; break;
case "Shadow AF": case "Shadow AF":
_cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00);
break; break;
case "Shadow BC": case "Shadow BC":
_cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00);
break; break;
case "Shadow DE": case "Shadow DE":
_cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00);
break; break;
case "Shadow HL": case "Shadow HL":
_cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00);
break; break;
case "SP": case "SP":
_cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF); _cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00); _cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00);
break; break;
} }
} }
public IMemoryCallbackSystem MemoryCallbacks { get; } public IMemoryCallbackSystem MemoryCallbacks { get; }
public bool CanStep(StepType type) => false; public bool CanStep(StepType type) => false;
[FeatureNotImplemented] [FeatureNotImplemented]
public void Step(StepType type) public void Step(StepType type)
{ {
throw new NotImplementedException(); 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 namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// CPCHawk: Core Class /// CPCHawk: Core Class
/// * IEmulator * /// * IEmulator *
/// </summary> /// </summary>
public partial class AmstradCPC : IEmulator public partial class AmstradCPC : IEmulator
{ {
public IEmulatorServiceProvider ServiceProvider { get; } public IEmulatorServiceProvider ServiceProvider { get; }
public ControllerDefinition ControllerDefinition { get; set; } public ControllerDefinition ControllerDefinition { get; set; }
public bool FrameAdvance(IController controller, bool render, bool renderSound) public bool FrameAdvance(IController controller, bool render, bool renderSound)
{ {
_controller = controller; _controller = controller;
bool ren = render; bool ren = render;
bool renSound = renderSound; bool renSound = renderSound;
if (DeterministicEmulation) if (DeterministicEmulation)
{ {
ren = true; ren = true;
renSound = true; renSound = true;
} }
_isLag = true; _isLag = true;
if (_tracer.Enabled) if (_tracer.Enabled)
{ {
_cpu.TraceCallback = s => _tracer.Put(s); _cpu.TraceCallback = s => _tracer.Put(s);
} }
else else
{ {
_cpu.TraceCallback = null; _cpu.TraceCallback = null;
} }
_machine.ExecuteFrame(ren, renSound); _machine.ExecuteFrame(ren, renSound);
if (_isLag) if (_isLag)
{ {
_lagCount++; _lagCount++;
} }
return true; return true;
} }
public int Frame public int Frame
{ {
get get
{ {
if (_machine == null) if (_machine == null)
return 0; return 0;
else else
return _machine.FrameCount; return _machine.FrameCount;
} }
} }
public string SystemId => "AmstradCPC"; public string SystemId => "AmstradCPC";
private bool deterministicEmulation; private bool deterministicEmulation;
public bool DeterministicEmulation public bool DeterministicEmulation
{ {
get { return deterministicEmulation; } get { return deterministicEmulation; }
} }
public void ResetCounters() public void ResetCounters()
{ {
_machine.FrameCount = 0; _machine.FrameCount = 0;
_lagCount = 0; _lagCount = 0;
_isLag = false; _isLag = false;
} }
public CoreComm CoreComm { get; } public CoreComm CoreComm { get; }
public void Dispose() public void Dispose()
{ {
if (_machine != null) if (_machine != null)
{ {
_machine = null; _machine = null;
} }
} }
} }
} }

View File

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

View File

@ -6,190 +6,190 @@ using System.Text;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// CPCHawk: Core Class /// CPCHawk: Core Class
/// * ISettable * /// * ISettable *
/// </summary> /// </summary>
public partial class AmstradCPC : ISettable<AmstradCPC.AmstradCPCSettings, AmstradCPC.AmstradCPCSyncSettings> public partial class AmstradCPC : ISettable<AmstradCPC.AmstradCPCSettings, AmstradCPC.AmstradCPCSyncSettings>
{ {
internal AmstradCPCSettings Settings = new AmstradCPCSettings(); internal AmstradCPCSettings Settings = new AmstradCPCSettings();
internal AmstradCPCSyncSettings SyncSettings = new AmstradCPCSyncSettings(); internal AmstradCPCSyncSettings SyncSettings = new AmstradCPCSyncSettings();
public AmstradCPCSettings GetSettings() public AmstradCPCSettings GetSettings()
{ {
return Settings.Clone(); return Settings.Clone();
} }
public AmstradCPCSyncSettings GetSyncSettings() public AmstradCPCSyncSettings GetSyncSettings()
{ {
return SyncSettings.Clone(); return SyncSettings.Clone();
} }
public bool PutSettings(AmstradCPCSettings o) 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;
}
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) Settings = o;
{
bool ret = AmstradCPCSyncSettings.NeedsReboot(SyncSettings, o);
SyncSettings = o;
return ret;
}
public class AmstradCPCSettings return false;
{ }
[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("AY-3-8912 Volume")] public bool PutSyncSettings(AmstradCPCSyncSettings o)
[Description("The AY chip volume")] {
[DefaultValue(75)] bool ret = AmstradCPCSyncSettings.NeedsReboot(SyncSettings, o);
public int AYVolume { get; set; } SyncSettings = o;
return ret;
}
[DisplayName("Core OSD Message Verbosity")] public class AmstradCPCSettings
[Description("Full: Display all GUI messages\nMedium: Display only emulator/device generated messages\nNone: Show no messages")] {
[DefaultValue(OSDVerbosity.Medium)] [DisplayName("AY-3-8912 Panning Config")]
public OSDVerbosity OSDMessageVerbosity { get; set; } [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")] [DisplayName("AY-3-8912 Volume")]
[Description("The buzzer volume when the tape is playing")] [Description("The AY chip volume")]
[DefaultValue(50)] [DefaultValue(75)]
public int TapeVolume { get; set; } public int AYVolume { get; set; }
public AmstradCPCSettings Clone() [DisplayName("Core OSD Message Verbosity")]
{ [Description("Full: Display all GUI messages\nMedium: Display only emulator/device generated messages\nNone: Show no messages")]
return (AmstradCPCSettings)MemberwiseClone(); [DefaultValue(OSDVerbosity.Medium)]
} public OSDVerbosity OSDMessageVerbosity { get; set; }
public AmstradCPCSettings() [DisplayName("Tape Loading Volume")]
{ [Description("The buzzer volume when the tape is playing")]
BizHawk.Common.SettingsUtil.SetDefaultValues(this); [DefaultValue(50)]
} public int TapeVolume { get; set; }
}
public class AmstradCPCSyncSettings public AmstradCPCSettings Clone()
{ {
[DisplayName("Deterministic Emulation")] return (AmstradCPCSettings)MemberwiseClone();
[Description("If true, the core agrees to behave in a completely deterministic manner")] }
[DefaultValue(true)]
public bool DeterministicEmulation { get; set; }
[DisplayName("CPC Model")] public AmstradCPCSettings()
[Description("The model of Amstrad CPC machine to be emulated")] {
[DefaultValue(MachineType.CPC464)] BizHawk.Common.SettingsUtil.SetDefaultValues(this);
public MachineType MachineType { get; set; } }
}
[DisplayName("Auto Start/Stop Tape")] public class AmstradCPCSyncSettings
[Description("If true, CPCHawk will automatically start and stop the tape when the tape motor is triggered")] {
[DefaultValue(true)] [DisplayName("Deterministic Emulation")]
public bool AutoStartStopTape { get; set; } [Description("If true, the core agrees to behave in a completely deterministic manner")]
[DefaultValue(true)]
public bool DeterministicEmulation { get; set; }
[DisplayName("Border type")] [DisplayName("CPC Model")]
[Description("Select how to show the border area")] [Description("The model of Amstrad CPC machine to be emulated")]
[DefaultValue(BorderType.Uniform)] [DefaultValue(MachineType.CPC464)]
public BorderType BorderType { get; set; } public MachineType MachineType { get; set; }
public AmstradCPCSyncSettings Clone() [DisplayName("Auto Start/Stop Tape")]
{ [Description("If true, CPCHawk will automatically start and stop the tape when the tape motor is triggered")]
return (AmstradCPCSyncSettings)MemberwiseClone(); [DefaultValue(true)]
} public bool AutoStartStopTape { get; set; }
public AmstradCPCSyncSettings() [DisplayName("Border type")]
{ [Description("Select how to show the border area")]
BizHawk.Common.SettingsUtil.SetDefaultValues(this); [DefaultValue(BorderType.Uniform)]
} public BorderType BorderType { get; set; }
public static bool NeedsReboot(AmstradCPCSyncSettings x, AmstradCPCSyncSettings y) public AmstradCPCSyncSettings Clone()
{ {
return !DeepEquality.DeepEquals(x, y); return (AmstradCPCSyncSettings)MemberwiseClone();
} }
}
/// <summary> public AmstradCPCSyncSettings()
/// Verbosity of the CPCHawk generated OSD messages {
/// </summary> BizHawk.Common.SettingsUtil.SetDefaultValues(this);
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> public static bool NeedsReboot(AmstradCPCSyncSettings x, AmstradCPCSyncSettings y)
/// Provides information on each emulated machine {
/// </summary> return !DeepEquality.DeepEquals(x, y);
public class CPCMachineMetaData }
{ }
public MachineType MachineType { get; set; }
public string Name { get; set; } /// <summary>
public string Description { get; set; } /// Verbosity of the CPCHawk generated OSD messages
public string Released { get; set; } /// </summary>
public string CPU { get; set; } public enum OSDVerbosity
public string Memory { get; set; } {
public string Video { get; set; } /// <summary>
public string Audio { get; set; } /// Show all OSD messages
public string Media { get; set; } /// </summary>
public string OtherMisc { get; set; } 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>(); Dictionary<string, string> Data = new Dictionary<string, string>();
public static CPCMachineMetaData GetMetaObject(MachineType type) public static CPCMachineMetaData GetMetaObject(MachineType type)
{ {
CPCMachineMetaData m = new CPCMachineMetaData(); CPCMachineMetaData m = new CPCMachineMetaData();
m.MachineType = type; m.MachineType = type;
switch (type) switch (type)
{ {
case MachineType.CPC464: case MachineType.CPC464:
m.Name = "Amstrad CPC 464"; m.Name = "Amstrad CPC 464";
m.Description = "The CPC 464 was the first personal home computer built by Amstrad in 1984. "; 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.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.Released = "1984";
m.CPU = "Zilog Z80A @ 4MHz"; m.CPU = "Zilog Z80A @ 4MHz";
m.Memory = "64KB RAM / 32KB ROM"; m.Memory = "64KB RAM / 32KB ROM";
m.Video = "Amstrad Gate Array @ 16Mhz & CRCT @ 1Mhz"; m.Video = "Amstrad Gate Array @ 16Mhz & CRCT @ 1Mhz";
m.Audio = "General Instruments AY-3-8912 PSG (3ch)"; m.Audio = "General Instruments AY-3-8912 PSG (3ch)";
m.Media = "Cassette Tape (via built-in Datacorder)"; m.Media = "Cassette Tape (via built-in Datacorder)";
break; break;
case MachineType.CPC6128: case MachineType.CPC6128:
m.Name = "Amstrad CPC 6128"; m.Name = "Amstrad CPC 6128";
m.Description = "The CPC6128 features 128 KB RAM and an internal 3-inch floppy disk drive. "; 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.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.Released = "1985";
m.CPU = "Zilog Z80A @ 4MHz"; m.CPU = "Zilog Z80A @ 4MHz";
m.Memory = "64KB RAM / 32KB ROM"; m.Memory = "64KB RAM / 32KB ROM";
m.Video = "Amstrad Gate Array @ 16Mhz & CRCT @ 1Mhz"; m.Video = "Amstrad Gate Array @ 16Mhz & CRCT @ 1Mhz";
m.Audio = "General Instruments AY-3-8912 PSG (3ch)"; 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)"; m.Media = "3\" Floppy Disk (via built-in Floppy Drive) & Cassette Tape (via external cassette player)";
break; break;
} }
m.Data.Add(AmstradCPC.GetMemberName((CPCMachineMetaData c) => c.Name), m.Name.Trim()); m.Data.Add(AmstradCPC.GetMemberName((CPCMachineMetaData c) => c.Name), m.Name.Trim());
m.Data.Add(AmstradCPC.GetMemberName((CPCMachineMetaData c) => c.Description), m.Description.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()); m.Data.Add(AmstradCPC.GetMemberName((CPCMachineMetaData c) => c.Media), m.Media.Trim());
return m; return m;
} }
public static string GetMetaString(MachineType type) public static string GetMetaString(MachineType type)
{ {
@ -275,75 +275,75 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
} }
public static string GetMetaStringOld(MachineType type) 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(m.Name);
sb.Append("\n"); sb.Append("\n");
sb.Append("-----------------------------------------------------------------\n"); sb.Append("-----------------------------------------------------------------\n");
// Release // Release
sb.Append("Released:"); sb.Append("Released:");
sb.Append(" "); sb.Append(" ");
sb.Append(m.Released); sb.Append(m.Released);
sb.Append("\n"); sb.Append("\n");
// CPU // CPU
sb.Append("CPU:"); sb.Append("CPU:");
sb.Append(" "); sb.Append(" ");
sb.Append(m.CPU); sb.Append(m.CPU);
sb.Append("\n"); sb.Append("\n");
// Memory // Memory
sb.Append("Memory:"); sb.Append("Memory:");
sb.Append(" "); sb.Append(" ");
sb.Append(m.Memory); sb.Append(m.Memory);
sb.Append("\n"); sb.Append("\n");
// Video // Video
sb.Append("Video:"); sb.Append("Video:");
sb.Append(" "); sb.Append(" ");
sb.Append(m.Video); sb.Append(m.Video);
sb.Append("\n"); sb.Append("\n");
// Audio // Audio
sb.Append("Audio:"); sb.Append("Audio:");
sb.Append(" "); sb.Append(" ");
sb.Append(m.Audio); sb.Append(m.Audio);
sb.Append("\n"); sb.Append("\n");
// Audio // Audio
sb.Append("Media:"); sb.Append("Media:");
sb.Append(" "); sb.Append(" ");
sb.Append(m.Media); sb.Append(m.Media);
sb.Append("\n"); sb.Append("\n");
sb.Append("-----------------------------------------------------------------\n"); sb.Append("-----------------------------------------------------------------\n");
// description // description
sb.Append(m.Description); sb.Append(m.Description);
if (m.OtherMisc != null) if (m.OtherMisc != null)
sb.Append("\n" + m.OtherMisc); sb.Append("\n" + m.OtherMisc);
return sb.ToString(); return sb.ToString();
} }
} }
/// <summary> /// <summary>
/// The size of the Spectrum border /// The size of the Spectrum border
/// </summary> /// </summary>
public enum BorderType public enum BorderType
{ {
/// <summary> /// <summary>
/// Attempts to equalise the border areas /// Attempts to equalise the border areas
/// </summary> /// </summary>
Uniform, Uniform,
/// <summary> /// <summary>
/// Pretty much the signal the gate array is generating (looks shit) /// Pretty much the signal the gate array is generating (looks shit)
/// </summary> /// </summary>
Uncropped, Uncropped,
/// <summary> /// <summary>
/// Top and bottom border removed so that the result is *almost* 16:9 /// Top and bottom border removed so that the result is *almost* 16:9
/// </summary> /// </summary>
Widescreen, Widescreen,
} }
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,52 +2,52 @@
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// Represents a spectrum keyboard /// Represents a spectrum keyboard
/// </summary> /// </summary>
public interface IKeyboard public interface IKeyboard
{ {
/// <summary> /// <summary>
/// The calling spectrumbase class /// The calling spectrumbase class
/// </summary> /// </summary>
CPCBase _machine { get; } CPCBase _machine { get; }
/// <summary> /// <summary>
/// The keyboard matrix for a particular CPC model /// The keyboard matrix for a particular CPC model
/// </summary> /// </summary>
string[] KeyboardMatrix { get; set; } string[] KeyboardMatrix { get; set; }
/// <summary> /// <summary>
/// Other keyboard keys that are not in the matrix /// Other keyboard keys that are not in the matrix
/// (usually keys derived from key combos) /// (usually keys derived from key combos)
/// </summary> /// </summary>
string[] NonMatrixKeys { get; set; } string[] NonMatrixKeys { get; set; }
/// <summary> /// <summary>
/// Represents the spectrum key state /// Represents the spectrum key state
/// </summary> /// </summary>
bool[] KeyStatus { get; set; } bool[] KeyStatus { get; set; }
/// <summary> /// <summary>
/// The currently selected line /// The currently selected line
/// </summary> /// </summary>
int CurrentLine { get; set; } int CurrentLine { get; set; }
/// <summary> /// <summary>
/// Reads the current line status /// Reads the current line status
/// </summary> /// </summary>
byte ReadCurrentLine(); byte ReadCurrentLine();
/// <summary> /// <summary>
/// Sets the CPC key status /// Sets the CPC key status
/// </summary> /// </summary>
void SetKeyStatus(string key, bool isPressed); void SetKeyStatus(string key, bool isPressed);
/// <summary> /// <summary>
/// Gets the status of a CPC key /// Gets the status of a CPC key
/// </summary> /// </summary>
bool GetKeyStatus(string key); 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 namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// Represents a PSG device (in this case an AY-3-891x) /// Represents a PSG device (in this case an AY-3-891x)
/// </summary> /// </summary>
public interface IPSG : ISoundProvider public interface IPSG : ISoundProvider
{ {
/// <summary> /// <summary>
/// Initlization routine /// Initlization routine
/// </summary> /// </summary>
void Init(int sampleRate, int tStatesPerFrame); void Init(int sampleRate, int tStatesPerFrame);
void SetFunction(int data); void SetFunction(int data);
//void ClockCycle(); //void ClockCycle();
/// <summary> /// <summary>
/// Activates a register /// Activates a register
/// </summary> /// </summary>
int SelectedRegister { get; set; } int SelectedRegister { get; set; }
/// <summary>
/// Writes to the PSG
/// </summary>
void PortWrite(int value);
/// <summary> /// <summary>
/// Reads from the PSG /// Writes to the PSG
/// </summary> /// </summary>
int PortRead(); void PortWrite(int value);
/// <summary> /// <summary>
/// Resets the PSG /// Reads from the PSG
/// </summary> /// </summary>
void Reset(); int PortRead();
/// <summary>
/// The volume of the AY chip
/// </summary>
int Volume { get; set; }
/// <summary> /// <summary>
/// Called at the start of a frame /// Resets the PSG
/// </summary> /// </summary>
void StartFrame(); void Reset();
/// <summary> /// <summary>
/// called at the end of a frame /// The volume of the AY chip
/// </summary> /// </summary>
void EndFrame(); int Volume { get; set; }
/// <summary> /// <summary>
/// Updates the sound based on number of frame cycles /// Called at the start of a frame
/// </summary> /// </summary>
void UpdateSound(int frameCycle); void StartFrame();
/// <summary> /// <summary>
/// IStatable serialization /// called at the end of a frame
/// </summary> /// </summary>
void SyncState(Serializer ser); 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 namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// Represents a device that utilizes port IN & OUT /// Represents a device that utilizes port IN & OUT
/// </summary> /// </summary>
public interface IPortIODevice public interface IPortIODevice
{ {
/// <summary> /// <summary>
/// Device responds to an IN instruction /// Device responds to an IN instruction
/// </summary> /// </summary>
bool ReadPort(ushort port, ref int result); bool ReadPort(ushort port, ref int result);
/// <summary> /// <summary>
/// Device responds to an OUT instruction /// Device responds to an OUT instruction
/// </summary> /// </summary>
bool WritePort(ushort port, int result); bool WritePort(ushort port, int result);
} }
} }

View File

@ -1,180 +1,180 @@
 
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// Used for the sector CHRN structure /// Used for the sector CHRN structure
/// </summary> /// </summary>
public class CHRN public class CHRN
{ {
/// <summary> /// <summary>
/// Track /// Track
/// </summary> /// </summary>
public byte C { get; set; } public byte C { get; set; }
/// <summary> /// <summary>
/// Side /// Side
/// </summary> /// </summary>
public byte H { get; set; } public byte H { get; set; }
/// <summary> /// <summary>
/// Sector ID /// Sector ID
/// </summary> /// </summary>
public byte R { get; set; } public byte R { get; set; }
/// <summary> /// <summary>
/// Sector Size /// Sector Size
/// </summary> /// </summary>
public byte N { get; set; } public byte N { get; set; }
/// <summary> /// <summary>
/// Status register 1 /// Status register 1
/// </summary> /// </summary>
private byte _flag1; private byte _flag1;
public byte Flag1 public byte Flag1
{ {
get { return _flag1; } get { return _flag1; }
set { _flag1 = value; } set { _flag1 = value; }
} }
/// <summary> /// <summary>
/// Status register 2 /// Status register 2
/// </summary> /// </summary>
private byte _flag2; private byte _flag2;
public byte Flag2 public byte Flag2
{ {
get { return _flag2; } get { return _flag2; }
set { _flag2 = value; } set { _flag2 = value; }
} }
/// <summary> /// <summary>
/// Used to store the last transmitted/received data bytes /// Used to store the last transmitted/received data bytes
/// </summary> /// </summary>
public byte[] DataBytes { get; set; } public byte[] DataBytes { get; set; }
/// <summary> /// <summary>
/// ID for the read/write data command /// ID for the read/write data command
/// </summary> /// </summary>
public int DataID { get; set; } public int DataID { get; set; }
#region Helper Methods #region Helper Methods
/// <summary> /// <summary>
/// Missing Address Mark (Sector_ID or DAM not found) /// Missing Address Mark (Sector_ID or DAM not found)
/// </summary> /// </summary>
public bool ST1MA public bool ST1MA
{ {
get { return NECUPD765.GetBit(0, _flag1); } get { return NECUPD765.GetBit(0, _flag1); }
set set
{ {
if (value) { NECUPD765.SetBit(0, ref _flag1); } if (value) { NECUPD765.SetBit(0, ref _flag1); }
else { NECUPD765.UnSetBit(0, ref _flag1); } else { NECUPD765.UnSetBit(0, ref _flag1); }
} }
} }
/// <summary> /// <summary>
/// No Data (Sector_ID not found, CRC fail in ID_field) /// No Data (Sector_ID not found, CRC fail in ID_field)
/// </summary> /// </summary>
public bool ST1ND public bool ST1ND
{ {
get { return NECUPD765.GetBit(2, _flag1); } get { return NECUPD765.GetBit(2, _flag1); }
set set
{ {
if (value) { NECUPD765.SetBit(2, ref _flag1); } if (value) { NECUPD765.SetBit(2, ref _flag1); }
else { NECUPD765.UnSetBit(2, ref _flag1); } else { NECUPD765.UnSetBit(2, ref _flag1); }
} }
} }
/// <summary> /// <summary>
/// Data Error (CRC-fail in ID- or Data-Field) /// Data Error (CRC-fail in ID- or Data-Field)
/// </summary> /// </summary>
public bool ST1DE public bool ST1DE
{ {
get { return NECUPD765.GetBit(5, _flag1); } get { return NECUPD765.GetBit(5, _flag1); }
set set
{ {
if (value) { NECUPD765.SetBit(5, ref _flag1); } if (value) { NECUPD765.SetBit(5, ref _flag1); }
else { NECUPD765.UnSetBit(5, ref _flag1); } else { NECUPD765.UnSetBit(5, ref _flag1); }
} }
} }
/// <summary> /// <summary>
/// End of Track (set past most read/write commands) (see IC) /// End of Track (set past most read/write commands) (see IC)
/// </summary> /// </summary>
public bool ST1EN public bool ST1EN
{ {
get { return NECUPD765.GetBit(7, _flag1); } get { return NECUPD765.GetBit(7, _flag1); }
set set
{ {
if (value) { NECUPD765.SetBit(7, ref _flag1); } if (value) { NECUPD765.SetBit(7, ref _flag1); }
else { NECUPD765.UnSetBit(7, ref _flag1); } else { NECUPD765.UnSetBit(7, ref _flag1); }
} }
} }
/// <summary> /// <summary>
/// Missing Address Mark in Data Field (DAM not found) /// Missing Address Mark in Data Field (DAM not found)
/// </summary> /// </summary>
public bool ST2MD public bool ST2MD
{ {
get { return NECUPD765.GetBit(0, _flag2); } get { return NECUPD765.GetBit(0, _flag2); }
set set
{ {
if (value) { NECUPD765.SetBit(0, ref _flag2); } if (value) { NECUPD765.SetBit(0, ref _flag2); }
else { NECUPD765.UnSetBit(0, ref _flag2); } else { NECUPD765.UnSetBit(0, ref _flag2); }
} }
} }
/// <summary> /// <summary>
/// Bad Cylinder (read/programmed track-ID different and read-ID = FF) /// Bad Cylinder (read/programmed track-ID different and read-ID = FF)
/// </summary> /// </summary>
public bool ST2BC public bool ST2BC
{ {
get { return NECUPD765.GetBit(1, _flag2); } get { return NECUPD765.GetBit(1, _flag2); }
set set
{ {
if (value) { NECUPD765.SetBit(1, ref _flag2); } if (value) { NECUPD765.SetBit(1, ref _flag2); }
else { NECUPD765.UnSetBit(1, ref _flag2); } else { NECUPD765.UnSetBit(1, ref _flag2); }
} }
} }
/// <summary> /// <summary>
/// Wrong Cylinder (read/programmed track-ID different) (see b1) /// Wrong Cylinder (read/programmed track-ID different) (see b1)
/// </summary> /// </summary>
public bool ST2WC public bool ST2WC
{ {
get { return NECUPD765.GetBit(4, _flag2); } get { return NECUPD765.GetBit(4, _flag2); }
set set
{ {
if (value) { NECUPD765.SetBit(4, ref _flag2); } if (value) { NECUPD765.SetBit(4, ref _flag2); }
else { NECUPD765.UnSetBit(4, ref _flag2); } else { NECUPD765.UnSetBit(4, ref _flag2); }
} }
} }
/// <summary> /// <summary>
/// Data Error in Data Field (CRC-fail in data-field) /// Data Error in Data Field (CRC-fail in data-field)
/// </summary> /// </summary>
public bool ST2DD public bool ST2DD
{ {
get { return NECUPD765.GetBit(5, _flag2); } get { return NECUPD765.GetBit(5, _flag2); }
set set
{ {
if (value) { NECUPD765.SetBit(5, ref _flag2); } if (value) { NECUPD765.SetBit(5, ref _flag2); }
else { NECUPD765.UnSetBit(5, ref _flag2); } else { NECUPD765.UnSetBit(5, ref _flag2); }
} }
} }
/// <summary> /// <summary>
/// Control Mark (read/scan command found sector with deleted DAM) /// Control Mark (read/scan command found sector with deleted DAM)
/// </summary> /// </summary>
public bool ST2CM public bool ST2CM
{ {
get { return NECUPD765.GetBit(6, _flag2); } get { return NECUPD765.GetBit(6, _flag2); }
set set
{ {
if (value) { NECUPD765.SetBit(6, ref _flag2); } if (value) { NECUPD765.SetBit(6, ref _flag2); }
else { NECUPD765.UnSetBit(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 namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// Floppy drive related stuff /// Floppy drive related stuff
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 : IFDDHost public partial class NECUPD765 : IFDDHost
{ {
#region Drive State #region Drive State
/// <summary> /// <summary>
/// FDD Flag - motor on/off /// FDD Flag - motor on/off
/// </summary> /// </summary>
public bool FDD_FLAG_MOTOR; public bool FDD_FLAG_MOTOR;
/// <summary> /// <summary>
/// The index of the currently active disk drive /// The index of the currently active disk drive
/// </summary> /// </summary>
public int DiskDriveIndex public int DiskDriveIndex
{ {
get { return _diskDriveIndex; } get { return _diskDriveIndex; }
set set
{ {
// when index is changed update the ActiveDrive // when index is changed update the ActiveDrive
_diskDriveIndex = value; _diskDriveIndex = value;
ActiveDrive = DriveStates[_diskDriveIndex]; ActiveDrive = DriveStates[_diskDriveIndex];
} }
} }
private int _diskDriveIndex = 0; private int _diskDriveIndex = 0;
/// <summary> /// <summary>
/// The currently active drive /// The currently active drive
/// </summary> /// </summary>
private DriveState ActiveDrive; private DriveState ActiveDrive;
/// <summary> /// <summary>
/// Array that holds state information for each possible drive /// Array that holds state information for each possible drive
/// </summary> /// </summary>
private DriveState[] DriveStates = new DriveState[4]; private DriveState[] DriveStates = new DriveState[4];
#endregion #endregion
#region FDD Methods #region FDD Methods
/// <summary> /// <summary>
/// Initialization / reset of the floppy drive subsystem /// Initialization / reset of the floppy drive subsystem
/// </summary> /// </summary>
private void FDD_Init() private void FDD_Init()
{ {
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
DriveState ds = new DriveState(i, this); DriveState ds = new DriveState(i, this);
DriveStates[i] = ds; DriveStates[i] = ds;
} }
} }
/// <summary> /// <summary>
/// Searches for the requested sector /// Searches for the requested sector
/// </summary> /// </summary>
private FloppyDisk.Sector GetSector() private FloppyDisk.Sector GetSector()
{ {
FloppyDisk.Sector sector = null; FloppyDisk.Sector sector = null;
// get the current track // get the current track
var trk = ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex]; var trk = ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex];
// get the current sector index // get the current sector index
int index = ActiveDrive.SectorIndex; int index = ActiveDrive.SectorIndex;
// make sure this index exists // make sure this index exists
if (index > trk.Sectors.Length) if (index > trk.Sectors.Length)
{ {
index = 0; index = 0;
} }
// index hole count // index hole count
int iHole = 0; int iHole = 0;
// loop through the sectors in a track // loop through the sectors in a track
// the loop ends with either the sector being found // the loop ends with either the sector being found
// or the index hole being passed twice // or the index hole being passed twice
while (iHole <= 2) while (iHole <= 2)
{ {
// does the requested sector match the current sector // does the requested sector match the current sector
if (trk.Sectors[index].SectorIDInfo.C == ActiveCommandParams.Cylinder && if (trk.Sectors[index].SectorIDInfo.C == ActiveCommandParams.Cylinder &&
trk.Sectors[index].SectorIDInfo.H == ActiveCommandParams.Head && trk.Sectors[index].SectorIDInfo.H == ActiveCommandParams.Head &&
trk.Sectors[index].SectorIDInfo.R == ActiveCommandParams.Sector && trk.Sectors[index].SectorIDInfo.R == ActiveCommandParams.Sector &&
trk.Sectors[index].SectorIDInfo.N == ActiveCommandParams.SectorSize) trk.Sectors[index].SectorIDInfo.N == ActiveCommandParams.SectorSize)
{ {
// sector has been found // sector has been found
sector = trk.Sectors[index]; sector = trk.Sectors[index];
UnSetBit(SR2_BC, ref Status2); UnSetBit(SR2_BC, ref Status2);
UnSetBit(SR2_WC, ref Status2); UnSetBit(SR2_WC, ref Status2);
break; break;
} }
// check for bad cylinder // check for bad cylinder
if (trk.Sectors[index].SectorIDInfo.C == 255) if (trk.Sectors[index].SectorIDInfo.C == 255)
{ {
SetBit(SR2_BC, ref Status2); SetBit(SR2_BC, ref Status2);
} }
// check for no cylinder // check for no cylinder
else if (trk.Sectors[index].SectorIDInfo.C != ActiveCommandParams.Cylinder) else if (trk.Sectors[index].SectorIDInfo.C != ActiveCommandParams.Cylinder)
{ {
SetBit(SR2_WC, ref Status2); SetBit(SR2_WC, ref Status2);
} }
// incrememnt sector index // incrememnt sector index
index++; index++;
// have we reached the index hole? // have we reached the index hole?
if (trk.Sectors.Length <= index) if (trk.Sectors.Length <= index)
{ {
// wrap around // wrap around
index = 0; index = 0;
iHole++; 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? // bad cylinder detected?
if (Status2.Bit(SR2_BC)) if (Status2.Bit(SR2_BC))
{ {
// remove WC // remove WC
UnSetBit(SR2_WC, ref Status2); UnSetBit(SR2_WC, ref Status2);
} }
// update sectorindex on drive // update sectorindex on drive
ActiveDrive.SectorIndex = index; 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> /// <summary>
/// Parses a new disk image and loads it into this floppy drive /// Parses a new disk image and loads it into this floppy drive
/// </summary> /// </summary>
public void FDD_LoadDisk(byte[] diskData) public void FDD_LoadDisk(byte[] diskData)
{ {
// we are only going to load into the first drive // we are only going to load into the first drive
DriveStates[0].FDD_LoadDisk(diskData); DriveStates[0].FDD_LoadDisk(diskData);
} }
/// <summary> /// <summary>
/// Ejects the current disk /// Ejects the current disk
/// </summary> /// </summary>
public void FDD_EjectDisk() public void FDD_EjectDisk()
{ {
DriveStates[0].FDD_EjectDisk(); DriveStates[0].FDD_EjectDisk();
} }
/// <summary> /// <summary>
/// Signs whether the current active drive has a disk inserted /// Signs whether the current active drive has a disk inserted
/// </summary> /// </summary>
public bool FDD_IsDiskLoaded public bool FDD_IsDiskLoaded
{ {
get { return DriveStates[DiskDriveIndex].FDD_IsDiskLoaded; } get { return DriveStates[DiskDriveIndex].FDD_IsDiskLoaded; }
} }
/// <summary> /// <summary>
/// Returns the disk object from drive 0 /// Returns the disk object from drive 0
/// </summary> /// </summary>
public FloppyDisk DiskPointer public FloppyDisk DiskPointer
{ {
get { return DriveStates[0].Disk; } get { return DriveStates[0].Disk; }
} }
public FloppyDisk Disk { get; set; }
#endregion public FloppyDisk Disk { get; set; }
#region Drive Status Class #endregion
/// <summary> #region Drive Status Class
/// Holds specfic state information about a drive
/// </summary>
private class DriveState : IFDDHost
{
#region State
/// <summary> /// <summary>
/// The drive ID from an FDC perspective /// Holds specfic state information about a drive
/// </summary> /// </summary>
public int ID; private class DriveState : IFDDHost
{
#region State
/// <summary> /// <summary>
/// Signs whether this drive ready /// The drive ID from an FDC perspective
/// TRUE if both drive exists and has a disk inserted /// </summary>
/// </summary> public int ID;
public bool FLAG_READY
{
get
{
if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR)
return false;
else
return true;
}
}
/// <summary> /// <summary>
/// Disk is write protected (TRUE BY DEFAULT) /// Signs whether this drive ready
/// </summary> /// TRUE if both drive exists and has a disk inserted
public bool FLAG_WRITEPROTECT = false; /// </summary>
public bool FLAG_READY
{
get
{
if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR)
return false;
else
return true;
}
}
/// <summary> /// <summary>
/// Storage for seek steps /// Disk is write protected (TRUE BY DEFAULT)
/// One step for each indexpulse (track index) until seeked track /// </summary>
/// </summary> public bool FLAG_WRITEPROTECT = false;
public int SeekCounter;
/// <summary> /// <summary>
/// Seek status /// Storage for seek steps
/// </summary> /// One step for each indexpulse (track index) until seeked track
public int SeekStatus; /// </summary>
public int SeekCounter;
/// <summary> /// <summary>
/// Age counter /// Seek status
/// </summary> /// </summary>
public int SeekAge; public int SeekStatus;
/// <summary> /// <summary>
/// The current side /// Age counter
/// </summary> /// </summary>
public byte CurrentSide; public int SeekAge;
/// <summary> /// <summary>
/// The current track index in the DiskTracks array /// The current side
/// </summary> /// </summary>
public byte TrackIndex; public byte CurrentSide;
/// <summary> /// <summary>
/// The track ID of the current cylinder /// The current track index in the DiskTracks array
/// </summary> /// </summary>
public byte CurrentTrackID public byte TrackIndex;
{
get
{
// default invalid track
int id = 0xff;
if (Disk == null) /// <summary>
return (byte)id; /// The track ID of the current cylinder
/// </summary>
public byte CurrentTrackID
{
get
{
// default invalid track
int id = 0xff;
if (Disk.DiskTracks.Count() == 0) if (Disk == null)
return (byte)id; return (byte)id;
if (TrackIndex >= Disk.GetTrackCount()) if (Disk.DiskTracks.Count() == 0)
TrackIndex = 0; return (byte)id;
else if (TrackIndex < 0)
TrackIndex = 0;
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; id = track.TrackNumber;
}
set return (byte)id;
{ }
for (int i = 0; i < Disk.GetTrackCount(); i++) set
{ {
if (Disk.DiskTracks[i].TrackNumber == value) for (int i = 0; i < Disk.GetTrackCount(); i++)
{ {
TrackIndex = (byte)i; if (Disk.DiskTracks[i].TrackNumber == value)
break; {
} TrackIndex = (byte)i;
} break;
} }
} }
}
}
/// <summary> /// <summary>
/// The new track that the drive is seeking to /// The new track that the drive is seeking to
/// (used in seek operations) /// (used in seek operations)
/// </summary> /// </summary>
public int SeekingTrack; public int SeekingTrack;
/// <summary> /// <summary>
/// The current sector index in the Sectors array /// The current sector index in the Sectors array
/// </summary> /// </summary>
public int SectorIndex; public int SectorIndex;
/// <summary> /// <summary>
/// The currently loaded floppy disk /// The currently loaded floppy disk
/// </summary> /// </summary>
public FloppyDisk Disk { get; set; } public FloppyDisk Disk { get; set; }
/// <summary> /// <summary>
/// The parent controller /// The parent controller
/// </summary> /// </summary>
private NECUPD765 FDC; private NECUPD765 FDC;
#endregion #endregion
#region Lookups #region Lookups
/// <summary> /// <summary>
/// TRUE if we are on track 0 /// TRUE if we are on track 0
/// </summary> /// </summary>
public bool FLAG_TRACK0 public bool FLAG_TRACK0
{ {
get get
{ {
if (TrackIndex == 0) { return true; } if (TrackIndex == 0) { return true; }
else { return false; } else { return false; }
} }
} }
#endregion #endregion
#region Public Methods #region Public Methods
/* /*
/// <summary> /// <summary>
/// Moves the head across the disk cylinders /// Moves the head across the disk cylinders
/// </summary> /// </summary>
@ -369,7 +369,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
} }
*/ */
/* /*
/// <summary> /// <summary>
/// Finds a supplied sector /// Finds a supplied sector
@ -524,7 +524,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
} }
*/ */
/* /*
/// <summary> /// <summary>
/// The drive performs a seek operation if necessary /// The drive performs a seek operation if necessary
@ -751,131 +751,131 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
SetBit(SR0_EC, ref IntStatus); SetBit(SR0_EC, ref IntStatus);
} }
*/ */
/* /*
// UnitSelect // UnitSelect
SetUnitSelect(ID, ref IntStatus); SetUnitSelect(ID, ref IntStatus);
// move to none state // move to none state
//CurrentState = DriveMainState.None; //CurrentState = DriveMainState.None;
//SeekState = SeekSubState.SeekCompleted; //SeekState = SeekSubState.SeekCompleted;
// set the seek interrupt flag for this drive // set the seek interrupt flag for this drive
// this will be cleared at the next successful senseint // this will be cleared at the next successful senseint
FLAG_SEEK_INTERRUPT = true; FLAG_SEEK_INTERRUPT = true;
//CurrentState = DriveMainState.None; //CurrentState = DriveMainState.None;
} }
*/ */
#endregion #endregion
#region Construction #region Construction
public DriveState(int driveID, NECUPD765 fdc) public DriveState(int driveID, NECUPD765 fdc)
{ {
ID = driveID; ID = driveID;
FDC = fdc; FDC = fdc;
} }
#endregion #endregion
#region IFDDHost #region IFDDHost
/// <summary> /// <summary>
/// Parses a new disk image and loads it into this floppy drive /// Parses a new disk image and loads it into this floppy drive
/// </summary> /// </summary>
public void FDD_LoadDisk(byte[] diskData) public void FDD_LoadDisk(byte[] diskData)
{ {
// try dsk first // try dsk first
FloppyDisk fdd = null; FloppyDisk fdd = null;
bool found = false; bool found = false;
foreach (DiskType type in Enum.GetValues(typeof(DiskType))) foreach (DiskType type in Enum.GetValues(typeof(DiskType)))
{ {
switch (type) switch (type)
{ {
case DiskType.CPCExtended: case DiskType.CPCExtended:
fdd = new CPCExtendedFloppyDisk(); fdd = new CPCExtendedFloppyDisk();
found = fdd.ParseDisk(diskData); found = fdd.ParseDisk(diskData);
break; break;
case DiskType.CPC: case DiskType.CPC:
fdd = new CPCFloppyDisk(); fdd = new CPCFloppyDisk();
found = fdd.ParseDisk(diskData); found = fdd.ParseDisk(diskData);
break; break;
} }
if (found) if (found)
{ {
Disk = fdd; Disk = fdd;
break; break;
} }
} }
if (!found) if (!found)
{ {
throw new Exception(this.GetType().ToString() + throw new Exception(this.GetType().ToString() +
"\n\nDisk image file could not be parsed. Potentially an unknown format."); "\n\nDisk image file could not be parsed. Potentially an unknown format.");
} }
} }
/// <summary> /// <summary>
/// Ejects the current disk /// Ejects the current disk
/// </summary> /// </summary>
public void FDD_EjectDisk() public void FDD_EjectDisk()
{ {
Disk = null; Disk = null;
//FLAG_READY = false; //FLAG_READY = false;
} }
/// <summary> /// <summary>
/// Signs whether the current active drive has a disk inserted /// Signs whether the current active drive has a disk inserted
/// </summary> /// </summary>
public bool FDD_IsDiskLoaded public bool FDD_IsDiskLoaded
{ {
get get
{ {
if (Disk != null) if (Disk != null)
return true; return true;
else else
return false; return false;
} }
} }
#endregion #endregion
#region StateSerialization #region StateSerialization
public void SyncState(Serializer ser) public void SyncState(Serializer ser)
{ {
ser.Sync(nameof(ID), ref ID); ser.Sync(nameof(ID), ref ID);
ser.Sync(nameof(FLAG_WRITEPROTECT), ref FLAG_WRITEPROTECT); ser.Sync(nameof(FLAG_WRITEPROTECT), ref FLAG_WRITEPROTECT);
//ser.Sync(nameof(FLAG_DISKCHANGED), ref FLAG_DISKCHANGED); //ser.Sync(nameof(FLAG_DISKCHANGED), ref FLAG_DISKCHANGED);
//ser.Sync(nameof(FLAG_RECALIBRATING), ref FLAG_RECALIBRATING); //ser.Sync(nameof(FLAG_RECALIBRATING), ref FLAG_RECALIBRATING);
//ser.Sync(nameof(FLAG_SEEK_INTERRUPT), ref FLAG_SEEK_INTERRUPT); //ser.Sync(nameof(FLAG_SEEK_INTERRUPT), ref FLAG_SEEK_INTERRUPT);
//ser.Sync(nameof(IntStatus), ref IntStatus); //ser.Sync(nameof(IntStatus), ref IntStatus);
//ser.Sync(nameof(ST0), ref ST0); //ser.Sync(nameof(ST0), ref ST0);
//ser.Sync(nameof(RecalibrationCounter), ref RecalibrationCounter); //ser.Sync(nameof(RecalibrationCounter), ref RecalibrationCounter);
ser.Sync(nameof(SeekCounter), ref SeekCounter); ser.Sync(nameof(SeekCounter), ref SeekCounter);
ser.Sync(nameof(SeekStatus), ref SeekStatus); ser.Sync(nameof(SeekStatus), ref SeekStatus);
ser.Sync(nameof(SeekAge), ref SeekAge); ser.Sync(nameof(SeekAge), ref SeekAge);
ser.Sync(nameof(CurrentSide), ref CurrentSide); ser.Sync(nameof(CurrentSide), ref CurrentSide);
//ser.Sync(nameof(CurrentTrack), ref CurrentTrack); //ser.Sync(nameof(CurrentTrack), ref CurrentTrack);
ser.Sync(nameof(TrackIndex), ref TrackIndex); ser.Sync(nameof(TrackIndex), ref TrackIndex);
ser.Sync(nameof(SeekingTrack), ref SeekingTrack); ser.Sync(nameof(SeekingTrack), ref SeekingTrack);
//ser.Sync(nameof(CurrentSector), ref CurrentSector); //ser.Sync(nameof(CurrentSector), ref CurrentSector);
ser.Sync(nameof(SectorIndex), ref SectorIndex); ser.Sync(nameof(SectorIndex), ref SectorIndex);
//ser.Sync(nameof(RAngles), ref RAngles); //ser.Sync(nameof(RAngles), ref RAngles);
//ser.Sync(nameof(DataPointer), ref DataPointer); //ser.Sync(nameof(DataPointer), ref DataPointer);
//ser.SyncEnum(nameof(CurrentState), ref CurrentState); //ser.SyncEnum(nameof(CurrentState), ref CurrentState);
//ser.SyncEnum(nameof(SeekState), ref SeekState); //ser.SyncEnum(nameof(SeekState), ref SeekState);
//ser.SyncEnum(nameof(SeekIntState), ref SeekIntState); //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 namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// IPortIODevice /// IPortIODevice
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 : IPortIODevice public partial class NECUPD765 : IPortIODevice
{ {
#region Dev Logging #region Dev Logging
public string outputfile = @"D:\Dropbox\Dropbox\_Programming\TASVideos\BizHawk\output\zxhawkio-" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv"; 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 string outputString = "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN\r\n";
public bool writeDebug = false; public bool writeDebug = false;
public List<string> dLog = new List<string> public List<string> dLog = new List<string>
{ {
"STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN" "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN"
}; };
/* /*
* Status read * Status read
* Data write * Data write
* Data read * Data read
@ -40,154 +40,154 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
* MK flag * MK flag
* SK flag * SK flag
* */ * */
private string[] workingArr = new string[3]; private string[] workingArr = new string[3];
private void BuildCSVLine() private void BuildCSVLine()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
sb.Append(workingArr[i]); sb.Append(workingArr[i]);
sb.Append(","); sb.Append(",");
workingArr[i] = ""; workingArr[i] = "";
} }
sb.Append(ActiveCommand.CommandCode).Append(","); sb.Append(ActiveCommand.CommandCode).Append(",");
sb.Append(CMD_FLAG_MT).Append(","); sb.Append(CMD_FLAG_MT).Append(",");
sb.Append(CMD_FLAG_MF).Append(","); sb.Append(CMD_FLAG_MF).Append(",");
sb.Append(CMD_FLAG_SK).Append(","); sb.Append(CMD_FLAG_SK).Append(",");
sb.Append(CommCounter).Append(","); sb.Append(CommCounter).Append(",");
sb.Append(ResCounter).Append(","); sb.Append(ResCounter).Append(",");
sb.Append(ExecCounter).Append(","); sb.Append(ExecCounter).Append(",");
sb.Append(ExecLength); sb.Append(ExecLength);
//sb.Append("\r\n"); //sb.Append("\r\n");
//outputString += sb.ToString(); //outputString += sb.ToString();
dLog.Add(sb.ToString()); dLog.Add(sb.ToString());
} }
#endregion #endregion
public void ReadStatus(ref int data) public void ReadStatus(ref int data)
{ {
// read main status register // read main status register
// this can happen at any time // this can happen at any time
data = ReadMainStatus(); data = ReadMainStatus();
if (writeDebug) if (writeDebug)
{ {
//outputString += data + ",,," + ActiveCommand.CommandCode + "\r\n"; //outputString += data + ",,," + ActiveCommand.CommandCode + "\r\n";
workingArr[0] = data.ToString(); workingArr[0] = data.ToString();
BuildCSVLine(); BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString); //System.IO.File.WriteAllText(outputfile, outputString);
} }
} }
public void ReadData(ref int data) public void ReadData(ref int data)
{ {
// Z80 is trying to read from the data register // Z80 is trying to read from the data register
data = ReadDataRegister(); data = ReadDataRegister();
if (writeDebug) if (writeDebug)
{ {
workingArr[2] = data.ToString(); workingArr[2] = data.ToString();
//outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n"; //outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n";
BuildCSVLine(); BuildCSVLine();
} }
} }
public void WriteData(int data) public void WriteData(int data)
{ {
// Z80 is attempting to write to the data register // Z80 is attempting to write to the data register
WriteDataRegister((byte)data); WriteDataRegister((byte)data);
if (writeDebug) if (writeDebug)
{ {
//outputString += "," + data + ",," + ActiveCommand.CommandCode + "\r\n"; //outputString += "," + data + ",," + ActiveCommand.CommandCode + "\r\n";
workingArr[1] = data.ToString(); workingArr[1] = data.ToString();
BuildCSVLine(); BuildCSVLine();
//System.IO.File.WriteAllText(outputfile, outputString); //System.IO.File.WriteAllText(outputfile, outputString);
} }
} }
public void Motor(int data) public void Motor(int data)
{ {
// set disk motor on/off // set disk motor on/off
if (data > 0) if (data > 0)
FDD_FLAG_MOTOR = true; FDD_FLAG_MOTOR = true;
else else
FDD_FLAG_MOTOR = false; FDD_FLAG_MOTOR = false;
} }
/// <summary> /// <summary>
/// Device responds to an IN instruction /// Device responds to an IN instruction
/// </summary> /// </summary>
public bool ReadPort(ushort port, ref int data) public bool ReadPort(ushort port, ref int data)
{ {
BitArray bits = new BitArray(new byte[] { (byte)data }); BitArray bits = new BitArray(new byte[] { (byte)data });
if (port == 0x3ffd) if (port == 0x3ffd)
{ {
// Z80 is trying to read from the data register // Z80 is trying to read from the data register
data = ReadDataRegister(); data = ReadDataRegister();
if (writeDebug) if (writeDebug)
{ {
workingArr[2] = data.ToString(); workingArr[2] = data.ToString();
//outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n"; //outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n";
BuildCSVLine(); BuildCSVLine();
} }
return true;
}
if (port == 0x2ffd) return true;
{ }
// 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 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> return true;
/// 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) return false;
{ }
// 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) /// <summary>
{ /// Device responds to an OUT instruction
// set disk motor on/off /// </summary>
FDD_FLAG_MOTOR = bits[3]; public bool WritePort(ushort port, int data)
return true; {
} BitArray bits = new BitArray(new byte[] { (byte)data });
return false;
} 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 namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// Timimng /// Timimng
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 public partial class NECUPD765
{ {
/// <summary> /// <summary>
/// The current Z80 cycle /// The current Z80 cycle
/// </summary> /// </summary>
private long CurrentCPUCycle private long CurrentCPUCycle
{ {
get get
{ {
if (_machine == null) if (_machine == null)
return 0; return 0;
else else
return _machine.CPU.TotalExecutedCycles; return _machine.CPU.TotalExecutedCycles;
} }
} }
/// <summary> /// <summary>
/// The last CPU cycle when the FDC accepted an IO read/write /// The last CPU cycle when the FDC accepted an IO read/write
/// </summary> /// </summary>
private long LastCPUCycle; private long LastCPUCycle;
/// <summary> /// <summary>
/// The current delay figure (in Z80 t-states) /// The current delay figure (in Z80 t-states)
/// This implementation only introduces delay upon main status register reads /// This implementation only introduces delay upon main status register reads
/// All timing calculations should be done during the other read/write operations /// All timing calculations should be done during the other read/write operations
/// </summary> /// </summary>
private long StatusDelay; private long StatusDelay;
/// <summary> /// <summary>
/// Defines the numbers of Z80 cycles per MS /// Defines the numbers of Z80 cycles per MS
/// </summary> /// </summary>
private long CPUCyclesPerMs; private long CPUCyclesPerMs;
/// <summary> /// <summary>
/// The floppy drive emulated clock speed /// The floppy drive emulated clock speed
/// </summary> /// </summary>
public const double DriveClock = 31250; public const double DriveClock = 31250;
/// <summary> /// <summary>
/// The number of floppy drive cycles per MS /// The number of floppy drive cycles per MS
/// </summary> /// </summary>
public long DriveCyclesPerMs; public long DriveCyclesPerMs;
/// <summary> /// <summary>
/// The number of T-States in one floppy drive clock tick /// The number of T-States in one floppy drive clock tick
/// </summary> /// </summary>
public long StatesPerDriveTick; public long StatesPerDriveTick;
/// <summary> /// <summary>
/// Responsible for measuring when the floppy drive is ready to run a cycle /// Responsible for measuring when the floppy drive is ready to run a cycle
/// </summary> /// </summary>
private long TickCounter; private long TickCounter;
/// <summary> /// <summary>
/// Internal drive cycle counter /// Internal drive cycle counter
/// </summary> /// </summary>
private int DriveCycleCounter = 1; private int DriveCycleCounter = 1;
/// <summary> /// <summary>
/// Initializes the timing routines /// Initializes the timing routines
/// </summary> /// </summary>
private void TimingInit() private void TimingInit()
{ {
// z80 timing // z80 timing
double frameSize = _machine.GateArray.FrameLength; double frameSize = _machine.GateArray.FrameLength;
double rRate = _machine.GateArray.Z80ClockSpeed / frameSize; double rRate = _machine.GateArray.Z80ClockSpeed / frameSize;
long tPerSecond = (long)(frameSize * rRate); long tPerSecond = (long)(frameSize * rRate);
CPUCyclesPerMs = tPerSecond / 1000; CPUCyclesPerMs = tPerSecond / 1000;
// drive timing // drive timing
double dRate = DriveClock / frameSize; double dRate = DriveClock / frameSize;
long dPerSecond = (long)(frameSize * dRate); long dPerSecond = (long)(frameSize * dRate);
DriveCyclesPerMs = dPerSecond / 1000; DriveCyclesPerMs = dPerSecond / 1000;
long TStatesPerDriveCycle = (long)((double)_machine.GateArray.Z80ClockSpeed / DriveClock); long TStatesPerDriveCycle = (long)((double)_machine.GateArray.Z80ClockSpeed / DriveClock);
StatesPerDriveTick = TStatesPerDriveCycle; StatesPerDriveTick = TStatesPerDriveCycle;
} }
/// <summary> /// <summary>
/// Called by reads to the main status register /// Called by reads to the main status register
/// Returns true if there is no delay /// Returns true if there is no delay
/// Returns false if read is to be deferred /// Returns false if read is to be deferred
/// </summary> /// </summary>
private bool CheckTiming() private bool CheckTiming()
{ {
// get delta // get delta
long delta = CurrentCPUCycle - LastCPUCycle; long delta = CurrentCPUCycle - LastCPUCycle;
if (StatusDelay >= delta) if (StatusDelay >= delta)
{ {
// there is still delay remaining // there is still delay remaining
StatusDelay -= delta; StatusDelay -= delta;
LastCPUCycle = CurrentCPUCycle; LastCPUCycle = CurrentCPUCycle;
return false; return false;
} }
else else
{ {
// no delay remaining // no delay remaining
StatusDelay = 0; StatusDelay = 0;
LastCPUCycle = CurrentCPUCycle; LastCPUCycle = CurrentCPUCycle;
return true; return true;
} }
} }
} }
} }

View File

@ -3,243 +3,243 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// The NEC floppy disk controller (and floppy drive) found in the +3 /// The NEC floppy disk controller (and floppy drive) found in the +3
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 public partial class NECUPD765
{ {
#region Devices #region Devices
/// <summary> /// <summary>
/// The emulated spectrum machine /// The emulated spectrum machine
/// </summary> /// </summary>
private CPCBase _machine; private CPCBase _machine;
#endregion #endregion
#region Construction & Initialization #region Construction & Initialization
/// <summary> /// <summary>
/// Main constructor /// Main constructor
/// </summary> /// </summary>
public NECUPD765() public NECUPD765()
{ {
InitCommandList(); InitCommandList();
} }
/// <summary> /// <summary>
/// Initialization routine /// Initialization routine
/// </summary> /// </summary>
public void Init(CPCBase machine) public void Init(CPCBase machine)
{ {
_machine = machine; _machine = machine;
FDD_Init(); FDD_Init();
TimingInit(); TimingInit();
Reset(); Reset();
} }
/// <summary>
/// Resets the FDC
/// </summary>
public void Reset()
{
// setup main status
StatusMain = 0;
Status0 = 0; /// <summary>
Status1 = 0; /// Resets the FDC
Status2 = 0; /// </summary>
Status3 = 0; 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; SetPhase_Idle();
//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) //FDC_FLAG_RQM = true;
{ //ActiveDirection = CommandDirection.IN;
//d.SeekingTrack = d.CurrentTrack; SRT = 6;
////d.SeekCounter = 0; HUT = 16;
//d.FLAG_SEEK_INTERRUPT = false; HLT = 2;
//d.IntStatus = 0; HLT_Counter = 0;
//d.SeekState = SeekSubState.Idle; HUT_Counter = 0;
//d.SeekIntState = SeekIntStatus.Normal; 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() /// <summary>
{ /// Setup the command structure
CommandList = new List<Command> /// Each command represents one of the internal UPD765 commands
{ /// </summary>
private void InitCommandList()
{
CommandList = new List<Command>
{
// read data // read data
new Command { CommandDelegate = UPD_ReadData, CommandCode = 0x06, MT = true, MF = true, SK = true, IsRead = true, 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 // read id
new Command { CommandDelegate = UPD_ReadID, CommandCode = 0x0a, MF = true, IsRead = true, 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 // specify
new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03, new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03,
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
// read diagnostic // read diagnostic
new Command { CommandDelegate = UPD_ReadDiagnostic, CommandCode = 0x02, MF = true, SK = true, IsRead = true, 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 // scan equal
new Command { CommandDelegate = UPD_ScanEqual, CommandCode = 0x11, MT = true, MF = true, SK = true, IsRead = true, 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 // scan high or equal
new Command { CommandDelegate = UPD_ScanHighOrEqual, CommandCode = 0x1d, MT = true, MF = true, SK = true, IsRead = true, 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 // scan low or equal
new Command { CommandDelegate = UPD_ScanLowOrEqual, CommandCode = 0x19, MT = true, MF = true, SK = true, IsRead = true, 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 // read deleted data
new Command { CommandDelegate = UPD_ReadDeletedData, CommandCode = 0x0c, MT = true, MF = true, SK = true, IsRead = true, 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 // write data
new Command { CommandDelegate = UPD_WriteData, CommandCode = 0x05, MT = true, MF = true, IsWrite = true, 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 // write id
new Command { CommandDelegate = UPD_WriteID, CommandCode = 0x0d, MF = true, IsWrite = true, 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 // write deleted data
new Command { CommandDelegate = UPD_WriteDeletedData, CommandCode = 0x09, MT = true, MF = true, IsWrite = true, 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 // seek
new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f, new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f,
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
// recalibrate (seek track00) // recalibrate (seek track00)
new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07, new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 }, Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 },
// sense interrupt status // sense interrupt status
new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08, new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 }, Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 },
// sense drive status // sense drive status
new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04, new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 }, Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 },
// version // version
new Command { CommandDelegate = UPD_Version, CommandCode = 0x10, new Command { CommandDelegate = UPD_Version, CommandCode = 0x10,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
// invalid // invalid
new Command { CommandDelegate = UPD_Invalid, CommandCode = 0x00, 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) public void SyncState(Serializer ser)
{ {
ser.BeginSection("NEC-UPD765"); ser.BeginSection("NEC-UPD765");
#region FDD #region FDD
ser.Sync(nameof(FDD_FLAG_MOTOR), ref FDD_FLAG_MOTOR);
for (int i = 0; i < 4; i++) ser.Sync(nameof(FDD_FLAG_MOTOR), ref FDD_FLAG_MOTOR);
{
ser.BeginSection("HITDrive_" + i);
DriveStates[i].SyncState(ser);
ser.EndSection();
}
ser.Sync(nameof(DiskDriveIndex), ref _diskDriveIndex); for (int i = 0; i < 4; i++)
// set active drive {
DiskDriveIndex = _diskDriveIndex; 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); #region Registers
ser.Sync("_Reg0", ref Status0);
ser.Sync("_Reg1", ref Status1);
ser.Sync("_Reg2", ref Status2);
ser.Sync("_Reg3", ref Status3);
#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); #region Controller state
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;
ActiveCommandParams.SyncState(ser); ser.Sync(nameof(DriveLight), ref DriveLight);
ser.SyncEnum(nameof(ActivePhase), ref ActivePhase);
ser.Sync(nameof(IndexPulseCounter), ref IndexPulseCounter); //ser.SyncEnum(nameof(ActiveDirection), ref ActiveDirection);
//ser.SyncEnum(nameof(_activeStatus), ref _activeStatus); ser.SyncEnum(nameof(ActiveInterrupt), ref ActiveInterrupt);
//ser.SyncEnum(nameof(_statusRaised), ref _statusRaised); 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(_cmdIndex), ref _cmdIndex);
ser.Sync(nameof(CMD_FLAG_MF), ref CMD_FLAG_MF); // resync the ActiveCommand
ser.Sync(nameof(CMD_FLAG_SK), ref CMD_FLAG_SK); CMDIndex = _cmdIndex;
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(SectorDelayCounter), ref SectorDelayCounter); ActiveCommandParams.SyncState(ser);
ser.Sync(nameof(SectorID), ref SectorID);
#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(SectorDelayCounter), ref SectorDelayCounter);
ser.Sync(nameof(StatusDelay), ref StatusDelay); ser.Sync(nameof(SectorID), ref SectorID);
ser.Sync(nameof(TickCounter), ref TickCounter);
ser.Sync(nameof(DriveCycleCounter), ref DriveCycleCounter);
#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 namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// Static helper methods /// Static helper methods
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 public partial class NECUPD765
{ {
/// <summary> /// <summary>
/// Returns the specified bit value from supplied byte /// Returns the specified bit value from supplied byte
/// </summary> /// </summary>
public static bool GetBit(int bitNumber, byte dataByte) public static bool GetBit(int bitNumber, byte dataByte)
{ {
if (bitNumber < 0 || bitNumber > 7) if (bitNumber < 0 || bitNumber > 7)
return false; return false;
BitArray bi = new BitArray(new byte[] { dataByte }); BitArray bi = new BitArray(new byte[] { dataByte });
return bi[bitNumber]; return bi[bitNumber];
} }
/// <summary> /// <summary>
/// Sets the specified bit of the supplied byte to 1 /// Sets the specified bit of the supplied byte to 1
/// </summary> /// </summary>
public static void SetBit(int bitNumber, ref byte dataByte) public static void SetBit(int bitNumber, ref byte dataByte)
{ {
if (bitNumber < 0 || bitNumber > 7) if (bitNumber < 0 || bitNumber > 7)
return; return;
int db = (int)dataByte; int db = (int)dataByte;
db |= 1 << bitNumber; db |= 1 << bitNumber;
dataByte = (byte)db; dataByte = (byte)db;
} }
/// <summary> /// <summary>
/// Sets the specified bit of the supplied byte to 0 /// Sets the specified bit of the supplied byte to 0
/// </summary> /// </summary>
public static void UnSetBit(int bitNumber, ref byte dataByte) public static void UnSetBit(int bitNumber, ref byte dataByte)
{ {
if (bitNumber < 0 || bitNumber > 7) if (bitNumber < 0 || bitNumber > 7)
return; return;
int db = (int)dataByte; int db = (int)dataByte;
db &= ~(1 << bitNumber); db &= ~(1 << bitNumber);
dataByte = (byte)db; dataByte = (byte)db;
} }
/// <summary> /// <summary>
/// Returns a drive number (0-3) based on the first two bits of the supplied byte /// Returns a drive number (0-3) based on the first two bits of the supplied byte
/// </summary> /// </summary>
public static int GetUnitSelect(byte dataByte) public static int GetUnitSelect(byte dataByte)
{ {
int driveNumber = dataByte & 0x03; int driveNumber = dataByte & 0x03;
return driveNumber; return driveNumber;
} }
/// <summary> /// <summary>
/// Sets the first two bits of a byte based on the supplied drive number (0-3) /// Sets the first two bits of a byte based on the supplied drive number (0-3)
/// </summary> /// </summary>
public static void SetUnitSelect(int driveNumber, ref byte dataByte) public static void SetUnitSelect(int driveNumber, ref byte dataByte)
{ {
switch (driveNumber) switch (driveNumber)
{ {
case 0: case 0:
UnSetBit(SR0_US0, ref dataByte); UnSetBit(SR0_US0, ref dataByte);
UnSetBit(SR0_US1, ref dataByte); UnSetBit(SR0_US1, ref dataByte);
break; break;
case 1: case 1:
SetBit(SR0_US0, ref dataByte); SetBit(SR0_US0, ref dataByte);
UnSetBit(SR0_US1, ref dataByte); UnSetBit(SR0_US1, ref dataByte);
break; break;
case 2: case 2:
SetBit(SR0_US1, ref dataByte); SetBit(SR0_US1, ref dataByte);
UnSetBit(SR0_US0, ref dataByte); UnSetBit(SR0_US0, ref dataByte);
break; break;
case 3: case 3:
SetBit(SR0_US0, ref dataByte); SetBit(SR0_US0, ref dataByte);
SetBit(SR0_US1, ref dataByte); SetBit(SR0_US1, ref dataByte);
break; 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 /// The CRTC latches the Display Start H & L address at different times
/// (depending on the chip type) /// (depending on the chip type)
/// </summary> /// </summary>
private int StartAddressLatch; private int StartAddressLatch;
#endregion #endregion
@ -541,7 +541,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
case 1: return ReadStatus_Type1(ref data); case 1: return ReadStatus_Type1(ref data);
case 3: case 3:
case 4: return ReadStatus_Type3_4(ref data); 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 // Bits 5 and 4 determine the skew
res = (val & 0x30) >> 4; res = (val & 0x30) >> 4;
if (res > 2) if (res > 2)
return -1; return -1;
break; break;
// UMR6845R // UMR6845R
@ -600,7 +600,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
if (res > 2) if (res > 2)
return -1; return -1;
break; break;
// UMR6845R // UMR6845R
case 1: case 1:
return 0; return 0;
@ -1141,7 +1141,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
#region Clock Cycles #region Clock Cycles
/* persistent switch signals */ /* persistent switch signals */
bool s_VS; bool s_VS;
bool s_HDISP; bool s_HDISP;
bool s_VDISP; bool s_VDISP;
@ -1172,7 +1172,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
/// </summary> /// </summary>
private void ClockCycle_Generic() private void ClockCycle_Generic()
{ {
} }
/// <summary> /// <summary>
@ -1788,7 +1788,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
else else
{ {
_DISPTMG = false; _DISPTMG = false;
} }
/* Cursor Control */ /* Cursor Control */
if (s_HDISP && s_VDISP) if (s_HDISP && s_VDISP)

View File

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

View File

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

View File

@ -4,206 +4,206 @@ using System;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// Logical Beeper class /// Logical Beeper class
/// Used to emulate the sound generated by tape loading /// Used to emulate the sound generated by tape loading
/// This implementation uses BlipBuffer and should *always* output at 44100 with 882 samples per frame /// 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) /// (so that it can be mixed easily further down the line)
/// </summary> /// </summary>
public class Beeper : ISoundProvider, IBeeperDevice public class Beeper : ISoundProvider, IBeeperDevice
{ {
#region Fields and Properties #region Fields and Properties
/// <summary> /// <summary>
/// Sample Rate /// Sample Rate
/// This usually has to be 44100 for ISoundProvider /// This usually has to be 44100 for ISoundProvider
/// </summary> /// </summary>
private int _sampleRate; private int _sampleRate;
public int SampleRate public int SampleRate
{ {
get { return _sampleRate; } get { return _sampleRate; }
set { _sampleRate = value; } set { _sampleRate = value; }
} }
/// <summary> /// <summary>
/// Buzzer volume /// Buzzer volume
/// Accepts an int 0-100 value /// Accepts an int 0-100 value
/// </summary> /// </summary>
private int _volume; private int _volume;
public int Volume public int Volume
{ {
get get
{ {
return VolumeConverterOut(_volume); return VolumeConverterOut(_volume);
} }
set set
{ {
var newVol = VolumeConverterIn(value); var newVol = VolumeConverterIn(value);
if (newVol != _volume) if (newVol != _volume)
blip.Clear(); blip.Clear();
_volume = VolumeConverterIn(value); _volume = VolumeConverterIn(value);
} }
} }
/// <summary> /// <summary>
/// The last used volume (used to modify blipbuffer delta values) /// The last used volume (used to modify blipbuffer delta values)
/// </summary> /// </summary>
private int lastVolume; private int lastVolume;
/// <summary> /// <summary>
/// The number of cpu cycles per frame /// The number of cpu cycles per frame
/// </summary> /// </summary>
private long _tStatesPerFrame; private long _tStatesPerFrame;
/// <summary> /// <summary>
/// The parent emulated machine /// The parent emulated machine
/// </summary> /// </summary>
private CPCBase _machine; private CPCBase _machine;
/// <summary> /// <summary>
/// The last pulse /// The last pulse
/// </summary> /// </summary>
private bool LastPulse; private bool LastPulse;
/// <summary> /// <summary>
/// The last T-State (cpu cycle) that the last pulse was received /// The last T-State (cpu cycle) that the last pulse was received
/// </summary> /// </summary>
private long LastPulseTState; private long LastPulseTState;
/// <summary> /// <summary>
/// Device blipbuffer /// Device blipbuffer
/// </summary> /// </summary>
private readonly BlipBuffer blip = new BlipBuffer(883); private readonly BlipBuffer blip = new BlipBuffer(883);
#endregion #endregion
#region Private Methods #region Private Methods
/// <summary> /// <summary>
/// Takes an int 0-100 and returns the relevant short volume to output /// Takes an int 0-100 and returns the relevant short volume to output
/// </summary> /// </summary>
private int VolumeConverterIn(int vol) private int VolumeConverterIn(int vol)
{ {
int maxLimit = short.MaxValue / 3; int maxLimit = short.MaxValue / 3;
int increment = maxLimit / 100; int increment = maxLimit / 100;
return vol * increment; return vol * increment;
} }
/// <summary> /// <summary>
/// Takes an short volume and returns the relevant int value 0-100 /// Takes an short volume and returns the relevant int value 0-100
/// </summary> /// </summary>
private int VolumeConverterOut(int shortvol) private int VolumeConverterOut(int shortvol)
{ {
int maxLimit = short.MaxValue / 3; int maxLimit = short.MaxValue / 3;
int increment = maxLimit / 100; int increment = maxLimit / 100;
if (shortvol > maxLimit) if (shortvol > maxLimit)
shortvol = maxLimit; shortvol = maxLimit;
return shortvol / increment; return shortvol / increment;
} }
#endregion #endregion
#region Construction & Initialisation #region Construction & Initialisation
public Beeper(CPCBase machine) public Beeper(CPCBase machine)
{ {
_machine = machine; _machine = machine;
} }
/// <summary> /// <summary>
/// Initialises the beeper /// Initialises the beeper
/// </summary> /// </summary>
public void Init(int sampleRate, int tStatesPerFrame) public void Init(int sampleRate, int tStatesPerFrame)
{ {
blip.SetRates((4000000), sampleRate); blip.SetRates((4000000), sampleRate);
_sampleRate = sampleRate; _sampleRate = sampleRate;
_tStatesPerFrame = tStatesPerFrame; _tStatesPerFrame = tStatesPerFrame;
} }
#endregion #endregion
#region IBeeperDevice #region IBeeperDevice
/// <summary> /// <summary>
/// Processes an incoming pulse value and adds it to the blipbuffer /// Processes an incoming pulse value and adds it to the blipbuffer
/// </summary> /// </summary>
public void ProcessPulseValue(bool pulse) public void ProcessPulseValue(bool pulse)
{ {
if (!_machine._renderSound) if (!_machine._renderSound)
return; return;
if (LastPulse == pulse) if (LastPulse == pulse)
{ {
// no change // no change
blip.AddDelta((uint)_machine.CurrentFrameCycle, 0); blip.AddDelta((uint)_machine.CurrentFrameCycle, 0);
} }
else
{
if (pulse)
blip.AddDelta((uint)_machine.CurrentFrameCycle, (short)(_volume));
else
blip.AddDelta((uint)_machine.CurrentFrameCycle, -(short)(_volume));
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) public SyncSoundMode SyncMode => SyncSoundMode.Sync;
{
if (mode != SyncSoundMode.Sync)
throw new InvalidOperationException("Only Sync mode is supported.");
}
public void GetSamplesAsync(short[] samples) public void SetSyncMode(SyncSoundMode mode)
{ {
throw new NotSupportedException("Async is not available"); if (mode != SyncSoundMode.Sync)
} throw new InvalidOperationException("Only Sync mode is supported.");
}
public void DiscardSamples() public void GetSamplesAsync(short[] samples)
{ {
blip.Clear(); throw new NotSupportedException("Async is not available");
} }
public void GetSamplesSync(out short[] samples, out int nsamp) public void DiscardSamples()
{ {
blip.EndFrame((uint)_tStatesPerFrame); blip.Clear();
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];
}
}
#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) #region State Serialization
{
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 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 namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// CPC464 /// CPC464
/// * Memory * /// * Memory *
/// </summary> /// </summary>
public partial class CPC464 : CPCBase public partial class CPC464 : CPCBase
{ {
/// <summary> /// <summary>
/// Simulates reading from the bus /// Simulates reading from the bus
/// ROM paging should be handled here /// ROM paging should be handled here
/// </summary> /// </summary>
public override byte ReadBus(ushort addr) public override byte ReadBus(ushort addr)
{ {
int divisor = addr / 0x4000; int divisor = addr / 0x4000;
byte result = 0xff; byte result = 0xff;
switch (divisor) switch (divisor)
{ {
// 0x000 or LowerROM // 0x000 or LowerROM
case 0: case 0:
if (LowerROMPaged) if (LowerROMPaged)
result = ROMLower[addr % 0x4000]; result = ROMLower[addr % 0x4000];
else else
result = RAM0[addr % 0x4000]; result = RAM0[addr % 0x4000];
break; break;
// 0x4000 // 0x4000
case 1: case 1:
result = RAM1[addr % 0x4000]; result = RAM1[addr % 0x4000];
break; break;
// 0x8000 // 0x8000
case 2: case 2:
result = RAM2[addr % 0x4000]; result = RAM2[addr % 0x4000];
break; break;
// 0xc000 or UpperROM // 0xc000 or UpperROM
case 3: case 3:
if (UpperROMPaged) if (UpperROMPaged)
result = ROM0[addr % 0x4000]; result = ROM0[addr % 0x4000];
else else
result = RAM3[addr % 0x4000]; result = RAM3[addr % 0x4000];
break; break;
default: default:
break; break;
} }
return result; return result;
} }
/// <summary> /// <summary>
/// Simulates writing to the bus /// Simulates writing to the bus
/// Writes to the bus ALWAYS go to RAM, regardless of what upper and lower ROMs are paged in /// Writes to the bus ALWAYS go to RAM, regardless of what upper and lower ROMs are paged in
/// </summary> /// </summary>
public override void WriteBus(ushort addr, byte value) public override void WriteBus(ushort addr, byte value)
{ {
int divisor = addr / 0x4000; int divisor = addr / 0x4000;
switch (divisor) switch (divisor)
{ {
// RAM 0x000 // RAM 0x000
case 0: case 0:
RAM0[addr % 0x4000] = value; RAM0[addr % 0x4000] = value;
break; break;
// RAM 0x4000 // RAM 0x4000
case 1: case 1:
RAM1[addr % 0x4000] = value; RAM1[addr % 0x4000] = value;
break; break;
// RAM 0x8000 // RAM 0x8000
case 2: case 2:
RAM2[addr % 0x4000] = value; RAM2[addr % 0x4000] = value;
break; break;
// RAM 0xc000 // RAM 0xc000
case 3: case 3:
RAM3[addr % 0x4000] = value; RAM3[addr % 0x4000] = value;
break; break;
default: default:
break; break;
} }
} }
/// <summary> /// <summary>
/// Reads a byte of data from a specified memory address /// Reads a byte of data from a specified memory address
/// </summary> /// </summary>
public override byte ReadMemory(ushort addr) public override byte ReadMemory(ushort addr)
{ {
var data = ReadBus(addr); var data = ReadBus(addr);
return data; return data;
} }
/// <summary> /// <summary>
/// Writes a byte of data to a specified memory address /// Writes a byte of data to a specified memory address
/// (with memory contention if appropriate) /// (with memory contention if appropriate)
/// </summary> /// </summary>
public override void WriteMemory(ushort addr, byte value) public override void WriteMemory(ushort addr, byte value)
{ {
WriteBus(addr, value); WriteBus(addr, value);
} }
/// <summary> /// <summary>
/// Sets up the ROM /// Sets up the ROM
/// </summary> /// </summary>
public override void InitROM(RomData[] romData) public override void InitROM(RomData[] romData)
{ {
foreach (var r in romData) foreach (var r in romData)
{ {
if (r.ROMType == RomData.ROMChipType.Lower) if (r.ROMType == RomData.ROMChipType.Lower)
{ {
for (int i = 0; i < 0x4000; i++) for (int i = 0; i < 0x4000; i++)
{ {
ROMLower[i] = r.RomBytes[i]; ROMLower[i] = r.RomBytes[i];
} }
} }
else else
{ {
for (int i = 0; i < 0x4000; i++) for (int i = 0; i < 0x4000; i++)
{ {
switch (r.ROMPosition) switch (r.ROMPosition)
{ {
case 0: case 0:
ROM0[i] = r.RomBytes[i]; ROM0[i] = r.RomBytes[i];
break; break;
case 7: case 7:
ROM7[i] = r.RomBytes[i]; ROM7[i] = r.RomBytes[i];
break; break;
} }
} }
} }
} }
LowerROMPaged = true; LowerROMPaged = true;
UpperROMPaged = true; UpperROMPaged = true;
} }
} }
} }

View File

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

View File

@ -4,43 +4,43 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// CPC464 construction /// CPC464 construction
/// </summary> /// </summary>
public partial class CPC464 : CPCBase public partial class CPC464 : CPCBase
{ {
#region Construction #region Construction
/// <summary> /// <summary>
/// Main constructor /// Main constructor
/// </summary> /// </summary>
public CPC464(AmstradCPC cpc, Z80A cpu, List<byte[]> files, bool autoTape, AmstradCPC.BorderType borderType) public CPC464(AmstradCPC cpc, Z80A cpu, List<byte[]> files, bool autoTape, AmstradCPC.BorderType borderType)
{ {
CPC = cpc; CPC = cpc;
CPU = cpu; CPU = cpu;
FrameLength = 79872; FrameLength = 79872;
CRCT = new CRCT_6845(CRCT_6845.CRCTType.MC6845, this); CRCT = new CRCT_6845(CRCT_6845.CRCTType.MC6845, this);
//CRT = new CRTDevice(this); //CRT = new CRTDevice(this);
GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007); GateArray = new AmstradGateArray(this, AmstradGateArray.GateArrayType.Amstrad40007);
PPI = new PPI_8255(this); PPI = new PPI_8255(this);
TapeBuzzer = new Beeper(this); TapeBuzzer = new Beeper(this);
TapeBuzzer.Init(44100, FrameLength); TapeBuzzer.Init(44100, FrameLength);
//AYDevice = new PSG(this, PSG.ay38910_type_t.AY38910_TYPE_8912, GateArray.PSGClockSpeed, 882 * 50); //AYDevice = new PSG(this, PSG.ay38910_type_t.AY38910_TYPE_8912, GateArray.PSGClockSpeed, 882 * 50);
AYDevice = new AY38912(this); AYDevice = new AY38912(this);
AYDevice.Init(44100, FrameLength); AYDevice.Init(44100, FrameLength);
KeyboardDevice = new StandardKeyboard(this); KeyboardDevice = new StandardKeyboard(this);
TapeDevice = new DatacorderDevice(autoTape); TapeDevice = new DatacorderDevice(autoTape);
TapeDevice.Init(this); 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 namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// CPC6128 /// CPC6128
/// * Memory * /// * Memory *
/// </summary> /// </summary>
public partial class CPC6128 : CPCBase public partial class CPC6128 : CPCBase
{ {
/// <summary> /// <summary>
/// Simulates reading from the bus /// Simulates reading from the bus
/// ROM and RAM paging should be handled here /// ROM and RAM paging should be handled here
/// </summary> /// </summary>
public override byte ReadBus(ushort addr) public override byte ReadBus(ushort addr)
{ {
int divisor = addr / 0x4000; int divisor = addr / 0x4000;
byte result = 0xff; byte result = 0xff;
switch (divisor) switch (divisor)
{ {
// RAM 0x000 // RAM 0x000
case 0: case 0:
if (LowerROMPaged) if (LowerROMPaged)
{ {
result = ROMLower[addr % 0x4000]; result = ROMLower[addr % 0x4000];
} }
else else
{ {
switch (RAMConfig) switch (RAMConfig)
{ {
case 2: case 2:
result = RAM4[addr % 0x4000]; result = RAM4[addr % 0x4000];
break; break;
default: default:
result = RAM0[addr % 0x4000]; result = RAM0[addr % 0x4000];
break; break;
} }
} }
break; break;
// RAM 0x4000 // RAM 0x4000
case 1: case 1:
switch (RAMConfig) switch (RAMConfig)
{ {
case 0: case 0:
case 1: case 1:
result = RAM1[addr % 0x4000]; result = RAM1[addr % 0x4000];
break; break;
case 2: case 2:
case 5: case 5:
result = RAM5[addr % 0x4000]; result = RAM5[addr % 0x4000];
break; break;
case 3: case 3:
result = RAM3[addr % 0x4000]; result = RAM3[addr % 0x4000];
break; break;
case 4: case 4:
result = RAM4[addr % 0x4000]; result = RAM4[addr % 0x4000];
break; break;
case 6: case 6:
result = RAM6[addr % 0x4000]; result = RAM6[addr % 0x4000];
break; break;
case 7: case 7:
result = RAM7[addr % 0x4000]; result = RAM7[addr % 0x4000];
break; break;
} }
break; break;
// RAM 0x8000 // RAM 0x8000
case 2: case 2:
switch (RAMConfig) switch (RAMConfig)
{ {
case 2: case 2:
result = RAM6[addr % 0x4000]; result = RAM6[addr % 0x4000];
break; break;
default: default:
result = RAM2[addr % 0x4000]; result = RAM2[addr % 0x4000];
break; break;
} }
break; break;
// RAM 0xc000 // RAM 0xc000
case 3: case 3:
if (UpperROMPaged) if (UpperROMPaged)
{ {
switch (UpperROMPosition) switch (UpperROMPosition)
{ {
case 7: case 7:
result = ROM7[addr % 0x4000]; result = ROM7[addr % 0x4000];
break; break;
case 0: case 0:
default: default:
result = ROM0[addr % 0x4000]; result = ROM0[addr % 0x4000];
break; break;
} }
} }
else else
{ {
switch (RAMConfig) switch (RAMConfig)
{ {
case 1: case 1:
case 2: case 2:
case 3: case 3:
result = RAM7[addr % 0x4000]; result = RAM7[addr % 0x4000];
break; break;
default: default:
result = RAM3[addr % 0x4000]; result = RAM3[addr % 0x4000];
break; break;
} }
} }
break; break;
default: default:
break; break;
} }
return result; return result;
} }
/// <summary> /// <summary>
/// Simulates writing to the bus /// Simulates writing to the bus
/// Writes to the bus ALWAYS go to RAM, regardless of what upper and lower ROMs are paged in /// Writes to the bus ALWAYS go to RAM, regardless of what upper and lower ROMs are paged in
/// </summary> /// </summary>
public override void WriteBus(ushort addr, byte value) public override void WriteBus(ushort addr, byte value)
{ {
int divisor = addr / 0x4000; int divisor = addr / 0x4000;
switch (divisor) switch (divisor)
{ {
// RAM 0x000 // RAM 0x000
case 0: case 0:
switch (RAMConfig) switch (RAMConfig)
{ {
case 2: case 2:
RAM4[addr % 0x4000] = value; RAM4[addr % 0x4000] = value;
break; break;
default: default:
RAM0[addr % 0x4000] = value; RAM0[addr % 0x4000] = value;
break; break;
} }
break; break;
// RAM 0x4000 // RAM 0x4000
case 1: case 1:
switch (RAMConfig) switch (RAMConfig)
{ {
case 0: case 0:
case 1: case 1:
RAM1[addr % 0x4000] = value; RAM1[addr % 0x4000] = value;
break; break;
case 2: case 2:
case 5: case 5:
RAM5[addr % 0x4000] = value; RAM5[addr % 0x4000] = value;
break; break;
case 3: case 3:
RAM3[addr % 0x4000] = value; RAM3[addr % 0x4000] = value;
break; break;
case 4: case 4:
RAM4[addr % 0x4000] = value; RAM4[addr % 0x4000] = value;
break; break;
case 6: case 6:
RAM6[addr % 0x4000] = value; RAM6[addr % 0x4000] = value;
break; break;
case 7: case 7:
RAM7[addr % 0x4000] = value; RAM7[addr % 0x4000] = value;
break; break;
} }
break;
// RAM 0x8000 break;
case 2:
switch (RAMConfig)
{
case 2:
RAM6[addr % 0x4000] = value;
break;
default:
RAM2[addr % 0x4000] = value;
break;
}
break;
// RAM 0xc000 // RAM 0x8000
case 3: case 2:
switch (RAMConfig) switch (RAMConfig)
{ {
case 1: case 2:
case 2: RAM6[addr % 0x4000] = value;
case 3: break;
RAM7[addr % 0x4000] = value; default:
break; RAM2[addr % 0x4000] = value;
default: break;
RAM3[addr % 0x4000] = value; }
break; break;
}
break;
default:
break;
}
}
/// <summary> // RAM 0xc000
/// Reads a byte of data from a specified memory address case 3:
/// </summary> switch (RAMConfig)
public override byte ReadMemory(ushort addr) {
{ case 1:
var data = ReadBus(addr); case 2:
return data; case 3:
} RAM7[addr % 0x4000] = value;
break;
default:
RAM3[addr % 0x4000] = value;
break;
}
break;
default:
break;
}
}
/// <summary> /// <summary>
/// Writes a byte of data to a specified memory address /// Reads a byte of data from a specified memory address
/// (with memory contention if appropriate) /// </summary>
/// </summary> public override byte ReadMemory(ushort addr)
public override void WriteMemory(ushort addr, byte value) {
{ var data = ReadBus(addr);
WriteBus(addr, value); 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> /// <summary>
/// Sets up the ROM /// Sets up the ROM
/// </summary> /// </summary>
public override void InitROM(RomData[] romData) public override void InitROM(RomData[] romData)
{ {
foreach (var r in romData) foreach (var r in romData)
{ {
if (r.ROMType == RomData.ROMChipType.Lower) if (r.ROMType == RomData.ROMChipType.Lower)
{ {
for (int i = 0; i < 0x4000; i++) for (int i = 0; i < 0x4000; i++)
{ {
ROMLower[i] = r.RomBytes[i]; ROMLower[i] = r.RomBytes[i];
} }
} }
else else
{ {
for (int i = 0; i < 0x4000; i++) for (int i = 0; i < 0x4000; i++)
{ {
switch (r.ROMPosition) switch (r.ROMPosition)
{ {
case 0: case 0:
ROM0[i] = r.RomBytes[i]; ROM0[i] = r.RomBytes[i];
break; break;
case 7: case 7:
ROM7[i] = r.RomBytes[i]; ROM7[i] = r.RomBytes[i];
break; break;
} }
} }
} }
} }
LowerROMPaged = true; LowerROMPaged = true;
UpperROMPaged = true; UpperROMPaged = true;
} }
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,213 +3,213 @@ using BizHawk.Emulation.Cores.Components.Z80A;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// The abstract class that all emulated models will inherit from /// The abstract class that all emulated models will inherit from
/// * Main properties / fields / contruction* /// * Main properties / fields / contruction*
/// </summary> /// </summary>
public abstract partial class CPCBase public abstract partial class CPCBase
{ {
#region Devices #region Devices
/// <summary> /// <summary>
/// The calling ZXSpectrum class (piped in via constructor) /// The calling ZXSpectrum class (piped in via constructor)
/// </summary> /// </summary>
public AmstradCPC CPC { get; set; } public AmstradCPC CPC { get; set; }
/// <summary> /// <summary>
/// Reference to the instantiated Z80 cpu (piped in via constructor) /// Reference to the instantiated Z80 cpu (piped in via constructor)
/// </summary> /// </summary>
public Z80A CPU { get; set; } public Z80A CPU { get; set; }
/// <summary> /// <summary>
/// ROM and extended info /// ROM and extended info
/// </summary> /// </summary>
public RomData RomData { get; set; } public RomData RomData { get; set; }
/// <summary> /// <summary>
/// The Amstrad datacorder device /// The Amstrad datacorder device
/// </summary> /// </summary>
public virtual DatacorderDevice TapeDevice { get; set; } public virtual DatacorderDevice TapeDevice { get; set; }
/// <summary> /// <summary>
/// beeper output for the tape /// beeper output for the tape
/// </summary> /// </summary>
public IBeeperDevice TapeBuzzer { get; set; } public IBeeperDevice TapeBuzzer { get; set; }
/// <summary> /// <summary>
/// Device representing the AY-3-8912 chip found in the CPC /// Device representing the AY-3-8912 chip found in the CPC
/// </summary> /// </summary>
public IPSG AYDevice { get; set; } public IPSG AYDevice { get; set; }
/// <summary> /// <summary>
/// The keyboard device /// The keyboard device
/// Technically, this is controlled by the PSG, but has been abstracted due to the port over from ZXHawk /// Technically, this is controlled by the PSG, but has been abstracted due to the port over from ZXHawk
/// </summary> /// </summary>
public IKeyboard KeyboardDevice { get; set; } public IKeyboard KeyboardDevice { get; set; }
/// <summary> /// <summary>
/// The Amstrad disk drive /// The Amstrad disk drive
/// </summary> /// </summary>
public virtual NECUPD765 UPDDiskDevice { get; set; } public virtual NECUPD765 UPDDiskDevice { get; set; }
/// <summary> /// <summary>
/// The Cathode Ray Tube Controller chip /// The Cathode Ray Tube Controller chip
/// </summary> /// </summary>
public CRCT_6845 CRCT { get; set; } public CRCT_6845 CRCT { get; set; }
/// <summary> /// <summary>
/// The Amstrad gate array /// The Amstrad gate array
/// </summary> /// </summary>
public AmstradGateArray GateArray { get; set; } public AmstradGateArray GateArray { get; set; }
// /// <summary> // /// <summary>
// /// Renders pixels to the screen // /// Renders pixels to the screen
// /// </summary> // /// </summary>
// public CRTDevice CRT { get; set; } // public CRTDevice CRT { get; set; }
/// <summary> /// <summary>
/// The PPI contoller chip /// The PPI contoller chip
/// </summary> /// </summary>
public PPI_8255 PPI { get; set; } public PPI_8255 PPI { get; set; }
/// <summary> /// <summary>
/// The length of a standard frame in CPU cycles /// The length of a standard frame in CPU cycles
/// </summary> /// </summary>
public int FrameLength; public int FrameLength;
#endregion #endregion
#region Emulator State #region Emulator State
/// <summary> /// <summary>
/// Signs whether the frame has ended /// Signs whether the frame has ended
/// </summary> /// </summary>
public bool FrameCompleted; public bool FrameCompleted;
/// <summary> /// <summary>
/// Overflow from the previous frame (in Z80 cycles) /// Overflow from the previous frame (in Z80 cycles)
/// </summary> /// </summary>
public int OverFlow; public int OverFlow;
/// <summary> /// <summary>
/// The total number of frames rendered /// The total number of frames rendered
/// </summary> /// </summary>
public int FrameCount; public int FrameCount;
/// <summary> /// <summary>
/// The current cycle (T-State) that we are at in the frame /// The current cycle (T-State) that we are at in the frame
/// </summary> /// </summary>
public long _frameCycles; public long _frameCycles;
/// <summary> /// <summary>
/// Stores where we are in the frame after each CPU cycle /// Stores where we are in the frame after each CPU cycle
/// </summary> /// </summary>
public long LastFrameStartCPUTick; public long LastFrameStartCPUTick;
/// <summary> /// <summary>
/// Gets the current frame cycle according to the CPU tick count /// Gets the current frame cycle according to the CPU tick count
/// </summary> /// </summary>
public virtual long CurrentFrameCycle => GateArray.FrameClock; // CPU.TotalExecutedCycles - LastFrameStartCPUTick; public virtual long CurrentFrameCycle => GateArray.FrameClock; // CPU.TotalExecutedCycles - LastFrameStartCPUTick;
/// <summary> /// <summary>
/// Non-Deterministic bools /// Non-Deterministic bools
/// </summary> /// </summary>
public bool _render; public bool _render;
public bool _renderSound; public bool _renderSound;
#endregion #endregion
#region Constants #region Constants
/// <summary> /// <summary>
/// Mask constants & misc /// Mask constants & misc
/// </summary> /// </summary>
protected const int BORDER_BIT = 0x07; protected const int BORDER_BIT = 0x07;
protected const int EAR_BIT = 0x10; protected const int EAR_BIT = 0x10;
protected const int MIC_BIT = 0x08; protected const int MIC_BIT = 0x08;
protected const int TAPE_BIT = 0x40; protected const int TAPE_BIT = 0x40;
protected const int AY_SAMPLE_RATE = 16; protected const int AY_SAMPLE_RATE = 16;
#endregion #endregion
#region Emulation Loop #region Emulation Loop
/// <summary> /// <summary>
/// Executes a single frame /// Executes a single frame
/// </summary> /// </summary>
public virtual void ExecuteFrame(bool render, bool renderSound) public virtual void ExecuteFrame(bool render, bool renderSound)
{ {
GateArray.FrameEnd = false; GateArray.FrameEnd = false;
CRCT.lineCounter = 0; CRCT.lineCounter = 0;
InputRead = false; InputRead = false;
_render = render; _render = render;
_renderSound = renderSound; _renderSound = renderSound;
FrameCompleted = false; FrameCompleted = false;
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.StartFrame(); TapeDevice.StartFrame();
if (_renderSound) if (_renderSound)
{ {
AYDevice.StartFrame(); AYDevice.StartFrame();
} }
PollInput(); PollInput();
//CRT.SetupVideo(); //CRT.SetupVideo();
//CRT.ScanlineCounter = 0; //CRT.ScanlineCounter = 0;
while (!GateArray.FrameEnd) while (!GateArray.FrameEnd)
{ {
GateArray.ClockCycle(); GateArray.ClockCycle();
// cycle the tape device // cycle the tape device
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.TapeCycle(); TapeDevice.TapeCycle();
} }
// we have reached the end of a frame // we have reached the end of a frame
LastFrameStartCPUTick = CPU.TotalExecutedCycles; // - OverFlow; LastFrameStartCPUTick = CPU.TotalExecutedCycles; // - OverFlow;
if (AYDevice != null) if (AYDevice != null)
AYDevice.EndFrame(); AYDevice.EndFrame();
FrameCount++; FrameCount++;
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.EndFrame(); TapeDevice.EndFrame();
FrameCompleted = true; FrameCompleted = true;
// is this a lag frame? // is this a lag frame?
CPC.IsLagFrame = !InputRead; CPC.IsLagFrame = !InputRead;
// FDC debug // FDC debug
if (UPDDiskDevice != null && UPDDiskDevice.writeDebug) if (UPDDiskDevice != null && UPDDiskDevice.writeDebug)
{ {
// only write UPD log every second // only write UPD log every second
if (FrameCount % 10 == 0) if (FrameCount % 10 == 0)
{ {
System.IO.File.AppendAllLines(UPDDiskDevice.outputfile, UPDDiskDevice.dLog); System.IO.File.AppendAllLines(UPDDiskDevice.outputfile, UPDDiskDevice.dLog);
UPDDiskDevice.dLog = new System.Collections.Generic.List<string>(); UPDDiskDevice.dLog = new System.Collections.Generic.List<string>();
//System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString); //System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString);
} }
} }
GateArray.FrameClock = 0; GateArray.FrameClock = 0;
} }
#endregion #endregion
#region Reset Functions #region Reset Functions
/// <summary> /// <summary>
/// Hard reset of the emulated machine /// Hard reset of the emulated machine
/// </summary> /// </summary>
public virtual void HardReset() public virtual void HardReset()
{ {
/* /*
//ULADevice.ResetInterrupt(); //ULADevice.ResetInterrupt();
ROMPaged = 0; ROMPaged = 0;
SpecialPagingMode = false; SpecialPagingMode = false;
@ -256,14 +256,14 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
} }
} }
*/ */
} }
/// <summary> /// <summary>
/// Soft reset of the emulated machine /// Soft reset of the emulated machine
/// </summary> /// </summary>
public virtual void SoftReset() public virtual void SoftReset()
{ {
/* /*
//ULADevice.ResetInterrupt(); //ULADevice.ResetInterrupt();
ROMPaged = 0; ROMPaged = 0;
SpecialPagingMode = false; SpecialPagingMode = false;
@ -310,65 +310,65 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
} }
} }
*/ */
} }
#endregion #endregion
#region IStatable #region IStatable
public void SyncState(Serializer ser) public void SyncState(Serializer ser)
{ {
ser.BeginSection("CPCMachine"); ser.BeginSection("CPCMachine");
ser.Sync(nameof(FrameCompleted), ref FrameCompleted); ser.Sync(nameof(FrameCompleted), ref FrameCompleted);
ser.Sync(nameof(OverFlow), ref OverFlow); ser.Sync(nameof(OverFlow), ref OverFlow);
ser.Sync(nameof(FrameCount), ref FrameCount); ser.Sync(nameof(FrameCount), ref FrameCount);
ser.Sync(nameof(_frameCycles), ref _frameCycles); ser.Sync(nameof(_frameCycles), ref _frameCycles);
ser.Sync(nameof(inputRead), ref inputRead); ser.Sync(nameof(inputRead), ref inputRead);
ser.Sync(nameof(LastFrameStartCPUTick), ref LastFrameStartCPUTick); ser.Sync(nameof(LastFrameStartCPUTick), ref LastFrameStartCPUTick);
ser.Sync(nameof(ROMLower), ref ROMLower, false); ser.Sync(nameof(ROMLower), ref ROMLower, false);
ser.Sync(nameof(ROM0), ref ROM0, false); ser.Sync(nameof(ROM0), ref ROM0, false);
ser.Sync(nameof(ROM7), ref ROM7, false); ser.Sync(nameof(ROM7), ref ROM7, false);
ser.Sync(nameof(RAM0), ref RAM0, false); ser.Sync(nameof(RAM0), ref RAM0, false);
ser.Sync(nameof(RAM1), ref RAM1, false); ser.Sync(nameof(RAM1), ref RAM1, false);
ser.Sync(nameof(RAM2), ref RAM2, false); ser.Sync(nameof(RAM2), ref RAM2, false);
ser.Sync(nameof(RAM3), ref RAM3, false); ser.Sync(nameof(RAM3), ref RAM3, false);
ser.Sync(nameof(RAM4), ref RAM4, false); ser.Sync(nameof(RAM4), ref RAM4, false);
ser.Sync(nameof(RAM5), ref RAM5, false); ser.Sync(nameof(RAM5), ref RAM5, false);
ser.Sync(nameof(RAM6), ref RAM6, false); ser.Sync(nameof(RAM6), ref RAM6, false);
ser.Sync(nameof(RAM7), ref RAM7, false); ser.Sync(nameof(RAM7), ref RAM7, false);
ser.Sync(nameof(UpperROMPosition), ref UpperROMPosition); ser.Sync(nameof(UpperROMPosition), ref UpperROMPosition);
ser.Sync(nameof(UpperROMPaged), ref UpperROMPaged); ser.Sync(nameof(UpperROMPaged), ref UpperROMPaged);
ser.Sync(nameof(LowerROMPaged), ref LowerROMPaged); ser.Sync(nameof(LowerROMPaged), ref LowerROMPaged);
ser.Sync(nameof(RAMConfig), ref RAMConfig); ser.Sync(nameof(RAMConfig), ref RAMConfig);
ser.Sync(nameof(RAM64KBank), ref RAM64KBank); ser.Sync(nameof(RAM64KBank), ref RAM64KBank);
CRCT.SyncState(ser); CRCT.SyncState(ser);
//CRT.SyncState(ser); //CRT.SyncState(ser);
GateArray.SyncState(ser); GateArray.SyncState(ser);
KeyboardDevice.SyncState(ser); KeyboardDevice.SyncState(ser);
TapeBuzzer.SyncState(ser); TapeBuzzer.SyncState(ser);
AYDevice.SyncState(ser); AYDevice.SyncState(ser);
ser.Sync(nameof(tapeMediaIndex), ref tapeMediaIndex); ser.Sync(nameof(tapeMediaIndex), ref tapeMediaIndex);
if (ser.IsReader) if (ser.IsReader)
TapeMediaIndex = tapeMediaIndex; TapeMediaIndex = tapeMediaIndex;
TapeDevice.SyncState(ser); TapeDevice.SyncState(ser);
ser.Sync(nameof(diskMediaIndex), ref diskMediaIndex); ser.Sync(nameof(diskMediaIndex), ref diskMediaIndex);
if (ser.IsReader) if (ser.IsReader)
DiskMediaIndex = diskMediaIndex; DiskMediaIndex = diskMediaIndex;
if (UPDDiskDevice != null) if (UPDDiskDevice != null)
{ {
UPDDiskDevice.SyncState(ser); 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 namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{ {
/// <summary> /// <summary>
/// The abstract class that all emulated models will inherit from /// The abstract class that all emulated models will inherit from
/// * Amstrad Gate Array * /// * Amstrad Gate Array *
/// https://web.archive.org/web/20170612081209/http://www.grimware.org/doku.php/documentations/devices/gatearray /// https://web.archive.org/web/20170612081209/http://www.grimware.org/doku.php/documentations/devices/gatearray
/// </summary> /// </summary>
public abstract class GateArrayBase : IVideoProvider public abstract class GateArrayBase : IVideoProvider
{ {
public int Z80ClockSpeed = 4000000; public int Z80ClockSpeed = 4000000;
public int FrameLength = 79872; public int FrameLength = 79872;
#region Devices #region Devices
private CPCBase _machine; private CPCBase _machine;
private Z80A CPU => _machine.CPU; private Z80A CPU => _machine.CPU;
private CRCT_6845 CRCT => _machine.CRCT; private CRCT_6845 CRCT => _machine.CRCT;
private IPSG PSG => _machine.AYDevice; private IPSG PSG => _machine.AYDevice;
#endregion #endregion
#region Constants #region Constants
/// <summary> /// <summary>
/// CRTC Register constants /// CRTC Register constants
/// </summary> /// </summary>
public const int HOR_TOTAL = 0; public const int HOR_TOTAL = 0;
public const int HOR_DISPLAYED = 1; public const int HOR_DISPLAYED = 1;
public const int HOR_SYNC_POS = 2; public const int HOR_SYNC_POS = 2;
public const int HOR_AND_VER_SYNC_WIDTHS = 3; public const int HOR_AND_VER_SYNC_WIDTHS = 3;
public const int VER_TOTAL = 4; public const int VER_TOTAL = 4;
public const int VER_TOTAL_ADJUST = 5; public const int VER_TOTAL_ADJUST = 5;
public const int VER_DISPLAYED = 6; public const int VER_DISPLAYED = 6;
public const int VER_SYNC_POS = 7; public const int VER_SYNC_POS = 7;
public const int INTERLACE_SKEW = 8; public const int INTERLACE_SKEW = 8;
public const int MAX_RASTER_ADDR = 9; public const int MAX_RASTER_ADDR = 9;
public const int CUR_START_RASTER = 10; public const int CUR_START_RASTER = 10;
public const int CUR_END_RASTER = 11; public const int CUR_END_RASTER = 11;
public const int DISP_START_ADDR_H = 12; public const int DISP_START_ADDR_H = 12;
public const int DISP_START_ADDR_L = 13; public const int DISP_START_ADDR_L = 13;
public const int CUR_ADDR_H = 14; public const int CUR_ADDR_H = 14;
public const int CUR_ADDR_L = 15; public const int CUR_ADDR_L = 15;
public const int LPEN_ADDR_H = 16; public const int LPEN_ADDR_H = 16;
public const int LPEN_ADDR_L = 17; public const int LPEN_ADDR_L = 17;
#endregion #endregion
#region Palletes #region Palletes
/// <summary> /// <summary>
/// The standard CPC Pallete (ordered by firmware #) /// The standard CPC Pallete (ordered by firmware #)
/// http://www.cpcwiki.eu/index.php/CPC_Palette /// http://www.cpcwiki.eu/index.php/CPC_Palette
/// </summary> /// </summary>
private static readonly int[] CPCFirmwarePalette = private static readonly int[] CPCFirmwarePalette =
{ {
Colors.ARGB(0x00, 0x00, 0x00), // Black Colors.ARGB(0x00, 0x00, 0x00), // Black
Colors.ARGB(0x00, 0x00, 0x80), // Blue Colors.ARGB(0x00, 0x00, 0x80), // Blue
Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue
Colors.ARGB(0x80, 0x00, 0x00), // Red Colors.ARGB(0x80, 0x00, 0x00), // Red
@ -92,13 +92,13 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White
}; };
/// <summary> /// <summary>
/// The standard CPC Pallete (ordered by hardware #) /// The standard CPC Pallete (ordered by hardware #)
/// http://www.cpcwiki.eu/index.php/CPC_Palette /// http://www.cpcwiki.eu/index.php/CPC_Palette
/// </summary> /// </summary>
private static readonly int[] CPCHardwarePalette = private static readonly int[] CPCHardwarePalette =
{ {
Colors.ARGB(0x80, 0x80, 0x80), // White Colors.ARGB(0x80, 0x80, 0x80), // White
Colors.ARGB(0x80, 0x80, 0x80), // White (duplicate) Colors.ARGB(0x80, 0x80, 0x80), // White (duplicate)
Colors.ARGB(0x00, 0xFF, 0x80), // Sea Green Colors.ARGB(0x00, 0xFF, 0x80), // Sea Green
Colors.ARGB(0xFF, 0xFF, 0x80), // Pastel Yellow Colors.ARGB(0xFF, 0xFF, 0x80), // Pastel Yellow
@ -132,374 +132,374 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
Colors.ARGB(0x80, 0x80, 0xFF), // Pastel Blue Colors.ARGB(0x80, 0x80, 0xFF), // Pastel Blue
}; };
#endregion #endregion
#region Construction #region Construction
public GateArrayBase(CPCBase machine) public GateArrayBase(CPCBase machine)
{ {
_machine = machine; _machine = machine;
PenColours = new int[17]; PenColours = new int[17];
SetupScreenSize(); SetupScreenSize();
Reset(); Reset();
} }
/// <summary> /// <summary>
/// Inits the pen lookup table /// Inits the pen lookup table
/// </summary> /// </summary>
public void SetupScreenMapping() public void SetupScreenMapping()
{ {
for (int m = 0; m < 4; m++) for (int m = 0; m < 4; m++)
{ {
Lookup[m] = new int[256 * 8]; Lookup[m] = new int[256 * 8];
int pos = 0; int pos = 0;
for (int b = 0; b < 256; b++) for (int b = 0; b < 256; b++)
{ {
switch (m) switch (m)
{ {
case 1: case 1:
int pc = (((b & 0x80) >> 7) | ((b & 0x80) >> 2)); int pc = (((b & 0x80) >> 7) | ((b & 0x80) >> 2));
Lookup[m][pos++] = pc; Lookup[m][pos++] = pc;
Lookup[m][pos++] = pc; Lookup[m][pos++] = pc;
pc = (((b & 0x40) >> 6) | ((b & 0x04) >> 1)); pc = (((b & 0x40) >> 6) | ((b & 0x04) >> 1));
Lookup[m][pos++] = pc; Lookup[m][pos++] = pc;
Lookup[m][pos++] = pc; Lookup[m][pos++] = pc;
pc = (((b & 0x20) >> 5) | (b & 0x02)); pc = (((b & 0x20) >> 5) | (b & 0x02));
Lookup[m][pos++] = pc; Lookup[m][pos++] = pc;
Lookup[m][pos++] = pc; Lookup[m][pos++] = pc;
pc = (((b & 0x10) >> 4) | ((b & 0x01) << 1)); pc = (((b & 0x10) >> 4) | ((b & 0x01) << 1));
break; break;
case 2: case 2:
for (int i = 7; i >= 0; i--) for (int i = 7; i >= 0; i--)
{ {
bool pixel_on = ((b & (1 << i)) != 0); bool pixel_on = ((b & (1 << i)) != 0);
Lookup[m][pos++] = pixel_on ? 1 : 0; Lookup[m][pos++] = pixel_on ? 1 : 0;
} }
break; break;
case 3: case 3:
case 0: case 0:
int pc2 = (b & 0xAA); int pc2 = (b & 0xAA);
pc2 = ( pc2 = (
((pc2 & 0x80) >> 7) | ((pc2 & 0x80) >> 7) |
((pc2 & 0x08) >> 2) | ((pc2 & 0x08) >> 2) |
((pc2 & 0x20) >> 3) | ((pc2 & 0x20) >> 3) |
((pc2 & 0x02) << 2)); ((pc2 & 0x02) << 2));
Lookup[m][pos++] = pc2; Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2; Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2; Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2; Lookup[m][pos++] = pc2;
pc2 = (b & 0x55); pc2 = (b & 0x55);
pc2 = ( pc2 = (
((pc2 & 0x40) >> 6) | ((pc2 & 0x40) >> 6) |
((pc2 & 0x04) >> 1) | ((pc2 & 0x04) >> 1) |
((pc2 & 0x10) >> 2) | ((pc2 & 0x10) >> 2) |
((pc2 & 0x01) << 3)); ((pc2 & 0x01) << 3));
Lookup[m][pos++] = pc2; Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2; Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2; Lookup[m][pos++] = pc2;
Lookup[m][pos++] = pc2; Lookup[m][pos++] = pc2;
break; break;
} }
} }
} }
} }
#endregion #endregion
#region State #region State
private int[] PenColours; private int[] PenColours;
private int CurrentPen; private int CurrentPen;
private int ScreenMode; private int ScreenMode;
private int INTScanlineCnt; private int INTScanlineCnt;
//private int VSYNCDelyCnt; //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 LatchedMode;
//private int buffPos; //private int buffPos;
public bool FrameEnd; public bool FrameEnd;
public bool WaitLine; public bool WaitLine;
#endregion #endregion
#region Clock Operations #region Clock Operations
/// <summary> /// <summary>
/// The gatearray runs on a 16Mhz clock /// The gatearray runs on a 16Mhz clock
/// (for the purposes of emulation, we will use a 4Mhz clock) /// (for the purposes of emulation, we will use a 4Mhz clock)
/// From this it generates: /// From this it generates:
/// 1Mhz clock for the CRTC chip /// 1Mhz clock for the CRTC chip
/// 1Mhz clock for the AY-3-8912 PSG /// 1Mhz clock for the AY-3-8912 PSG
/// 4Mhz clock for the Z80 CPU /// 4Mhz clock for the Z80 CPU
/// </summary> /// </summary>
public void ClockCycle() public void ClockCycle()
{ {
// 4-phase clock // 4-phase clock
for (int i = 1; i < 5; i++) for (int i = 1; i < 5; i++)
{ {
switch (i) switch (i)
{ {
// Phase 1 // Phase 1
case 1: case 1:
CRCT.ClockCycle(); CRCT.ClockCycle();
CPU.ExecuteOne(); CPU.ExecuteOne();
break; break;
// Phase 2 // Phase 2
case 2: case 2:
CPU.ExecuteOne(); CPU.ExecuteOne();
break; break;
// Phase 3 // Phase 3
case 3: case 3:
// video fetch // video fetch
break; break;
// Phase 4 // Phase 4
case 4: case 4:
// video fetch // video fetch
break; break;
} }
} }
} }
#endregion #endregion
#region Internal Methods #region Internal Methods
/// <summary> /// <summary>
/// Selects the pen /// Selects the pen
/// </summary> /// </summary>
public virtual void SetPen(BitArray bi) public virtual void SetPen(BitArray bi)
{ {
if (bi[4]) if (bi[4])
{ {
// border select // border select
CurrentPen = 16; CurrentPen = 16;
} }
else else
{ {
// pen select // pen select
byte[] b = new byte[1]; byte[] b = new byte[1];
bi.CopyTo(b, 0); bi.CopyTo(b, 0);
CurrentPen = b[0] & 0x0f; CurrentPen = b[0] & 0x0f;
} }
} }
/// <summary> /// <summary>
/// Selects colour for the currently selected pen /// Selects colour for the currently selected pen
/// </summary> /// </summary>
public virtual void SetPenColour(BitArray bi) public virtual void SetPenColour(BitArray bi)
{ {
byte[] b = new byte[1]; byte[] b = new byte[1];
bi.CopyTo(b, 0); bi.CopyTo(b, 0);
var colour = b[0] & 0x1f; var colour = b[0] & 0x1f;
PenColours[CurrentPen] = colour; PenColours[CurrentPen] = colour;
} }
/// <summary> /// <summary>
/// Returns the actual ARGB pen colour value /// Returns the actual ARGB pen colour value
/// </summary> /// </summary>
public virtual int GetPenColour(int idx) public virtual int GetPenColour(int idx)
{ {
return CPCHardwarePalette[PenColours[idx]]; return CPCHardwarePalette[PenColours[idx]];
} }
/// <summary> /// <summary>
/// Screen mode and ROM config /// Screen mode and ROM config
/// </summary> /// </summary>
public virtual void SetReg2(BitArray bi) public virtual void SetReg2(BitArray bi)
{ {
byte[] b = new byte[1]; byte[] b = new byte[1];
bi.CopyTo(b, 0); bi.CopyTo(b, 0);
// screen mode // screen mode
var mode = b[0] & 0x03; var mode = b[0] & 0x03;
ScreenMode = mode; ScreenMode = mode;
// ROM // ROM
// upper // upper
if ((b[0] & 0x08) != 0) if ((b[0] & 0x08) != 0)
{ {
_machine.UpperROMPaged = false; _machine.UpperROMPaged = false;
} }
else else
{ {
_machine.UpperROMPaged = true; _machine.UpperROMPaged = true;
} }
// lower // lower
if ((b[0] & 0x04) != 0) if ((b[0] & 0x04) != 0)
{ {
_machine.LowerROMPaged = false; _machine.LowerROMPaged = false;
} }
else else
{ {
_machine.LowerROMPaged = true; _machine.LowerROMPaged = true;
} }
// INT delay // INT delay
if ((b[0] & 0x10) != 0) if ((b[0] & 0x10) != 0)
{ {
INTScanlineCnt = 0; INTScanlineCnt = 0;
} }
} }
/// <summary> /// <summary>
/// Only available on machines with a 64KB memory expansion /// Only available on machines with a 64KB memory expansion
/// Default assume we dont have this /// Default assume we dont have this
/// </summary> /// </summary>
public virtual void SetRAM(BitArray bi) public virtual void SetRAM(BitArray bi)
{ {
return; return;
} }
public void InterruptACK() public void InterruptACK()
{ {
INTScanlineCnt &= 0x01f; INTScanlineCnt &= 0x01f;
} }
#endregion
#region Reset
public void Reset() #endregion
{
CurrentPen = 0;
ScreenMode = 1;
for (int i = 0; i < 17; i++)
PenColours[i] = 0;
INTScanlineCnt = 0;
//VSYNCDelyCnt = 0;
}
#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> #endregion
/// Device responds to an IN instruction
/// </summary>
public bool ReadPort(ushort port, ref int result)
{
// gate array is OUT only
return false;
}
/// <summary> #region IPortIODevice
/// 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));
// The gate array responds to port 0x7F /// <summary>
bool accessed = !portBits[15]; /// Device responds to an IN instruction
if (!accessed) /// </summary>
return false; 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 /// <summary>
if (!dataBits[6] && !dataBits[7]) /// Device responds to an OUT instruction
{ /// </summary>
// select pen public bool WritePort(ushort port, int result)
SetPen(dataBits); {
} BitArray portBits = new BitArray(BitConverter.GetBytes(port));
BitArray dataBits = new BitArray(BitConverter.GetBytes((byte)result));
if (dataBits[6] && !dataBits[7]) // The gate array responds to port 0x7F
{ bool accessed = !portBits[15];
// select colour for selected pen if (!accessed)
SetPenColour(dataBits); return false;
}
if (!dataBits[6] && dataBits[7]) // Bit 9 and 8 of the data byte define the function to access
{ if (!dataBits[6] && !dataBits[7])
// select screen mode, ROM configuration and interrupt control {
SetReg2(dataBits); // select pen
} SetPen(dataBits);
}
if (dataBits[6] && dataBits[7]) if (dataBits[6] && !dataBits[7])
{ {
// RAM memory management // select colour for selected pen
SetRAM(dataBits); 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> #endregion
/// Video output buffer
/// </summary>
public int[] ScreenBuffer;
private int _virtualWidth; #region IVideoProvider
private int _virtualHeight;
private int _bufferWidth;
private int _bufferHeight;
public int BackgroundColor /// <summary>
{ /// Video output buffer
get { return CPCHardwarePalette[16]; } /// </summary>
} public int[] ScreenBuffer;
public int VirtualWidth private int _virtualWidth;
{ private int _virtualHeight;
get { return _virtualWidth; } private int _bufferWidth;
set { _virtualWidth = value; } private int _bufferHeight;
}
public int VirtualHeight public int BackgroundColor
{ {
get { return _virtualHeight; } get { return CPCHardwarePalette[16]; }
set { _virtualHeight = value; } }
}
public int BufferWidth public int VirtualWidth
{ {
get { return _bufferWidth; } get { return _virtualWidth; }
set { _bufferWidth = value; } set { _virtualWidth = value; }
} }
public int BufferHeight public int VirtualHeight
{ {
get { return _bufferHeight; } get { return _virtualHeight; }
set { _bufferHeight = value; } set { _virtualHeight = value; }
} }
public int VsyncNumerator public int BufferWidth
{ {
get { return Z80ClockSpeed * 50; } get { return _bufferWidth; }
set { } set { _bufferWidth = value; }
} }
public int VsyncDenominator public int BufferHeight
{ {
get { return Z80ClockSpeed; } get { return _bufferHeight; }
} set { _bufferHeight = value; }
}
public int[] GetVideoBuffer() public int VsyncNumerator
{ {
return ScreenBuffer; 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) * Rect Pixels: Mode 0: 160×200 pixels with 16 colors (4 bpp)
Sqaure Pixels: Mode 1: 320×200 pixels with 4 colors (2 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) 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 // define maximum screen buffer size
// to fit all possible resolutions, 640x400 should do it // to fit all possible resolutions, 640x400 should do it
// therefore a scanline will take two buffer rows // therefore a scanline will take two buffer rows
// and buffer columns will be: // and buffer columns will be:
// Mode 1: 2 pixels // Mode 1: 2 pixels
// Mode 2: 1 pixel // Mode 2: 1 pixel
// Mode 0: 4 pixels // Mode 0: 4 pixels
// Mode 3: 4 pixels // Mode 3: 4 pixels
BufferWidth = 640; BufferWidth = 640;
BufferHeight = 400; BufferHeight = 400;
VirtualHeight = BufferHeight; VirtualHeight = BufferHeight;
VirtualWidth = BufferWidth; VirtualWidth = BufferWidth;
ScreenBuffer = new int[BufferWidth * BufferHeight]; ScreenBuffer = new int[BufferWidth * BufferHeight];
croppedBuffer = ScreenBuffer; croppedBuffer = ScreenBuffer;
} }
protected int[] croppedBuffer; protected int[] croppedBuffer;
#endregion #endregion
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,69 +2,69 @@
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// Represents a spectrum keyboard /// Represents a spectrum keyboard
/// </summary> /// </summary>
public interface IKeyboard : IPortIODevice public interface IKeyboard : IPortIODevice
{ {
/// <summary> /// <summary>
/// The calling spectrumbase class /// The calling spectrumbase class
/// </summary> /// </summary>
SpectrumBase _machine { get; } SpectrumBase _machine { get; }
/// <summary> /// <summary>
/// The keyboard matrix for a particular spectrum model /// The keyboard matrix for a particular spectrum model
/// </summary> /// </summary>
string[] KeyboardMatrix { get; set; } string[] KeyboardMatrix { get; set; }
/// <summary> /// <summary>
/// Other keyboard keys that are not in the matrix /// Other keyboard keys that are not in the matrix
/// (usually keys derived from key combos) /// (usually keys derived from key combos)
/// </summary> /// </summary>
string[] NonMatrixKeys { get; set; } string[] NonMatrixKeys { get; set; }
/// <summary> /// <summary>
/// Represents the spectrum key state /// Represents the spectrum key state
/// </summary> /// </summary>
int[] KeyLine { get; set; } int[] KeyLine { get; set; }
/// <summary> /// <summary>
/// Resets the line status /// Resets the line status
/// </summary> /// </summary>
void ResetLineStatus(); void ResetLineStatus();
/// <summary> /// <summary>
/// There are some slight differences in how PortIN and PortOUT functions /// There are some slight differences in how PortIN and PortOUT functions
/// between Issue2 and Issue3 keyboards (16k/48k spectrum only) /// between Issue2 and Issue3 keyboards (16k/48k spectrum only)
/// It is possible that some very old games require Issue2 emulation /// It is possible that some very old games require Issue2 emulation
/// </summary> /// </summary>
bool IsIssue2Keyboard { get; set; } bool IsIssue2Keyboard { get; set; }
/// <summary> /// <summary>
/// Sets the spectrum key status /// Sets the spectrum key status
/// </summary> /// </summary>
void SetKeyStatus(string key, bool isPressed); void SetKeyStatus(string key, bool isPressed);
/// <summary> /// <summary>
/// Gets the status of a spectrum key /// Gets the status of a spectrum key
/// </summary> /// </summary>
bool GetKeyStatus(string key); bool GetKeyStatus(string key);
/// <summary> /// <summary>
/// Returns the query byte /// Returns the query byte
/// </summary> /// </summary>
byte GetLineStatus(byte lines); byte GetLineStatus(byte lines);
/// <summary> /// <summary>
/// Reads a keyboard byte /// Reads a keyboard byte
/// </summary> /// </summary>
byte ReadKeyboardByte(ushort addr); byte ReadKeyboardByte(ushort addr);
/// <summary> /// <summary>
/// Looks up a key in the keyboard matrix and returns the relevent byte value /// Looks up a key in the keyboard matrix and returns the relevent byte value
/// </summary> /// </summary>
byte GetByteFromKeyMatrix(string key); 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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// Represents a PSG device (in this case an AY-3-891x) /// Represents a PSG device (in this case an AY-3-891x)
/// </summary> /// </summary>
public interface IPSG : ISoundProvider, IPortIODevice public interface IPSG : ISoundProvider, IPortIODevice
{ {
/// <summary> /// <summary>
/// Initlization routine /// Initlization routine
/// </summary> /// </summary>
void Init(int sampleRate, int tStatesPerFrame); void Init(int sampleRate, int tStatesPerFrame);
/// <summary> /// <summary>
/// Activates a register /// Activates a register
/// </summary> /// </summary>
int SelectedRegister { get; set; } int SelectedRegister { get; set; }
int[] ExportRegisters(); int[] ExportRegisters();
/// <summary>
/// Writes to the PSG
/// </summary>
void PortWrite(int value);
/// <summary> /// <summary>
/// Reads from the PSG /// Writes to the PSG
/// </summary> /// </summary>
int PortRead(); void PortWrite(int value);
/// <summary> /// <summary>
/// Resets the PSG /// Reads from the PSG
/// </summary> /// </summary>
void Reset(); int PortRead();
/// <summary>
/// The volume of the AY chip
/// </summary>
int Volume { get; set; }
/// <summary> /// <summary>
/// Called at the start of a frame /// Resets the PSG
/// </summary> /// </summary>
void StartFrame(); void Reset();
/// <summary> /// <summary>
/// called at the end of a frame /// The volume of the AY chip
/// </summary> /// </summary>
void EndFrame(); int Volume { get; set; }
/// <summary> /// <summary>
/// Updates the sound based on number of frame cycles /// Called at the start of a frame
/// </summary> /// </summary>
void UpdateSound(int frameCycle); void StartFrame();
/// <summary> /// <summary>
/// IStatable serialization /// called at the end of a frame
/// </summary> /// </summary>
void SyncState(Serializer ser); 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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// Represents a device that utilizes port IN & OUT /// Represents a device that utilizes port IN & OUT
/// </summary> /// </summary>
public interface IPortIODevice public interface IPortIODevice
{ {
/// <summary> /// <summary>
/// Device responds to an IN instruction /// Device responds to an IN instruction
/// </summary> /// </summary>
bool ReadPort(ushort port, ref int result); bool ReadPort(ushort port, ref int result);
/// <summary> /// <summary>
/// Device responds to an OUT instruction /// Device responds to an OUT instruction
/// </summary> /// </summary>
bool WritePort(ushort port, int result); bool WritePort(ushort port, int result);
} }
} }

View File

@ -1,180 +1,180 @@
 
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// Used for the sector CHRN structure /// Used for the sector CHRN structure
/// </summary> /// </summary>
public class CHRN public class CHRN
{ {
/// <summary> /// <summary>
/// Track /// Track
/// </summary> /// </summary>
public byte C { get; set; } public byte C { get; set; }
/// <summary> /// <summary>
/// Side /// Side
/// </summary> /// </summary>
public byte H { get; set; } public byte H { get; set; }
/// <summary> /// <summary>
/// Sector ID /// Sector ID
/// </summary> /// </summary>
public byte R { get; set; } public byte R { get; set; }
/// <summary> /// <summary>
/// Sector Size /// Sector Size
/// </summary> /// </summary>
public byte N { get; set; } public byte N { get; set; }
/// <summary> /// <summary>
/// Status register 1 /// Status register 1
/// </summary> /// </summary>
private byte _flag1; private byte _flag1;
public byte Flag1 public byte Flag1
{ {
get { return _flag1; } get { return _flag1; }
set { _flag1 = value; } set { _flag1 = value; }
} }
/// <summary> /// <summary>
/// Status register 2 /// Status register 2
/// </summary> /// </summary>
private byte _flag2; private byte _flag2;
public byte Flag2 public byte Flag2
{ {
get { return _flag2; } get { return _flag2; }
set { _flag2 = value; } set { _flag2 = value; }
} }
/// <summary> /// <summary>
/// Used to store the last transmitted/received data bytes /// Used to store the last transmitted/received data bytes
/// </summary> /// </summary>
public byte[] DataBytes { get; set; } public byte[] DataBytes { get; set; }
/// <summary> /// <summary>
/// ID for the read/write data command /// ID for the read/write data command
/// </summary> /// </summary>
public int DataID { get; set; } public int DataID { get; set; }
#region Helper Methods #region Helper Methods
/// <summary> /// <summary>
/// Missing Address Mark (Sector_ID or DAM not found) /// Missing Address Mark (Sector_ID or DAM not found)
/// </summary> /// </summary>
public bool ST1MA public bool ST1MA
{ {
get { return NECUPD765.GetBit(0, _flag1); } get { return NECUPD765.GetBit(0, _flag1); }
set set
{ {
if (value) { NECUPD765.SetBit(0, ref _flag1); } if (value) { NECUPD765.SetBit(0, ref _flag1); }
else { NECUPD765.UnSetBit(0, ref _flag1); } else { NECUPD765.UnSetBit(0, ref _flag1); }
} }
} }
/// <summary> /// <summary>
/// No Data (Sector_ID not found, CRC fail in ID_field) /// No Data (Sector_ID not found, CRC fail in ID_field)
/// </summary> /// </summary>
public bool ST1ND public bool ST1ND
{ {
get { return NECUPD765.GetBit(2, _flag1); } get { return NECUPD765.GetBit(2, _flag1); }
set set
{ {
if (value) { NECUPD765.SetBit(2, ref _flag1); } if (value) { NECUPD765.SetBit(2, ref _flag1); }
else { NECUPD765.UnSetBit(2, ref _flag1); } else { NECUPD765.UnSetBit(2, ref _flag1); }
} }
} }
/// <summary> /// <summary>
/// Data Error (CRC-fail in ID- or Data-Field) /// Data Error (CRC-fail in ID- or Data-Field)
/// </summary> /// </summary>
public bool ST1DE public bool ST1DE
{ {
get { return NECUPD765.GetBit(5, _flag1); } get { return NECUPD765.GetBit(5, _flag1); }
set set
{ {
if (value) { NECUPD765.SetBit(5, ref _flag1); } if (value) { NECUPD765.SetBit(5, ref _flag1); }
else { NECUPD765.UnSetBit(5, ref _flag1); } else { NECUPD765.UnSetBit(5, ref _flag1); }
} }
} }
/// <summary> /// <summary>
/// End of Track (set past most read/write commands) (see IC) /// End of Track (set past most read/write commands) (see IC)
/// </summary> /// </summary>
public bool ST1EN public bool ST1EN
{ {
get { return NECUPD765.GetBit(7, _flag1); } get { return NECUPD765.GetBit(7, _flag1); }
set set
{ {
if (value) { NECUPD765.SetBit(7, ref _flag1); } if (value) { NECUPD765.SetBit(7, ref _flag1); }
else { NECUPD765.UnSetBit(7, ref _flag1); } else { NECUPD765.UnSetBit(7, ref _flag1); }
} }
} }
/// <summary> /// <summary>
/// Missing Address Mark in Data Field (DAM not found) /// Missing Address Mark in Data Field (DAM not found)
/// </summary> /// </summary>
public bool ST2MD public bool ST2MD
{ {
get { return NECUPD765.GetBit(0, _flag2); } get { return NECUPD765.GetBit(0, _flag2); }
set set
{ {
if (value) { NECUPD765.SetBit(0, ref _flag2); } if (value) { NECUPD765.SetBit(0, ref _flag2); }
else { NECUPD765.UnSetBit(0, ref _flag2); } else { NECUPD765.UnSetBit(0, ref _flag2); }
} }
} }
/// <summary> /// <summary>
/// Bad Cylinder (read/programmed track-ID different and read-ID = FF) /// Bad Cylinder (read/programmed track-ID different and read-ID = FF)
/// </summary> /// </summary>
public bool ST2BC public bool ST2BC
{ {
get { return NECUPD765.GetBit(1, _flag2); } get { return NECUPD765.GetBit(1, _flag2); }
set set
{ {
if (value) { NECUPD765.SetBit(1, ref _flag2); } if (value) { NECUPD765.SetBit(1, ref _flag2); }
else { NECUPD765.UnSetBit(1, ref _flag2); } else { NECUPD765.UnSetBit(1, ref _flag2); }
} }
} }
/// <summary> /// <summary>
/// Wrong Cylinder (read/programmed track-ID different) (see b1) /// Wrong Cylinder (read/programmed track-ID different) (see b1)
/// </summary> /// </summary>
public bool ST2WC public bool ST2WC
{ {
get { return NECUPD765.GetBit(4, _flag2); } get { return NECUPD765.GetBit(4, _flag2); }
set set
{ {
if (value) { NECUPD765.SetBit(4, ref _flag2); } if (value) { NECUPD765.SetBit(4, ref _flag2); }
else { NECUPD765.UnSetBit(4, ref _flag2); } else { NECUPD765.UnSetBit(4, ref _flag2); }
} }
} }
/// <summary> /// <summary>
/// Data Error in Data Field (CRC-fail in data-field) /// Data Error in Data Field (CRC-fail in data-field)
/// </summary> /// </summary>
public bool ST2DD public bool ST2DD
{ {
get { return NECUPD765.GetBit(5, _flag2); } get { return NECUPD765.GetBit(5, _flag2); }
set set
{ {
if (value) { NECUPD765.SetBit(5, ref _flag2); } if (value) { NECUPD765.SetBit(5, ref _flag2); }
else { NECUPD765.UnSetBit(5, ref _flag2); } else { NECUPD765.UnSetBit(5, ref _flag2); }
} }
} }
/// <summary> /// <summary>
/// Control Mark (read/scan command found sector with deleted DAM) /// Control Mark (read/scan command found sector with deleted DAM)
/// </summary> /// </summary>
public bool ST2CM public bool ST2CM
{ {
get { return NECUPD765.GetBit(6, _flag2); } get { return NECUPD765.GetBit(6, _flag2); }
set set
{ {
if (value) { NECUPD765.SetBit(6, ref _flag2); } if (value) { NECUPD765.SetBit(6, ref _flag2); }
else { NECUPD765.UnSetBit(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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// The NEC floppy disk controller (and floppy drive) found in the +3 /// The NEC floppy disk controller (and floppy drive) found in the +3
/// </summary> /// </summary>
#region Attribution #region Attribution
/* /*
Implementation based on the information contained here: Implementation based on the information contained here:
http://www.cpcwiki.eu/index.php/765_FDC http://www.cpcwiki.eu/index.php/765_FDC
and here: and here:
http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf
*/ */
#endregion #endregion
public partial class NECUPD765 public partial class NECUPD765
{ {
#region Devices #region Devices
/// <summary> /// <summary>
/// The emulated spectrum machine /// The emulated spectrum machine
/// </summary> /// </summary>
private SpectrumBase _machine; private SpectrumBase _machine;
#endregion #endregion
#region Construction & Initialization #region Construction & Initialization
/// <summary> /// <summary>
/// Main constructor /// Main constructor
/// </summary> /// </summary>
public NECUPD765() public NECUPD765()
{ {
InitCommandList(); InitCommandList();
} }
/// <summary> /// <summary>
/// Initialization routine /// Initialization routine
/// </summary> /// </summary>
public void Init(SpectrumBase machine) public void Init(SpectrumBase machine)
{ {
_machine = machine; _machine = machine;
FDD_Init(); FDD_Init();
TimingInit(); TimingInit();
Reset(); Reset();
} }
/// <summary>
/// Resets the FDC
/// </summary>
public void Reset()
{
// setup main status
StatusMain = 0;
Status0 = 0; /// <summary>
Status1 = 0; /// Resets the FDC
Status2 = 0; /// </summary>
Status3 = 0; 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; SetPhase_Idle();
//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) //FDC_FLAG_RQM = true;
{ //ActiveDirection = CommandDirection.IN;
//d.SeekingTrack = d.CurrentTrack; SRT = 6;
////d.SeekCounter = 0; HUT = 16;
//d.FLAG_SEEK_INTERRUPT = false; HLT = 2;
//d.IntStatus = 0; HLT_Counter = 0;
//d.SeekState = SeekSubState.Idle; HUT_Counter = 0;
//d.SeekIntState = SeekIntStatus.Normal; 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() /// <summary>
{ /// Setup the command structure
CommandList = new List<Command> /// Each command represents one of the internal UPD765 commands
{ /// </summary>
private void InitCommandList()
{
CommandList = new List<Command>
{
// read data // read data
new Command { CommandDelegate = UPD_ReadData, CommandCode = 0x06, MT = true, MF = true, SK = true, IsRead = true, 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 // read id
new Command { CommandDelegate = UPD_ReadID, CommandCode = 0x0a, MF = true, IsRead = true, 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 // specify
new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03, new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03,
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
// read diagnostic // read diagnostic
new Command { CommandDelegate = UPD_ReadDiagnostic, CommandCode = 0x02, MF = true, SK = true, IsRead = true, 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 // scan equal
new Command { CommandDelegate = UPD_ScanEqual, CommandCode = 0x11, MT = true, MF = true, SK = true, IsRead = true, 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 // scan high or equal
new Command { CommandDelegate = UPD_ScanHighOrEqual, CommandCode = 0x1d, MT = true, MF = true, SK = true, IsRead = true, 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 // scan low or equal
new Command { CommandDelegate = UPD_ScanLowOrEqual, CommandCode = 0x19, MT = true, MF = true, SK = true, IsRead = true, 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 // read deleted data
new Command { CommandDelegate = UPD_ReadDeletedData, CommandCode = 0x0c, MT = true, MF = true, SK = true, IsRead = true, 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 // write data
new Command { CommandDelegate = UPD_WriteData, CommandCode = 0x05, MT = true, MF = true, IsWrite = true, 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 // write id
new Command { CommandDelegate = UPD_WriteID, CommandCode = 0x0d, MF = true, IsWrite = true, 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 // write deleted data
new Command { CommandDelegate = UPD_WriteDeletedData, CommandCode = 0x09, MT = true, MF = true, IsWrite = true, 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 // seek
new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f, new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f,
Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 },
// recalibrate (seek track00) // recalibrate (seek track00)
new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07, new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 }, Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 },
// sense interrupt status // sense interrupt status
new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08, new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 }, Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 },
// sense drive status // sense drive status
new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04, new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04,
Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 }, Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 },
// version // version
new Command { CommandDelegate = UPD_Version, CommandCode = 0x10, new Command { CommandDelegate = UPD_Version, CommandCode = 0x10,
Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 },
// invalid // invalid
new Command { CommandDelegate = UPD_Invalid, CommandCode = 0x00, 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) public void SyncState(Serializer ser)
{ {
ser.BeginSection("NEC-UPD765"); ser.BeginSection("NEC-UPD765");
#region FDD #region FDD
ser.Sync(nameof(FDD_FLAG_MOTOR), ref FDD_FLAG_MOTOR);
for (int i = 0; i < 4; i++) ser.Sync(nameof(FDD_FLAG_MOTOR), ref FDD_FLAG_MOTOR);
{
ser.BeginSection("HITDrive_" + i);
DriveStates[i].SyncState(ser);
ser.EndSection();
}
ser.Sync(nameof(DiskDriveIndex), ref _diskDriveIndex); for (int i = 0; i < 4; i++)
// set active drive {
DiskDriveIndex = _diskDriveIndex; 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); #region Registers
ser.Sync("_Reg0", ref Status0);
ser.Sync("_Reg1", ref Status1);
ser.Sync("_Reg2", ref Status2);
ser.Sync("_Reg3", ref Status3);
#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); #region Controller state
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;
ActiveCommandParams.SyncState(ser); ser.Sync(nameof(DriveLight), ref DriveLight);
ser.SyncEnum(nameof(ActivePhase), ref ActivePhase);
ser.Sync(nameof(IndexPulseCounter), ref IndexPulseCounter); //ser.SyncEnum(nameof(ActiveDirection), ref ActiveDirection);
//ser.SyncEnum(nameof(_activeStatus), ref _activeStatus); ser.SyncEnum(nameof(ActiveInterrupt), ref ActiveInterrupt);
//ser.SyncEnum(nameof(_statusRaised), ref _statusRaised); 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(_cmdIndex), ref _cmdIndex);
ser.Sync(nameof(CMD_FLAG_MF), ref CMD_FLAG_MF); // resync the ActiveCommand
ser.Sync(nameof(CMD_FLAG_SK), ref CMD_FLAG_SK); CMDIndex = _cmdIndex;
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(SectorDelayCounter), ref SectorDelayCounter); ActiveCommandParams.SyncState(ser);
ser.Sync(nameof(SectorID), ref SectorID);
#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(SectorDelayCounter), ref SectorDelayCounter);
ser.Sync(nameof(StatusDelay), ref StatusDelay); ser.Sync(nameof(SectorID), ref SectorID);
ser.Sync(nameof(TickCounter), ref TickCounter);
ser.Sync(nameof(DriveCycleCounter), ref DriveCycleCounter);
#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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// Cursor joystick /// Cursor joystick
/// Maps to a combination of 0xf7fe and 0xeffe /// Maps to a combination of 0xf7fe and 0xeffe
/// </summary> /// </summary>
public class CursorJoystick : IJoystick public class CursorJoystick : IJoystick
{ {
//private int _joyLine; //private int _joyLine;
private SpectrumBase _machine; private SpectrumBase _machine;
#region Construction #region Construction
public CursorJoystick(SpectrumBase machine, int playerNumber) public CursorJoystick(SpectrumBase machine, int playerNumber)
{ {
_machine = machine; _machine = machine;
//_joyLine = 0; //_joyLine = 0;
_playerNumber = playerNumber; _playerNumber = playerNumber;
ButtonCollection = new List<string> ButtonCollection = new List<string>
{ {
"P" + _playerNumber + " Left", "P" + _playerNumber + " Left",
"P" + _playerNumber + " Right", "P" + _playerNumber + " Right",
"P" + _playerNumber + " Down", "P" + _playerNumber + " Down",
"P" + _playerNumber + " Up", "P" + _playerNumber + " Up",
"P" + _playerNumber + " Button", "P" + _playerNumber + " Button",
}.ToArray(); }.ToArray();
} }
private List<string> btnLookups = new List<string> private List<string> btnLookups = new List<string>
{ {
"Key 5", // left "Key 5", // left
"Key 8", // right "Key 8", // right
"Key 6", // down "Key 6", // down
"Key 7", // up "Key 7", // up
"Key 0", // fire "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; private int _playerNumber;
public int PlayerNumber public int PlayerNumber
{ {
get { return _playerNumber; } get { return _playerNumber; }
set { _playerNumber = value; } set { _playerNumber = value; }
} }
/// <summary> /// <summary>
/// Sets the joystick line based on key pressed /// Sets the joystick line based on key pressed
/// </summary> /// </summary>
public void SetJoyInput(string key, bool isPressed) public void SetJoyInput(string key, bool isPressed)
{ {
var pos = GetBitPos(key); var pos = GetBitPos(key);
if (isPressed) if (isPressed)
{ {
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true); _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true);
} }
else else
{ {
if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos])) if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]))
{ {
// key is already pressed elswhere - leave it as is // key is already pressed elswhere - leave it as is
} }
else else
{ {
// key is safe to unpress // key is safe to unpress
_machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false); _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false);
} }
} }
} }
/// <summary> /// <summary>
/// Gets the state of a particular joystick binding /// Gets the state of a particular joystick binding
/// </summary> /// </summary>
public bool GetJoyInput(string key) public bool GetJoyInput(string key)
{ {
var pos = GetBitPos(key); var pos = GetBitPos(key);
if (_machine == null) if (_machine == null)
return false; return false;
var l = _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]); var l = _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]);
return l; return l;
} }
#endregion
/// <summary> #endregion
/// Gets the bit position of a particular joystick binding from the matrix
/// </summary> /// <summary>
public int GetBitPos(string key) /// Gets the bit position of a particular joystick binding from the matrix
{ /// </summary>
int index = Array.IndexOf(ButtonCollection, key); public int GetBitPos(string key)
return index; {
} int index = Array.IndexOf(ButtonCollection, key);
} return index;
}
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,48 +4,48 @@ using BizHawk.Emulation.Cores.Sound;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// 128K Constructor /// 128K Constructor
/// </summary> /// </summary>
public partial class Pentagon128 : SpectrumBase public partial class Pentagon128 : SpectrumBase
{ {
#region Construction #region Construction
/// <summary> /// <summary>
/// Main constructor /// Main constructor
/// </summary> /// </summary>
public Pentagon128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks) public Pentagon128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{ {
Spectrum = spectrum; Spectrum = spectrum;
CPU = cpu; CPU = cpu;
CPUMon = new CPUMonitor(this); CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.Pentagon128; CPUMon.machineType = MachineType.Pentagon128;
ROMPaged = 0; ROMPaged = 0;
SHADOWPaged = false; SHADOWPaged = false;
RAMPaged = 0; RAMPaged = 0;
PagingDisabled = false; PagingDisabled = false;
ULADevice = new ScreenPentagon128(this); ULADevice = new ScreenPentagon128(this);
BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer"); 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 = 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 = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this); 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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// The abstract class that all emulated models will inherit from /// The abstract class that all emulated models will inherit from
/// * Main properties / fields / contruction* /// * Main properties / fields / contruction*
/// </summary> /// </summary>
public abstract partial class SpectrumBase public abstract partial class SpectrumBase
{ {
#region Devices #region Devices
/// <summary> /// <summary>
/// The calling ZXSpectrum class (piped in via constructor) /// The calling ZXSpectrum class (piped in via constructor)
/// </summary> /// </summary>
public ZXSpectrum Spectrum { get; set; } public ZXSpectrum Spectrum { get; set; }
/// <summary> /// <summary>
/// Reference to the instantiated Z80 cpu (piped in via constructor) /// Reference to the instantiated Z80 cpu (piped in via constructor)
/// </summary> /// </summary>
public Z80A CPU { get; set; } public Z80A CPU { get; set; }
/// <summary> /// <summary>
/// ROM and extended info /// ROM and extended info
/// </summary> /// </summary>
public RomData RomData { get; set; } public RomData RomData { get; set; }
/// <summary> /// <summary>
/// The emulated ULA device /// The emulated ULA device
/// </summary> /// </summary>
//public ULABase ULADevice { get; set; } //public ULABase ULADevice { get; set; }
public ULA ULADevice { get; set; } public ULA ULADevice { get; set; }
/// <summary> /// <summary>
/// Monitors CPU cycles /// Monitors CPU cycles
/// </summary> /// </summary>
public CPUMonitor CPUMon { get; set; } public CPUMonitor CPUMon { get; set; }
/// <summary> /// <summary>
/// The spectrum buzzer/beeper /// The spectrum buzzer/beeper
/// </summary> /// </summary>
public OneBitBeeper BuzzerDevice { get; set; } public OneBitBeeper BuzzerDevice { get; set; }
/// <summary> /// <summary>
/// A second beeper for the tape /// A second beeper for the tape
/// </summary> /// </summary>
public OneBitBeeper TapeBuzzer { get; set; } public OneBitBeeper TapeBuzzer { get; set; }
/// <summary> /// <summary>
/// Device representing the AY-3-8912 chip found in the 128k and up spectrums /// Device representing the AY-3-8912 chip found in the 128k and up spectrums
/// </summary> /// </summary>
public IPSG AYDevice { get; set; } public IPSG AYDevice { get; set; }
/// <summary> /// <summary>
/// The spectrum keyboard /// The spectrum keyboard
/// </summary> /// </summary>
public virtual IKeyboard KeyboardDevice { get; set; } public virtual IKeyboard KeyboardDevice { get; set; }
/// <summary> /// <summary>
/// The spectrum datacorder device /// The spectrum datacorder device
/// </summary> /// </summary>
public virtual DatacorderDevice TapeDevice { get; set; } public virtual DatacorderDevice TapeDevice { get; set; }
/// <summary> /// <summary>
/// The +3 built-in disk drive /// The +3 built-in disk drive
/// </summary> /// </summary>
public virtual NECUPD765 UPDDiskDevice { get; set; } public virtual NECUPD765 UPDDiskDevice { get; set; }
/// <summary> /// <summary>
/// Holds the currently selected joysticks /// Holds the currently selected joysticks
/// </summary> /// </summary>
public virtual IJoystick[] JoystickCollection { get; set; } public virtual IJoystick[] JoystickCollection { get; set; }
/// <summary> /// <summary>
/// +3/2a printer port strobe /// +3/2a printer port strobe
/// </summary> /// </summary>
protected bool PrinterPortStrobe; protected bool PrinterPortStrobe;
#endregion #endregion
#region Emulator State #region Emulator State
/// <summary> /// <summary>
/// Signs whether the frame has ended /// Signs whether the frame has ended
/// </summary> /// </summary>
public bool FrameCompleted; public bool FrameCompleted;
/// <summary> /// <summary>
/// Overflow from the previous frame (in Z80 cycles) /// Overflow from the previous frame (in Z80 cycles)
/// </summary> /// </summary>
public int OverFlow; public int OverFlow;
/// <summary> /// <summary>
/// The total number of frames rendered /// The total number of frames rendered
/// </summary> /// </summary>
public int FrameCount; public int FrameCount;
/// <summary> /// <summary>
/// The current cycle (T-State) that we are at in the frame /// The current cycle (T-State) that we are at in the frame
/// </summary> /// </summary>
public long _frameCycles; public long _frameCycles;
/// <summary> /// <summary>
/// Stores where we are in the frame after each CPU cycle /// Stores where we are in the frame after each CPU cycle
/// </summary> /// </summary>
public long LastFrameStartCPUTick; public long LastFrameStartCPUTick;
/// <summary> /// <summary>
/// Gets the current frame cycle according to the CPU tick count /// Gets the current frame cycle according to the CPU tick count
/// </summary> /// </summary>
public virtual long CurrentFrameCycle => CPU.TotalExecutedCycles - LastFrameStartCPUTick; public virtual long CurrentFrameCycle => CPU.TotalExecutedCycles - LastFrameStartCPUTick;
/// <summary> /// <summary>
/// Non-Deterministic bools /// Non-Deterministic bools
/// </summary> /// </summary>
public bool _render; public bool _render;
public bool _renderSound; public bool _renderSound;
#endregion #endregion
#region Constants #region Constants
/// <summary> /// <summary>
/// Mask constants & misc /// Mask constants & misc
/// </summary> /// </summary>
protected const int BORDER_BIT = 0x07; protected const int BORDER_BIT = 0x07;
protected const int EAR_BIT = 0x10; protected const int EAR_BIT = 0x10;
protected const int MIC_BIT = 0x08; protected const int MIC_BIT = 0x08;
protected const int TAPE_BIT = 0x40; protected const int TAPE_BIT = 0x40;
protected const int AY_SAMPLE_RATE = 16; protected const int AY_SAMPLE_RATE = 16;
#endregion #endregion
#region Emulation Loop #region Emulation Loop
/// <summary> /// <summary>
/// Executes a single frame /// Executes a single frame
/// </summary> /// </summary>
public virtual void ExecuteFrame(bool render, bool renderSound) public virtual void ExecuteFrame(bool render, bool renderSound)
{ {
ULADevice.FrameEnd = false; ULADevice.FrameEnd = false;
ULADevice.ULACycleCounter = CurrentFrameCycle; ULADevice.ULACycleCounter = CurrentFrameCycle;
InputRead = false; InputRead = false;
_render = render; _render = render;
_renderSound = renderSound; _renderSound = renderSound;
FrameCompleted = false; FrameCompleted = false;
//if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) //if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
//TapeDevice.StartFrame(); //TapeDevice.StartFrame();
if (_renderSound) if (_renderSound)
{ {
if (AYDevice != null) if (AYDevice != null)
AYDevice.StartFrame(); AYDevice.StartFrame();
} }
PollInput(); PollInput();
for (;;) for (; ; )
{ {
// run the CPU Monitor cycle // run the CPU Monitor cycle
CPUMon.ExecuteCycle(); CPUMon.ExecuteCycle();
// clock the beepers // clock the beepers
TapeBuzzer.SetClock((int)CurrentFrameCycle); TapeBuzzer.SetClock((int)CurrentFrameCycle);
@ -171,229 +171,229 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// cycle the tape device // cycle the tape device
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.TapeCycle(); TapeDevice.TapeCycle();
// has frame end been reached? // has frame end been reached?
if (ULADevice.FrameEnd) if (ULADevice.FrameEnd)
break; break;
} }
OverFlow = (int)CurrentFrameCycle - ULADevice.FrameLength; OverFlow = (int)CurrentFrameCycle - ULADevice.FrameLength;
// we have reached the end of a frame // we have reached the end of a frame
LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow; LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow;
ULADevice.LastTState = 0; ULADevice.LastTState = 0;
if (AYDevice != null) if (AYDevice != null)
AYDevice.EndFrame(); AYDevice.EndFrame();
FrameCount++; FrameCount++;
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.EndFrame();
FrameCompleted = true; if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.EndFrame();
// is this a lag frame? FrameCompleted = true;
Spectrum.IsLagFrame = !InputRead;
// FDC debug // is this a lag frame?
if (UPDDiskDevice != null && UPDDiskDevice.writeDebug) Spectrum.IsLagFrame = !InputRead;
{
// 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);
}
}
}
#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> #region Reset Functions
/// Hard reset of the emulated machine
/// </summary>
public virtual void HardReset()
{
//ULADevice.ResetInterrupt();
ROMPaged = 0;
SpecialPagingMode = false;
RAMPaged = 0;
CPU.RegPC = 0;
Spectrum.SetCpuRegister("SP", 0xFFFF); /// <summary>
Spectrum.SetCpuRegister("IY", 0xFFFF); /// Hard reset of the emulated machine
Spectrum.SetCpuRegister("IX", 0xFFFF); /// </summary>
Spectrum.SetCpuRegister("AF", 0xFFFF); public virtual void HardReset()
Spectrum.SetCpuRegister("BC", 0xFFFF); {
Spectrum.SetCpuRegister("DE", 0xFFFF); //ULADevice.ResetInterrupt();
Spectrum.SetCpuRegister("HL", 0xFFFF); ROMPaged = 0;
Spectrum.SetCpuRegister("SP", 0xFFFF); SpecialPagingMode = false;
Spectrum.SetCpuRegister("Shadow AF", 0xFFFF); RAMPaged = 0;
Spectrum.SetCpuRegister("Shadow BC", 0xFFFF); CPU.RegPC = 0;
Spectrum.SetCpuRegister("Shadow DE", 0xFFFF);
Spectrum.SetCpuRegister("Shadow HL", 0xFFFF);
CPU.Regs[CPU.I] = 0; Spectrum.SetCpuRegister("SP", 0xFFFF);
CPU.Regs[CPU.R] = 0; 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(); CPU.Regs[CPU.I] = 0;
if (AYDevice != null) CPU.Regs[CPU.R] = 0;
AYDevice.Reset();
byte[][] rams = new byte[][] TapeDevice.Reset();
{ if (AYDevice != null)
RAM0, AYDevice.Reset();
RAM1,
RAM2,
RAM3,
RAM4,
RAM5,
RAM6,
RAM7
};
foreach (var r in rams) byte[][] rams = new byte[][]
{ {
for (int i = 0; i < r.Length; i++) RAM0,
{ RAM1,
r[i] = 0x00; RAM2,
} RAM3,
} RAM4,
} RAM5,
RAM6,
RAM7
};
/// <summary> foreach (var r in rams)
/// Soft reset of the emulated machine {
/// </summary> for (int i = 0; i < r.Length; i++)
public virtual void SoftReset() {
{ r[i] = 0x00;
//ULADevice.ResetInterrupt(); }
ROMPaged = 0; }
SpecialPagingMode = false; }
RAMPaged = 0;
CPU.RegPC = 0;
Spectrum.SetCpuRegister("SP", 0xFFFF); /// <summary>
Spectrum.SetCpuRegister("IY", 0xFFFF); /// Soft reset of the emulated machine
Spectrum.SetCpuRegister("IX", 0xFFFF); /// </summary>
Spectrum.SetCpuRegister("AF", 0xFFFF); public virtual void SoftReset()
Spectrum.SetCpuRegister("BC", 0xFFFF); {
Spectrum.SetCpuRegister("DE", 0xFFFF); //ULADevice.ResetInterrupt();
Spectrum.SetCpuRegister("HL", 0xFFFF); ROMPaged = 0;
Spectrum.SetCpuRegister("SP", 0xFFFF); SpecialPagingMode = false;
Spectrum.SetCpuRegister("Shadow AF", 0xFFFF); RAMPaged = 0;
Spectrum.SetCpuRegister("Shadow BC", 0xFFFF); CPU.RegPC = 0;
Spectrum.SetCpuRegister("Shadow DE", 0xFFFF);
Spectrum.SetCpuRegister("Shadow HL", 0xFFFF);
CPU.Regs[CPU.I] = 0; Spectrum.SetCpuRegister("SP", 0xFFFF);
CPU.Regs[CPU.R] = 0; 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(); CPU.Regs[CPU.I] = 0;
if (AYDevice != null) CPU.Regs[CPU.R] = 0;
AYDevice.Reset();
byte[][] rams = new byte[][] TapeDevice.Reset();
{ if (AYDevice != null)
RAM0, AYDevice.Reset();
RAM1,
RAM2,
RAM3,
RAM4,
RAM5,
RAM6,
RAM7
};
foreach (var r in rams) byte[][] rams = new byte[][]
{ {
for (int i = 0; i < r.Length; i++) RAM0,
{ RAM1,
r[i] = 0x00; 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) #region IStatable
{
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);
KeyboardDevice.SyncState(ser); public void SyncState(Serializer ser)
BuzzerDevice.SyncState(ser); {
TapeBuzzer.SyncState(ser); ser.BeginSection("ZXMachine");
ULADevice.SyncState(ser); ser.Sync(nameof(FrameCompleted), ref FrameCompleted);
CPUMon.SyncState(ser); 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) KeyboardDevice.SyncState(ser);
{ BuzzerDevice.SyncState(ser);
AYDevice.SyncState(ser); TapeBuzzer.SyncState(ser);
((AY38912)AYDevice as AY38912).PanningConfiguration = Spectrum.Settings.AYPanConfig; ULADevice.SyncState(ser);
} CPUMon.SyncState(ser);
ser.Sync(nameof(tapeMediaIndex), ref tapeMediaIndex); if (AYDevice != null)
if (ser.IsReader) {
{ AYDevice.SyncState(ser);
IsLoadState = true; ((AY38912)AYDevice as AY38912).PanningConfiguration = Spectrum.Settings.AYPanConfig;
TapeMediaIndex = tapeMediaIndex; }
IsLoadState = false;
}
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) TapeDevice.SyncState(ser);
{
UPDDiskDevice.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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// 128K/+2 ULA /// 128K/+2 ULA
/// </summary> /// </summary>
class Screen128 : ULA class Screen128 : ULA
{ {
#region Construction #region Construction
public Screen128(SpectrumBase machine) public Screen128(SpectrumBase machine)
: base(machine) : base(machine)
{ {
// interrupt // interrupt
InterruptStartTime = 3; InterruptStartTime = 3;
InterruptLength = 36; InterruptLength = 36;
// offsets // offsets
RenderTableOffset = 58; RenderTableOffset = 58;
ContentionOffset = 6; ContentionOffset = 6;
FloatingBusOffset = 1; FloatingBusOffset = 1;
// timing // timing
ClockSpeed = 3546900; ClockSpeed = 3546900;
FrameCycleLength = 70908; FrameCycleLength = 70908;
ScanlineTime = 228; ScanlineTime = 228;
BorderLeftTime = 24; BorderLeftTime = 24;
BorderRightTime = 24; BorderRightTime = 24;
FirstPaperLine = 63; FirstPaperLine = 63;
FirstPaperTState = 64; FirstPaperTState = 64;
// screen layout // screen layout
Border4T = true; Border4T = true;
Border4TStage = 2; Border4TStage = 2;
ScreenWidth = 256; ScreenWidth = 256;
ScreenHeight = 192; ScreenHeight = 192;
BorderTopHeight = 48; // 55; // 48; BorderTopHeight = 48; // 55; // 48;
BorderBottomHeight = 48; // 56; BorderBottomHeight = 48; // 56;
BorderLeftWidth = 48; BorderLeftWidth = 48;
BorderRightWidth = 48; BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
RenderingTable = new RenderTable(this, RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum128); MachineType.ZXSpectrum128);
SetupScreenSize(); SetupScreenSize();
} }
#endregion #endregion
} }
} }

View File

@ -4,48 +4,48 @@ using BizHawk.Emulation.Cores.Sound;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// 128K Constructor /// 128K Constructor
/// </summary> /// </summary>
public partial class ZX128 : SpectrumBase public partial class ZX128 : SpectrumBase
{ {
#region Construction #region Construction
/// <summary> /// <summary>
/// Main constructor /// Main constructor
/// </summary> /// </summary>
public ZX128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks) public ZX128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{ {
Spectrum = spectrum; Spectrum = spectrum;
CPU = cpu; CPU = cpu;
CPUMon = new CPUMonitor(this); CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128; CPUMon.machineType = MachineType.ZXSpectrum128;
ROMPaged = 0; ROMPaged = 0;
SHADOWPaged = false; SHADOWPaged = false;
RAMPaged = 0; RAMPaged = 0;
PagingDisabled = false; PagingDisabled = false;
ULADevice = new Screen128(this); ULADevice = new Screen128(this);
BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer"); 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 = 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 = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this); 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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// The +2 is almost identical to the 128k from an emulation point of view /// The +2 is almost identical to the 128k from an emulation point of view
/// There are just a few small changes in the ROMs /// There are just a few small changes in the ROMs
/// </summary> /// </summary>
public partial class ZX128Plus2 : ZX128 public partial class ZX128Plus2 : ZX128
{ {
#region Construction #region Construction
/// <summary> /// <summary>
/// Main constructor /// Main constructor
/// </summary> /// </summary>
public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks) public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
: base(spectrum, cpu, borderType, files, joysticks) : base(spectrum, cpu, borderType, files, joysticks)
{ {
}
#endregion }
}
#endregion
}
} }

View File

@ -1,50 +1,50 @@
 
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// +2A/+3 ULA /// +2A/+3 ULA
/// </summary> /// </summary>
class Screen128Plus2a : ULA class Screen128Plus2a : ULA
{ {
#region Construction #region Construction
public Screen128Plus2a(SpectrumBase machine) public Screen128Plus2a(SpectrumBase machine)
: base(machine) : base(machine)
{ {
// interrupt // interrupt
InterruptStartTime = 0; InterruptStartTime = 0;
InterruptLength = 32; InterruptLength = 32;
// offsets // offsets
RenderTableOffset = 58; RenderTableOffset = 58;
ContentionOffset = 9; ContentionOffset = 9;
FloatingBusOffset = 0; FloatingBusOffset = 0;
// timing // timing
ClockSpeed = 3546900; ClockSpeed = 3546900;
FrameCycleLength = 70908; FrameCycleLength = 70908;
ScanlineTime = 228; ScanlineTime = 228;
BorderLeftTime = 24; BorderLeftTime = 24;
BorderRightTime = 24; BorderRightTime = 24;
FirstPaperLine = 63; FirstPaperLine = 63;
FirstPaperTState = 64; FirstPaperTState = 64;
// screen layout // screen layout
Border4T = true; Border4T = true;
Border4TStage = 2; Border4TStage = 2;
ScreenWidth = 256; ScreenWidth = 256;
ScreenHeight = 192; ScreenHeight = 192;
BorderTopHeight = 48;// 55; BorderTopHeight = 48;// 55;
BorderBottomHeight = 48; // 56; BorderBottomHeight = 48; // 56;
BorderLeftWidth = 48; BorderLeftWidth = 48;
BorderRightWidth = 48; BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
RenderingTable = new RenderTable(this, RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum128Plus2a); 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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// +2A Constructor /// +2A Constructor
/// </summary> /// </summary>
public partial class ZX128Plus2a : SpectrumBase public partial class ZX128Plus2a : SpectrumBase
{ {
#region Construction #region Construction
/// <summary> /// <summary>
/// Main constructor /// Main constructor
/// </summary> /// </summary>
public ZX128Plus2a(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks) public ZX128Plus2a(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{ {
Spectrum = spectrum; Spectrum = spectrum;
CPU = cpu; CPU = cpu;
CPUMon = new CPUMonitor(this); CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128Plus2a; CPUMon.machineType = MachineType.ZXSpectrum128Plus2a;
ROMPaged = 0; ROMPaged = 0;
SHADOWPaged = false; SHADOWPaged = false;
RAMPaged = 0; RAMPaged = 0;
PagingDisabled = false; PagingDisabled = false;
ULADevice = new Screen128Plus2a(this); ULADevice = new Screen128Plus2a(this);
BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer"); 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 = 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 = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this); 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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// +3 Constructor /// +3 Constructor
/// </summary> /// </summary>
public partial class ZX128Plus3 : SpectrumBase public partial class ZX128Plus3 : SpectrumBase
{ {
#region Construction #region Construction
/// <summary> /// <summary>
/// Main constructor /// Main constructor
/// </summary> /// </summary>
public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks) public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
{ {
Spectrum = spectrum; Spectrum = spectrum;
CPU = cpu; CPU = cpu;
CPUMon = new CPUMonitor(this); CPUMon = new CPUMonitor(this);
CPUMon.machineType = MachineType.ZXSpectrum128Plus3; CPUMon.machineType = MachineType.ZXSpectrum128Plus3;
ROMPaged = 0; ROMPaged = 0;
SHADOWPaged = false; SHADOWPaged = false;
RAMPaged = 0; RAMPaged = 0;
PagingDisabled = false; PagingDisabled = false;
ULADevice = new Screen128Plus2a(this); ULADevice = new Screen128Plus2a(this);
BuzzerDevice = new OneBitBeeper(44100, ULADevice.FrameLength, 50, "SystemBuzzer"); 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 = 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 = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape);
TapeDevice.Init(this); TapeDevice.Init(this);
UPDDiskDevice = new NECUPD765(); UPDDiskDevice = new NECUPD765();
UPDDiskDevice.Init(this); 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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// 16K is idential to 48K, just without the top 32KB of RAM /// 16K is idential to 48K, just without the top 32KB of RAM
/// </summary> /// </summary>
public class ZX16 : ZX48 public class ZX16 : ZX48
{ {
#region Construction #region Construction
/// <summary> /// <summary>
/// Main constructor /// Main constructor
/// </summary> /// </summary>
public ZX16(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks) public ZX16(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List<byte[]> files, List<JoystickType> joysticks)
: base(spectrum, cpu, borderType, files, 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 | | Bank 0 |
@ -38,101 +38,101 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
0x0000 +--------+ 0x0000 +--------+
*/ */
/// <summary> /// <summary>
/// Simulates reading from the bus (no contention) /// Simulates reading from the bus (no contention)
/// Paging should be handled here /// Paging should be handled here
/// </summary> /// </summary>
public override byte ReadBus(ushort addr) public override byte ReadBus(ushort addr)
{ {
int divisor = addr / 0x4000; int divisor = addr / 0x4000;
var index = addr % 0x4000; var index = addr % 0x4000;
// paging logic goes here // paging logic goes here
switch (divisor) switch (divisor)
{ {
case 0: case 0:
TestForTapeTraps(addr % 0x4000); TestForTapeTraps(addr % 0x4000);
return ROM0[index]; return ROM0[index];
case 1: return RAM0[index]; case 1: return RAM0[index];
default: default:
// memory does not exist // memory does not exist
return 0xff; return 0xff;
} }
} }
/// <summary> /// <summary>
/// Simulates writing to the bus (no contention) /// Simulates writing to the bus (no contention)
/// Paging should be handled here /// Paging should be handled here
/// </summary> /// </summary>
public override void WriteBus(ushort addr, byte value) public override void WriteBus(ushort addr, byte value)
{ {
int divisor = addr / 0x4000; int divisor = addr / 0x4000;
var index = addr % 0x4000; var index = addr % 0x4000;
// paging logic goes here // paging logic goes here
switch (divisor) switch (divisor)
{ {
case 0: case 0:
// cannot write to ROM // cannot write to ROM
break; break;
case 1: case 1:
//ULADevice.RenderScreen((int)CurrentFrameCycle); //ULADevice.RenderScreen((int)CurrentFrameCycle);
RAM0[index] = value; RAM0[index] = value;
break; break;
} }
} }
/// <summary> /// <summary>
/// Reads a byte of data from a specified memory address /// Reads a byte of data from a specified memory address
/// (with memory contention if appropriate) /// (with memory contention if appropriate)
/// </summary> /// </summary>
public override byte ReadMemory(ushort addr) public override byte ReadMemory(ushort addr)
{ {
var data = ReadBus(addr); var data = ReadBus(addr);
return data; return data;
} }
/// <summary> /// <summary>
/// Returns the ROM/RAM enum that relates to this particular memory read operation /// Returns the ROM/RAM enum that relates to this particular memory read operation
/// </summary> /// </summary>
public override ZXSpectrum.CDLResult ReadCDL(ushort addr) public override ZXSpectrum.CDLResult ReadCDL(ushort addr)
{ {
var res = new ZXSpectrum.CDLResult(); var res = new ZXSpectrum.CDLResult();
int divisor = addr / 0x4000; int divisor = addr / 0x4000;
res.Address = addr % 0x4000; res.Address = addr % 0x4000;
// paging logic goes here // paging logic goes here
switch (divisor) switch (divisor)
{ {
case 0: res.Type = ZXSpectrum.CDLType.ROM0; break; case 0: res.Type = ZXSpectrum.CDLType.ROM0; break;
case 1: res.Type = ZXSpectrum.CDLType.RAM0; break; case 1: res.Type = ZXSpectrum.CDLType.RAM0; break;
} }
return res; return res;
} }
/// <summary> /// <summary>
/// Writes a byte of data to a specified memory address /// Writes a byte of data to a specified memory address
/// (with memory contention if appropriate) /// (with memory contention if appropriate)
/// </summary> /// </summary>
public override void WriteMemory(ushort addr, byte value) public override void WriteMemory(ushort addr, byte value)
{ {
WriteBus(addr, 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);
}
#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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// 48K ULA /// 48K ULA
/// </summary> /// </summary>
class Screen48 : ULA class Screen48 : ULA
{ {
#region Construction #region Construction
public Screen48(SpectrumBase machine) public Screen48(SpectrumBase machine)
: base(machine) : base(machine)
{ {
// interrupt // interrupt
InterruptStartTime = 3; InterruptStartTime = 3;
InterruptLength = 32; InterruptLength = 32;
// offsets // offsets
RenderTableOffset = 56; RenderTableOffset = 56;
ContentionOffset = 6; ContentionOffset = 6;
FloatingBusOffset = 1; FloatingBusOffset = 1;
// timing // timing
ClockSpeed = 3500000; ClockSpeed = 3500000;
FrameCycleLength = 69888; FrameCycleLength = 69888;
ScanlineTime = 224; ScanlineTime = 224;
BorderLeftTime = 24; BorderLeftTime = 24;
BorderRightTime = 24; BorderRightTime = 24;
FirstPaperLine = 64; FirstPaperLine = 64;
FirstPaperTState = 64; FirstPaperTState = 64;
// screen layout // screen layout
Border4T = true; Border4T = true;
Border4TStage = 0; Border4TStage = 0;
ScreenWidth = 256; ScreenWidth = 256;
ScreenHeight = 192; ScreenHeight = 192;
BorderTopHeight = 48;// 55;// 48; BorderTopHeight = 48;// 55;// 48;
BorderBottomHeight = 48;// 56; BorderBottomHeight = 48;// 56;
BorderLeftWidth = 48; BorderLeftWidth = 48;
BorderRightWidth = 48; BorderRightWidth = 48;
ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;
RenderingTable = new RenderTable(this, RenderingTable = new RenderTable(this,
MachineType.ZXSpectrum48); MachineType.ZXSpectrum48);
SetupScreenSize(); SetupScreenSize();
} }
#endregion #endregion
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,205 +8,205 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
public class UDI1_0FloppyDisk : FloppyDisk public class UDI1_0FloppyDisk : FloppyDisk
{ {
/// <summary> /// <summary>
/// The format type /// The format type
/// </summary> /// </summary>
public override DiskType DiskFormatType => DiskType.UDI; public override DiskType DiskFormatType => DiskType.UDI;
/// <summary> /// <summary>
/// Attempts to parse incoming disk data /// Attempts to parse incoming disk data
/// </summary> /// </summary>
/// <returns> /// <returns>
/// TRUE: disk parsed /// TRUE: disk parsed
/// FALSE: unable to parse disk /// FALSE: unable to parse disk
/// </returns> /// </returns>
public override bool ParseDisk(byte[] data) public override bool ParseDisk(byte[] data)
{ {
// look for standard magic string // look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 4); string ident = Encoding.ASCII.GetString(data, 0, 4);
if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!")) if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!"))
{ {
// incorrect format // incorrect format
return false; return false;
} }
if (data[0x08] != 0) if (data[0x08] != 0)
{ {
// wrong version // wrong version
return false; return false;
} }
if (ident == "udi!") if (ident == "udi!")
{ {
// cant handle compression yet // cant handle compression yet
return false; return false;
} }
DiskHeader.DiskIdent = ident; DiskHeader.DiskIdent = ident;
DiskHeader.NumberOfTracks = (byte)(data[0x09] + 1); DiskHeader.NumberOfTracks = (byte)(data[0x09] + 1);
DiskHeader.NumberOfSides = (byte)(data[0x0A] + 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 // ignore extended header
var extHdrSize = MediaConverter.GetInt32(data, 0x0C); var extHdrSize = MediaConverter.GetInt32(data, 0x0C);
int pos = 0x10 + extHdrSize; int pos = 0x10 + extHdrSize;
// process track information // process track information
for (int t = 0; t < DiskHeader.NumberOfTracks; t++) for (int t = 0; t < DiskHeader.NumberOfTracks; t++)
{ {
DiskTracks[t] = new UDIv1Track(); DiskTracks[t] = new UDIv1Track();
DiskTracks[t].TrackNumber = (byte)t; DiskTracks[t].TrackNumber = (byte)t;
DiskTracks[t].SideNumber = 0; DiskTracks[t].SideNumber = 0;
DiskTracks[t].TrackType = data[pos++]; DiskTracks[t].TrackType = data[pos++];
DiskTracks[t].TLEN = MediaConverter.GetWordValue(data, pos); pos += 2; DiskTracks[t].TLEN = MediaConverter.GetWordValue(data, pos); pos += 2;
DiskTracks[t].TrackData = new byte[DiskTracks[t].TLEN + DiskTracks[t].CLEN]; 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); Array.Copy(data, pos, DiskTracks[t].TrackData, 0, DiskTracks[t].TLEN + DiskTracks[t].CLEN);
pos += DiskTracks[t].TLEN + DiskTracks[t].CLEN; pos += DiskTracks[t].TLEN + DiskTracks[t].CLEN;
} }
return true; return true;
} }
/// <summary> /// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays /// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary> /// </summary>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results) public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{ {
// look for standard magic string // look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 4); string ident = Encoding.ASCII.GetString(data, 0, 4);
if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!")) if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!"))
{ {
// incorrect format // incorrect format
return false; return false;
} }
if (data[0x08] != 0) if (data[0x08] != 0)
{ {
// wrong version // wrong version
return false; return false;
} }
if (ident == "udi!") if (ident == "udi!")
{ {
// cant handle compression yet // cant handle compression yet
return false; return false;
} }
byte[] S0 = new byte[data.Length]; byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length]; byte[] S1 = new byte[data.Length];
// header // header
var extHdr = MediaConverter.GetInt32(data, 0x0C); var extHdr = MediaConverter.GetInt32(data, 0x0C);
Array.Copy(data, 0, S0, 0, 0x10 + extHdr); Array.Copy(data, 0, S0, 0, 0x10 + extHdr);
Array.Copy(data, 0, S1, 0, 0x10 + extHdr); Array.Copy(data, 0, S1, 0, 0x10 + extHdr);
// change side number // change side number
S0[0x0A] = 0; S0[0x0A] = 0;
S1[0x0A] = 0; S1[0x0A] = 0;
int pos = 0x10 + extHdr; int pos = 0x10 + extHdr;
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
int s0Pos = pos; int s0Pos = pos;
int s1Pos = pos; int s1Pos = pos;
// process track information // process track information
for (int t = 0; t < (data[0x09] + 1) * 2; t++) for (int t = 0; t < (data[0x09] + 1) * 2; t++)
{ {
var TLEN = MediaConverter.GetWordValue(data, pos + 1); var TLEN = MediaConverter.GetWordValue(data, pos + 1);
var CLEN = TLEN / 8 + (TLEN % 8 / 7) / 8; var CLEN = TLEN / 8 + (TLEN % 8 / 7) / 8;
var blockSize = TLEN + CLEN + 3; var blockSize = TLEN + CLEN + 3;
// 2 sided image: side 0 tracks will all have t as an even number // 2 sided image: side 0 tracks will all have t as an even number
try try
{ {
if (t == 0 || t % 2 == 0) if (t == 0 || t % 2 == 0)
{ {
Array.Copy(data, pos, S0, s0Pos, blockSize); Array.Copy(data, pos, S0, s0Pos, blockSize);
s0Pos += blockSize; s0Pos += blockSize;
} }
else else
{ {
Array.Copy(data, pos, S1, s1Pos, blockSize); Array.Copy(data, pos, S1, s1Pos, blockSize);
s1Pos += blockSize; s1Pos += blockSize;
} }
} }
catch (Exception) 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> pos += blockSize;
/// State serlialization }
/// </summary>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
ser.Sync(nameof(CylinderCount), ref CylinderCount); // skip checkum bytes for now
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); byte[] s0final = new byte[s0Pos];
if (DirtyData) 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 return true;
ser.Sync(nameof( _randomCounter), ref _randomCounter); }
RandomCounter = _randomCounter;
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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// Abtract class that represents all Media Converters /// Abtract class that represents all Media Converters
/// </summary> /// </summary>
public abstract class MediaConverter public abstract class MediaConverter
{ {
/// <summary> /// <summary>
/// The type of serializer /// The type of serializer
/// </summary> /// </summary>
public abstract MediaConverterType FormatType { get; } public abstract MediaConverterType FormatType { get; }
/// <summary> /// <summary>
/// Signs whether this class can be used to read the data format /// Signs whether this class can be used to read the data format
/// </summary> /// </summary>
public virtual bool IsReader public virtual bool IsReader
{ {
get get
{ {
return false; return false;
} }
} }
/// <summary> /// <summary>
/// Signs whether this class can be used to write the data format /// Signs whether this class can be used to write the data format
/// </summary> /// </summary>
public virtual bool IsWriter public virtual bool IsWriter
{ {
get get
{ {
return false; return false;
} }
} }
/// <summary> /// <summary>
/// Serialization method /// Serialization method
/// </summary> /// </summary>
public virtual void Read(byte[] data) public virtual void Read(byte[] data)
{ {
throw new NotImplementedException(this.GetType().ToString() + throw new NotImplementedException(this.GetType().ToString() +
"Read operation is not implemented for this converter"); "Read operation is not implemented for this converter");
} }
/// <summary> /// <summary>
/// DeSerialization method /// DeSerialization method
/// </summary> /// </summary>
public virtual void Write(byte[] data) public virtual void Write(byte[] data)
{ {
throw new NotImplementedException(this.GetType().ToString() + throw new NotImplementedException(this.GetType().ToString() +
"Write operation is not implemented for this converter"); "Write operation is not implemented for this converter");
} }
/// <summary> /// <summary>
/// Serializer does a quick check, returns TRUE if file is detected as this type /// Serializer does a quick check, returns TRUE if file is detected as this type
/// </summary> /// </summary>
public virtual bool CheckType(byte[] data) public virtual bool CheckType(byte[] data)
{ {
throw new NotImplementedException(this.GetType().ToString() + throw new NotImplementedException(this.GetType().ToString() +
"Check type operation is not implemented for this converter"); "Check type operation is not implemented for this converter");
} }
#region Static Tools #region Static Tools
/// <summary> /// <summary>
/// Converts an int32 value into a byte array /// Converts an int32 value into a byte array
/// </summary> /// </summary>
public static byte[] GetBytes(int value) public static byte[] GetBytes(int value)
{ {
byte[] buf = new byte[4]; byte[] buf = new byte[4];
buf[0] = (byte)value; buf[0] = (byte)value;
buf[1] = (byte)(value >> 8); buf[1] = (byte)(value >> 8);
buf[2] = (byte)(value >> 16); buf[2] = (byte)(value >> 16);
buf[3] = (byte)(value >> 24); buf[3] = (byte)(value >> 24);
return buf; return buf;
} }
/// <summary> /// <summary>
/// Returns an int32 from a byte array based on offset /// Returns an int32 from a byte array based on offset
/// </summary> /// </summary>
public static int GetInt32(byte[] buf, int offsetIndex) public static int GetInt32(byte[] buf, int offsetIndex)
{ {
return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24; return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24;
} }
/// <summary> /// <summary>
/// Returns an int32 from a byte array based on offset (in BIG ENDIAN format) /// Returns an int32 from a byte array based on offset (in BIG ENDIAN format)
/// </summary> /// </summary>
public static int GetBEInt32(byte[] buf, int offsetIndex) public static int GetBEInt32(byte[] buf, int offsetIndex)
{ {
byte[] b = new byte[4]; byte[] b = new byte[4];
Array.Copy(buf, offsetIndex, b, 0, 4); Array.Copy(buf, offsetIndex, b, 0, 4);
byte[] buffer = b.Reverse().ToArray(); byte[] buffer = b.Reverse().ToArray();
int pos = 0; int pos = 0;
return buffer[pos++] | buffer[pos++] << 8 | buffer[pos++] << 16 | buffer[pos++] << 24; return buffer[pos++] | buffer[pos++] << 8 | buffer[pos++] << 16 | buffer[pos++] << 24;
} }
/// <summary> /// <summary>
/// Returns an int32 from a byte array based on the length of the byte array (in BIG ENDIAN format) /// Returns an int32 from a byte array based on the length of the byte array (in BIG ENDIAN format)
/// </summary> /// </summary>
public static int GetBEInt32FromByteArray(byte[] buf) public static int GetBEInt32FromByteArray(byte[] buf)
{ {
byte[] b = buf.Reverse().ToArray(); byte[] b = buf.Reverse().ToArray();
if (b.Length == 0) if (b.Length == 0)
return 0; return 0;
int res = b[0]; int res = b[0];
int pos = 1; int pos = 1;
switch (b.Length) switch (b.Length)
{ {
case 1: case 1:
default: default:
return res; return res;
case 2: case 2:
return res | b[pos] << (8 * pos++); return res | b[pos] << (8 * pos++);
case 3: case 3:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++); return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 4: case 4:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++); return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 5: case 5:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++); return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 6: case 6:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++); return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 7: 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++); 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> /// <summary>
/// Returns an int32 from a byte array based on offset /// Returns an int32 from a byte array based on offset
/// </summary> /// </summary>
public static uint GetUInt32(byte[] buf, int offsetIndex) public static uint GetUInt32(byte[] buf, int offsetIndex)
{ {
return (uint)(buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24); return (uint)(buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24);
} }
/// <summary> /// <summary>
/// Returns an uint16 from a byte array based on offset /// Returns an uint16 from a byte array based on offset
/// </summary> /// </summary>
public static ushort GetWordValue(byte[] buf, int offsetIndex) public static ushort GetWordValue(byte[] buf, int offsetIndex)
{ {
return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8); return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8);
} }
/// <summary> /// <summary>
/// Updates a byte array with a uint16 value based on offset /// Updates a byte array with a uint16 value based on offset
/// </summary> /// </summary>
public static void SetWordValue(byte[] buf, int offsetIndex, ushort value) public static void SetWordValue(byte[] buf, int offsetIndex, ushort value)
{ {
buf[offsetIndex] = (byte)value; buf[offsetIndex] = (byte)value;
buf[offsetIndex + 1] = (byte)(value >> 8); buf[offsetIndex + 1] = (byte)(value >> 8);
} }
/// <summary> /// <summary>
/// Takes a PauseInMilliseconds value and returns the value in T-States /// Takes a PauseInMilliseconds value and returns the value in T-States
/// </summary> /// </summary>
public static int TranslatePause(int pauseInMS) public static int TranslatePause(int pauseInMS)
{ {
// t-states per millisecond // t-states per millisecond
var tspms = (69888 * 50) / 1000; var tspms = (69888 * 50) / 1000;
// get value // get value
int res = pauseInMS * tspms; int res = pauseInMS * tspms;
return res; return res;
} }
/// <summary> /// <summary>
/// Decompresses a byte array that is Z-RLE compressed /// Decompresses a byte array that is Z-RLE compressed
/// </summary> /// </summary>
public static void DecompressZRLE(byte[] sourceBuffer, ref byte[] destBuffer) public static void DecompressZRLE(byte[] sourceBuffer, ref byte[] destBuffer)
{ {
MemoryStream stream = new MemoryStream(); MemoryStream stream = new MemoryStream();
stream.Write(sourceBuffer, 0, sourceBuffer.Length); stream.Write(sourceBuffer, 0, sourceBuffer.Length);
stream.Position = 0; stream.Position = 0;
stream.ReadByte(); stream.ReadByte();
stream.ReadByte(); stream.ReadByte();
DeflateStream ds = new DeflateStream(stream, CompressionMode.Decompress, false); DeflateStream ds = new DeflateStream(stream, CompressionMode.Decompress, false);
ds.Read(destBuffer, 0, destBuffer.Length); ds.Read(destBuffer, 0, destBuffer.Length);
} }
public static byte[] SerializeRaw(object obj) public static byte[] SerializeRaw(object obj)
{ {
int rSize = Marshal.SizeOf(obj); int rSize = Marshal.SizeOf(obj);
IntPtr buff = Marshal.AllocHGlobal(rSize); IntPtr buff = Marshal.AllocHGlobal(rSize);
Marshal.StructureToPtr(obj, buff, false); Marshal.StructureToPtr(obj, buff, false);
byte[] rData = new byte[rSize]; byte[] rData = new byte[rSize];
Marshal.Copy(buff, rData, 0, rSize); Marshal.Copy(buff, rData, 0, rSize);
return rData; return rData;
} }
public static T DeserializeRaw<T>(byte[] rData, int pos) public static T DeserializeRaw<T>(byte[] rData, int pos)
{ {
int rSize = Marshal.SizeOf(typeof(T)); int rSize = Marshal.SizeOf(typeof(T));
if (rSize > rData.Length - pos) if (rSize > rData.Length - pos)
throw new Exception(); throw new Exception();
IntPtr buff = Marshal.AllocHGlobal(rSize); IntPtr buff = Marshal.AllocHGlobal(rSize);
Marshal.Copy(rData, pos, buff, rSize); Marshal.Copy(rData, pos, buff, rSize);
T rObj = (T)Marshal.PtrToStructure(buff, typeof(T)); T rObj = (T)Marshal.PtrToStructure(buff, typeof(T));
Marshal.FreeHGlobal(buff); Marshal.FreeHGlobal(buff);
return rObj; return rObj;
} }
#endregion #endregion
} }
} }

View File

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

View File

@ -11,177 +11,177 @@ using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// SZX Methods /// 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 /// Based on the work done by ArjunNair in ZERO spectrum emulator: https://github.com/ArjunNair/Zero-Emulator/blob/master/Ziggy/Peripherals/SZXFile.cs
/// </summary> /// </summary>
public partial class SZX public partial class SZX
{ {
private SpectrumBase _machine; private SpectrumBase _machine;
private Z80A _cpu => _machine.CPU; private Z80A _cpu => _machine.CPU;
private SZX(SpectrumBase machine) private SZX(SpectrumBase machine)
{ {
_machine = machine; _machine = machine;
} }
/// <summary> /// <summary>
/// Exports state information to a byte array in ZX-State format /// Exports state information to a byte array in ZX-State format
/// </summary> /// </summary>
public static byte[] ExportSZX(SpectrumBase machine) public static byte[] ExportSZX(SpectrumBase machine)
{ {
var s = new SZX(machine); var s = new SZX(machine);
byte[] result = null; byte[] result = null;
using (MemoryStream ms = new MemoryStream()) using (MemoryStream ms = new MemoryStream())
{ {
using (BinaryWriter r = new BinaryWriter(ms)) using (BinaryWriter r = new BinaryWriter(ms))
{ {
// temp buffer // temp buffer
byte[] buff; byte[] buff;
// working block // working block
ZXSTBLOCK block = new ZXSTBLOCK(); ZXSTBLOCK block = new ZXSTBLOCK();
// header // header
ZXSTHEADER header = new ZXSTHEADER(); ZXSTHEADER header = new ZXSTHEADER();
header.dwMagic = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("ZXST"), 0); header.dwMagic = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("ZXST"), 0);
header.chMajorVersion = 1; header.chMajorVersion = 1;
header.chMinorVersion = 4; header.chMinorVersion = 4;
header.chFlags = 0; header.chFlags = 0;
switch (s._machine.Spectrum.MachineType) switch (s._machine.Spectrum.MachineType)
{ {
case MachineType.ZXSpectrum16: header.chMachineId = (int)MachineIdentifier.ZXSTMID_16K; break; case MachineType.ZXSpectrum16: header.chMachineId = (int)MachineIdentifier.ZXSTMID_16K; break;
case MachineType.ZXSpectrum48: header.chMachineId = (int)MachineIdentifier.ZXSTMID_48K; break; case MachineType.ZXSpectrum48: header.chMachineId = (int)MachineIdentifier.ZXSTMID_48K; break;
case MachineType.ZXSpectrum128: header.chMachineId = (int)MachineIdentifier.ZXSTMID_128K; break; case MachineType.ZXSpectrum128: header.chMachineId = (int)MachineIdentifier.ZXSTMID_128K; break;
case MachineType.ZXSpectrum128Plus2: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2; break; case MachineType.ZXSpectrum128Plus2: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2; break;
case MachineType.ZXSpectrum128Plus2a: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2A; break; case MachineType.ZXSpectrum128Plus2a: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2A; break;
case MachineType.ZXSpectrum128Plus3: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS3; break; case MachineType.ZXSpectrum128Plus3: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS3; break;
} }
buff = MediaConverter.SerializeRaw(header); buff = MediaConverter.SerializeRaw(header);
r.Write(buff); r.Write(buff);
// ZXSTCREATOR // ZXSTCREATOR
var bStruct = s.GetZXSTCREATOR(); var bStruct = s.GetZXSTCREATOR();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("CRTR"), 0); block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("CRTR"), 0);
block.dwSize = (uint)Marshal.SizeOf(bStruct); block.dwSize = (uint)Marshal.SizeOf(bStruct);
buff = MediaConverter.SerializeRaw(block); buff = MediaConverter.SerializeRaw(block);
r.Write(buff); r.Write(buff);
buff = MediaConverter.SerializeRaw(bStruct); buff = MediaConverter.SerializeRaw(bStruct);
r.Write(buff); r.Write(buff);
// ZXSTZ80REGS // ZXSTZ80REGS
var cStruct = s.GetZXSTZ80REGS(); var cStruct = s.GetZXSTZ80REGS();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("Z80R"), 0); block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("Z80R"), 0);
block.dwSize = (uint)Marshal.SizeOf(cStruct); block.dwSize = (uint)Marshal.SizeOf(cStruct);
buff = MediaConverter.SerializeRaw(block); buff = MediaConverter.SerializeRaw(block);
r.Write(buff); r.Write(buff);
buff = MediaConverter.SerializeRaw(cStruct); buff = MediaConverter.SerializeRaw(cStruct);
r.Write(buff); r.Write(buff);
// ZXSTSPECREGS // ZXSTSPECREGS
var dStruct = s.GetZXSTSPECREGS(); var dStruct = s.GetZXSTSPECREGS();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("SPCR"), 0); block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("SPCR"), 0);
block.dwSize = (uint)Marshal.SizeOf(dStruct); block.dwSize = (uint)Marshal.SizeOf(dStruct);
buff = MediaConverter.SerializeRaw(block); buff = MediaConverter.SerializeRaw(block);
r.Write(buff); r.Write(buff);
buff = MediaConverter.SerializeRaw(dStruct); buff = MediaConverter.SerializeRaw(dStruct);
r.Write(buff); r.Write(buff);
// ZXSTKEYBOARD // ZXSTKEYBOARD
var eStruct = s.GetZXSTKEYBOARD(); var eStruct = s.GetZXSTKEYBOARD();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("KEYB"), 0); block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("KEYB"), 0);
block.dwSize = (uint)Marshal.SizeOf(eStruct); block.dwSize = (uint)Marshal.SizeOf(eStruct);
buff = MediaConverter.SerializeRaw(block); buff = MediaConverter.SerializeRaw(block);
r.Write(buff); r.Write(buff);
buff = MediaConverter.SerializeRaw(eStruct); buff = MediaConverter.SerializeRaw(eStruct);
r.Write(buff); r.Write(buff);
// ZXSTJOYSTICK // ZXSTJOYSTICK
var fStruct = s.GetZXSTJOYSTICK(); var fStruct = s.GetZXSTJOYSTICK();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("JOY\0"), 0); block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("JOY\0"), 0);
block.dwSize = (uint)Marshal.SizeOf(fStruct); block.dwSize = (uint)Marshal.SizeOf(fStruct);
buff = MediaConverter.SerializeRaw(block); buff = MediaConverter.SerializeRaw(block);
r.Write(buff); r.Write(buff);
buff = MediaConverter.SerializeRaw(fStruct); buff = MediaConverter.SerializeRaw(fStruct);
r.Write(buff); 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 // ZXSTAYBLOCK
switch (s._machine.Spectrum.MachineType) if (s._machine.Spectrum.MachineType != MachineType.ZXSpectrum16 && s._machine.Spectrum.MachineType != MachineType.ZXSpectrum48)
{ {
// For 16k Spectrums, only page 5 (0x4000 - 0x7fff) is saved. var gStruct = s.GetZXSTAYBLOCK();
case MachineType.ZXSpectrum16: block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("AY\0\0"), 0);
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0); block.dwSize = (uint)Marshal.SizeOf(gStruct);
var rp16 = s.GetZXSTRAMPAGE(5, s._machine.RAM0); buff = MediaConverter.SerializeRaw(block);
block.dwSize = (uint)Marshal.SizeOf(rp16); r.Write(buff);
buff = MediaConverter.SerializeRaw(block); buff = MediaConverter.SerializeRaw(gStruct);
r.Write(buff); 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); // ZXSTRAMPAGE
var rp48_1 = s.GetZXSTRAMPAGE(5, s._machine.RAM1); switch (s._machine.Spectrum.MachineType)
block.dwSize = (uint)Marshal.SizeOf(rp48_1); {
buff = MediaConverter.SerializeRaw(block); // For 16k Spectrums, only page 5 (0x4000 - 0x7fff) is saved.
r.Write(buff); case MachineType.ZXSpectrum16:
buff = MediaConverter.SerializeRaw(rp48_1); block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
r.Write(buff); 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); block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp48_2 = s.GetZXSTRAMPAGE(5, s._machine.RAM2); var rp48_1 = s.GetZXSTRAMPAGE(5, s._machine.RAM1);
block.dwSize = (uint)Marshal.SizeOf(rp48_2); block.dwSize = (uint)Marshal.SizeOf(rp48_1);
buff = MediaConverter.SerializeRaw(block); buff = MediaConverter.SerializeRaw(block);
r.Write(buff); r.Write(buff);
buff = MediaConverter.SerializeRaw(rp48_2); buff = MediaConverter.SerializeRaw(rp48_1);
r.Write(buff); r.Write(buff);
break;
// For 128k Spectrums and the Pentagon 128, all pages (0-7) are saved. block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
case MachineType.ZXSpectrum128: var rp48_2 = s.GetZXSTRAMPAGE(5, s._machine.RAM2);
case MachineType.ZXSpectrum128Plus2: block.dwSize = (uint)Marshal.SizeOf(rp48_2);
case MachineType.ZXSpectrum128Plus2a: buff = MediaConverter.SerializeRaw(block);
case MachineType.ZXSpectrum128Plus3: r.Write(buff);
List<byte[]> rams = new List<byte[]> buff = MediaConverter.SerializeRaw(rp48_2);
{ r.Write(buff);
s._machine.RAM0, s._machine.RAM1, s._machine.RAM2, s._machine.RAM3, break;
s._machine.RAM4, s._machine.RAM5, s._machine.RAM6, s._machine.RAM7 // For 128k Spectrums and the Pentagon 128, all pages (0-7) are saved.
}; case MachineType.ZXSpectrum128:
for (byte i = 0; i < 8; i++) case MachineType.ZXSpectrum128Plus2:
{ case MachineType.ZXSpectrum128Plus2a:
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0); case MachineType.ZXSpectrum128Plus3:
var rp = s.GetZXSTRAMPAGE(i, rams[i]); List<byte[]> rams = new List<byte[]>
block.dwSize = (uint)Marshal.SizeOf(rp); {
buff = MediaConverter.SerializeRaw(block); s._machine.RAM0, s._machine.RAM1, s._machine.RAM2, s._machine.RAM3,
r.Write(buff); s._machine.RAM4, s._machine.RAM5, s._machine.RAM6, s._machine.RAM7
buff = MediaConverter.SerializeRaw(rp); };
r.Write(buff); for (byte i = 0; i < 8; i++)
} {
break; 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 // ZXSTPLUS3
if (s._machine.Spectrum.MachineType == MachineType.ZXSpectrum128Plus3) if (s._machine.Spectrum.MachineType == MachineType.ZXSpectrum128Plus3)
{ {
@ -223,201 +223,201 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
r.Write(terminator); r.Write(terminator);
} }
*/ */
}
result = ms.ToArray(); }
}
return result; result = ms.ToArray();
} }
private ZXSTRAMPAGE GetZXSTRAMPAGE(byte page, byte[] RAM) return result;
{ }
var s = new ZXSTRAMPAGE();
s.wFlags = 0; // uncompressed only at the moment
s.chPageNo = page;
s.ramPage = RAM;
return s;
}
private ZXSTCREATOR GetZXSTCREATOR() private ZXSTRAMPAGE GetZXSTRAMPAGE(byte page, byte[] RAM)
{ {
var s = new ZXSTCREATOR(); var s = new ZXSTRAMPAGE();
var str = "BIZHAWK EMULATOR".ToCharArray(); s.wFlags = 0; // uncompressed only at the moment
s.szCreator = new char[32]; s.chPageNo = page;
for (int i = 0; i < str.Length; i++) s.ramPage = RAM;
s.szCreator[i] = str[i]; return s;
s.chMajorVersion = 1; }
s.chMinorVersion = 4;
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() return s;
{ }
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;
}
private ZXSTSPECREGS GetZXSTSPECREGS() private ZXSTZ80REGS GetZXSTZ80REGS()
{ {
var s = new ZXSTSPECREGS(); var s = new ZXSTZ80REGS();
s.chBorder = _machine.ULADevice.BorderColor > 7 ? (byte)0 : (byte)_machine.ULADevice.BorderColor; s.AF = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["AF"].Value;
s.chFe = _machine.LastFe; s.BC = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["BC"].Value;
byte x7ffd = (byte)_machine.RAMPaged; s.DE = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["DE"].Value;
byte x1ffd = 0; s.HL = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["HL"].Value;
switch (_machine.Spectrum.MachineType) s.AF1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow AF"].Value;
{ s.BC1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow BC"].Value;
case MachineType.ZXSpectrum16: s.DE1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow DE"].Value;
case MachineType.ZXSpectrum48: s.HL1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow HL"].Value;
s.ch7ffd = 0; s.IX = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["IX"].Value;
s.unionPage = 0; s.IY = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["IY"].Value;
break; 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: if (_machine.CPU.EIPending > 0)
case MachineType.ZXSpectrum128Plus2: {
// 7FFD s.chFlags |= ZXSTZF_EILAST;
if (_machine._ROMpaged == 1) }
x7ffd |= 0x10; else if (_machine.CPU.halted)
if (_machine.SHADOWPaged) {
x7ffd |= 0x08; s.chFlags |= ZXSTZF_HALTED;
if (_machine.PagingDisabled) }
x7ffd |= 0x20;
break;
case MachineType.ZXSpectrum128Plus2a: return s;
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;
}
s.ch7ffd = x7ffd; private ZXSTSPECREGS GetZXSTSPECREGS()
s.unionPage = x1ffd; {
return s; 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() case MachineType.ZXSpectrum128:
{ case MachineType.ZXSpectrum128Plus2:
var s = new ZXSTKEYBOARD(); // 7FFD
s.dwFlags = 0; //no issue 2 emulation if (_machine._ROMpaged == 1)
s.chKeyboardJoystick |= (byte)JoystickTypes.ZXSTKJT_NONE; x7ffd |= 0x10;
return s; if (_machine.SHADOWPaged)
} x7ffd |= 0x08;
if (_machine.PagingDisabled)
x7ffd |= 0x20;
break;
private ZXSTJOYSTICK GetZXSTJOYSTICK() case MachineType.ZXSpectrum128Plus2a:
{ case MachineType.ZXSpectrum128Plus3:
var s = new ZXSTJOYSTICK(); if (_machine.UPDDiskDevice.FDD_FLAG_MOTOR)
s.dwFlags = 0; //depreciated x1ffd |= 0x08;
s.chTypePlayer1 |= (byte)JoystickTypes.ZXSTKJT_KEMPSTON; if (_machine.SpecialPagingMode)
s.chTypePlayer2 |= (byte)JoystickTypes.ZXSTKJT_SINCLAIR1; {
return s; 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() s.ch7ffd = x7ffd;
{ s.unionPage = x1ffd;
var s = new ZXSTAYBLOCK(); return s;
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() private ZXSTKEYBOARD GetZXSTKEYBOARD()
{ {
var s = new ZXSTTAPE(); var s = new ZXSTKEYBOARD();
s.wFlags |= (int)CassetteRecorderState.ZXSTTP_EMBEDDED; s.dwFlags = 0; //no issue 2 emulation
s.wCurrentBlockNo = (ushort)_machine.TapeDevice.CurrentDataBlockIndex; s.chKeyboardJoystick |= (byte)JoystickTypes.ZXSTKJT_NONE;
s.dwCompressedSize = _machine.tapeImages[_machine.TapeDevice.CurrentDataBlockIndex].Length; return s;
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() private ZXSTJOYSTICK GetZXSTJOYSTICK()
{ {
var s = new ZXSTPLUS3(); var s = new ZXSTJOYSTICK();
s.chNumDrives = 1; s.dwFlags = 0; //depreciated
s.fMotorOn = _machine.UPDDiskDevice.FDD_FLAG_MOTOR ? (byte)1 : (byte)0; s.chTypePlayer1 |= (byte)JoystickTypes.ZXSTKJT_KEMPSTON;
return s; s.chTypePlayer2 |= (byte)JoystickTypes.ZXSTKJT_SINCLAIR1;
} return s;
}
private ZXSTDSKFILE GetZXSTDSKFILE() private ZXSTAYBLOCK GetZXSTAYBLOCK()
{ {
var s = new ZXSTDSKFILE(); var s = new ZXSTAYBLOCK();
s.wFlags = 0; s.cFlags = 0; // no external units
s.chDriveNum = 0; s.chCurrentRegister = (byte)_machine.AYDevice.SelectedRegister;
s.dwUncompressedSize = 0; var regs = _machine.AYDevice.ExportRegisters();
return s; 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 namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{ {
/// <summary> /// <summary>
/// Structs, Constants and Enums /// Structs, Constants and Enums
/// http://www.spectaculator.com/docs/zx-state/intro.shtml /// http://www.spectaculator.com/docs/zx-state/intro.shtml
/// </summary> /// </summary>
public partial class SZX public partial class SZX
{ {
#region ZX-State Header #region ZX-State Header
public enum MachineIdentifier : byte public enum MachineIdentifier : byte
{ {
ZXSTMID_16K = 0, ZXSTMID_16K = 0,
ZXSTMID_48K = 1, ZXSTMID_48K = 1,
ZXSTMID_128K = 2, ZXSTMID_128K = 2,
ZXSTMID_PLUS2 = 3, ZXSTMID_PLUS2 = 3,
ZXSTMID_PLUS2A = 4, ZXSTMID_PLUS2A = 4,
ZXSTMID_PLUS3 = 5, ZXSTMID_PLUS3 = 5,
ZXSTMID_PLUS3E = 6, ZXSTMID_PLUS3E = 6,
ZXSTMID_PENTAGON128 = 7, ZXSTMID_PENTAGON128 = 7,
ZXSTMID_TC2048 = 8, ZXSTMID_TC2048 = 8,
ZXSTMID_TC2068 = 9, ZXSTMID_TC2068 = 9,
ZXSTMID_SCORPION = 10, ZXSTMID_SCORPION = 10,
ZXSTMID_SE = 11, ZXSTMID_SE = 11,
ZXSTMID_TS2068 = 12, ZXSTMID_TS2068 = 12,
ZXSTMID_PENTAGON512 = 13, ZXSTMID_PENTAGON512 = 13,
ZXSTMID_PENTAGON1024 = 14, ZXSTMID_PENTAGON1024 = 14,
ZXSTMID_NTSC48K = 15, ZXSTMID_NTSC48K = 15,
ZXSTMID_128KE = 16 ZXSTMID_128KE = 16
} }
/// <summary> /// <summary>
/// If set, the emulated Spectrum uses alternate timings (one cycle later than normal timings). If reset, the emulated Spectrum uses standard timings. /// 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. /// This flag is only applicable for the ZXSTMID_16K, ZXSTMID_48K and ZXSTMID_128K models.
/// </summary> /// </summary>
public const int ZXSTMF_ALTERNATETIMINGS = 1; public const int ZXSTMF_ALTERNATETIMINGS = 1;
/// <summary> /// <summary>
/// The zx-state header appears right at the start of a zx-state (.szx) file. /// The zx-state header appears right at the start of a zx-state (.szx) file.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTHEADER public struct ZXSTHEADER
{ {
public uint dwMagic; public uint dwMagic;
public byte chMajorVersion; public byte chMajorVersion;
public byte chMinorVersion; public byte chMinorVersion;
public byte chMachineId; public byte chMachineId;
public byte chFlags; public byte chFlags;
} }
#endregion #endregion
#region ZXSTBLOCK Header #region ZXSTBLOCK Header
/// <summary> /// <summary>
/// Block Header. Each real block starts with this header. /// Block Header. Each real block starts with this header.
/// </summary> /// </summary>
public struct ZXSTBLOCK public struct ZXSTBLOCK
{ {
public uint dwId; public uint dwId;
public uint dwSize; public uint dwSize;
} }
#endregion #endregion
#region ZXSTCREATOR #region ZXSTCREATOR
/// <summary> /// <summary>
/// This block identifies the program that created this zx-state file. /// This block identifies the program that created this zx-state file.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTCREATOR public struct ZXSTCREATOR
{ {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] szCreator; public char[] szCreator;
public short chMajorVersion; public short chMajorVersion;
public short chMinorVersion; public short chMinorVersion;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] chData; public byte[] chData;
} }
#endregion #endregion
#region ZXSTZ80REGS #region ZXSTZ80REGS
/// <summary> /// <summary>
/// The last instruction executed was an EI instruction or an invalid $DD or $FD prefix. /// The last instruction executed was an EI instruction or an invalid $DD or $FD prefix.
/// </summary> /// </summary>
public const int ZXSTZF_EILAST = 1; public const int ZXSTZF_EILAST = 1;
/// <summary> /// <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. /// 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. /// This flag is mutually exclusive with ZXSTZF_EILAST.
/// </summary> /// </summary>
public const int ZXSTZF_HALTED = 2; public const int ZXSTZF_HALTED = 2;
/// <summary> /// <summary>
/// Contains the Z80 registers and other internal state values. It does not contain any specific model registers. /// Contains the Z80 registers and other internal state values. It does not contain any specific model registers.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTZ80REGS public struct ZXSTZ80REGS
{ {
public ushort AF, BC, DE, HL; public ushort AF, BC, DE, HL;
public ushort AF1, BC1, DE1, HL1; public ushort AF1, BC1, DE1, HL1;
public ushort IX, IY, SP, PC; public ushort IX, IY, SP, PC;
public byte I; public byte I;
public byte R; public byte R;
public byte IFF1, IFF2; public byte IFF1, IFF2;
public byte IM; public byte IM;
public uint dwCyclesStart; public uint dwCyclesStart;
public byte chHoldIntReqCycles; public byte chHoldIntReqCycles;
public byte chFlags; public byte chFlags;
public ushort wMemPtr; public ushort wMemPtr;
} }
#endregion #endregion
#region ZXSTSPECREGS #region ZXSTSPECREGS
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTSPECREGS public struct ZXSTSPECREGS
{ {
public byte chBorder; public byte chBorder;
public byte ch7ffd; public byte ch7ffd;
public byte unionPage; public byte unionPage;
public byte chFe; public byte chFe;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] chReserved; public byte[] chReserved;
} }
#endregion #endregion
#region ZXSTAYBLOCK #region ZXSTAYBLOCK
/// <summary> /// <summary>
/// Fuller Box emulation /// Fuller Box emulation
/// </summary> /// </summary>
public const int ZXSTAYF_FULLERBOX = 1; public const int ZXSTAYF_FULLERBOX = 1;
/// <summary> /// <summary>
/// Melodik Soundbox emulation. /// Melodik Soundbox emulation.
/// This is essentially an AY chip for older Spectrums that uses the same ports as that found in 128k Spectrums /// This is essentially an AY chip for older Spectrums that uses the same ports as that found in 128k Spectrums
/// </summary> /// </summary>
public const int ZXSTAYF_128AY = 2; public const int ZXSTAYF_128AY = 2;
/// <summary> /// <summary>
/// The state of the AY chip found in all 128k Spectrums, Pentagons, Scorpions and Timex machines. /// 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. /// This block may also be present for 16k/48k Spectrums if Fuller Box or Melodik emulation is enabled.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTAYBLOCK public struct ZXSTAYBLOCK
{ {
public byte cFlags; public byte cFlags;
public byte chCurrentRegister; public byte chCurrentRegister;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] chAyRegs; public byte[] chAyRegs;
} }
#endregion #endregion
#region ZXSTRAMPAGE #region ZXSTRAMPAGE
/// <summary> /// <summary>
/// Ram pages are compressed using Zlib /// Ram pages are compressed using Zlib
/// </summary> /// </summary>
public const int ZXSTRF_COMPRESSED = 1; public const int ZXSTRF_COMPRESSED = 1;
/// <summary> /// <summary>
/// zx-state files will contain a number of 16KB RAM page blocks, depending on the specific Spectrum model. /// zx-state files will contain a number of 16KB RAM page blocks, depending on the specific Spectrum model.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTRAMPAGE public struct ZXSTRAMPAGE
{ {
public ushort wFlags; public ushort wFlags;
public byte chPageNo; public byte chPageNo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000)]
public byte[] ramPage; public byte[] ramPage;
} }
#endregion #endregion
#region ZXSTKEYBOARD #region ZXSTKEYBOARD
/// <summary> /// <summary>
/// Keyboard state /// Keyboard state
/// </summary> /// </summary>
public const int ZXSTKF_ISSUE2 = 1; public const int ZXSTKF_ISSUE2 = 1;
/// <summary> /// <summary>
/// Supported joystick types /// Supported joystick types
/// </summary> /// </summary>
public enum JoystickTypes public enum JoystickTypes
{ {
ZXSTKJT_KEMPSTON = 0, ZXSTKJT_KEMPSTON = 0,
ZXSTKJT_FULLER = 1, ZXSTKJT_FULLER = 1,
ZXSTKJT_CURSOR = 2, ZXSTKJT_CURSOR = 2,
ZXSTKJT_SINCLAIR1 = 3, ZXSTKJT_SINCLAIR1 = 3,
ZXSTKJT_SINCLAIR2 = 4, ZXSTKJT_SINCLAIR2 = 4,
ZXSTKJT_SPECTRUMPLUS = 5, ZXSTKJT_SPECTRUMPLUS = 5,
ZXSTKJT_TIMEX1 = 6, ZXSTKJT_TIMEX1 = 6,
ZXSTKJT_TIMEX2 = 7, ZXSTKJT_TIMEX2 = 7,
ZXSTKJT_NONE = 8 ZXSTKJT_NONE = 8
} }
/// <summary> /// <summary>
/// The state of the Spectrum keyboard and any keyboard joystick emulation. /// The state of the Spectrum keyboard and any keyboard joystick emulation.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTKEYBOARD public struct ZXSTKEYBOARD
{ {
public uint dwFlags; public uint dwFlags;
public byte chKeyboardJoystick; public byte chKeyboardJoystick;
} }
#endregion #endregion
#region ZXSTJOYSTICK #region ZXSTJOYSTICK
/// <summary> /// <summary>
/// Joystick setup for both players. /// Joystick setup for both players.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTJOYSTICK public struct ZXSTJOYSTICK
{ {
public uint dwFlags; public uint dwFlags;
public byte chTypePlayer1; public byte chTypePlayer1;
public byte chTypePlayer2; public byte chTypePlayer2;
} }
#endregion #endregion
#region ZXSTTAPE #region ZXSTTAPE
/// <summary> /// <summary>
/// Cassette Recorder state /// Cassette Recorder state
/// </summary> /// </summary>
public enum CassetteRecorderState public enum CassetteRecorderState
{ {
ZXSTTP_EMBEDDED = 1, ZXSTTP_EMBEDDED = 1,
ZXSTTP_COMPRESSED = 2 ZXSTTP_COMPRESSED = 2
} }
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTTAPE public struct ZXSTTAPE
{ {
public ushort wCurrentBlockNo; public ushort wCurrentBlockNo;
public ushort wFlags; public ushort wFlags;
public int dwUncompressedSize; public int dwUncompressedSize;
public int dwCompressedSize; public int dwCompressedSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public char[] szFileExtension; public char[] szFileExtension;
} }
#endregion #endregion
#region ZXSTPLUS3 #region ZXSTPLUS3
/// <summary> /// <summary>
/// The number of drives connected to the Spectrum +3 and whether their motors are turned on. /// 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. /// Any blocks specifying which disk files are in which drive will follow this one.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTPLUS3 public struct ZXSTPLUS3
{ {
public byte chNumDrives; public byte chNumDrives;
public byte fMotorOn; public byte fMotorOn;
} }
#endregion #endregion
#region ZXSTDSKFILE #region ZXSTDSKFILE
/// <summary> /// <summary>
/// Not implemented. All disk images are currently links to external .dsk or .ipf files /// Not implemented. All disk images are currently links to external .dsk or .ipf files
/// </summary> /// </summary>
public const int ZXSTDSKF_COMPRESSED = 1; public const int ZXSTDSKF_COMPRESSED = 1;
/// <summary> /// <summary>
/// Not implemented. All disk images are currently links to external .dsk or .ipf files /// Not implemented. All disk images are currently links to external .dsk or .ipf files
/// </summary> /// </summary>
public const int ZXSTDSKF_EMBEDDED = 2; public const int ZXSTDSKF_EMBEDDED = 2;
/// <summary> /// <summary>
/// When a double-sided disk is inserted into a single-sided drive, specifies the side being read from/written to. /// 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. /// If set, Side B is the active side, otherwise it is Side A.
/// </summary> /// </summary>
public const int ZXSTDSKF_SIDEB = 3; public const int ZXSTDSKF_SIDEB = 3;
/// <summary> /// <summary>
/// Each +3 disk drive that has a disk inserted in it will have one of these blocks. /// 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. /// They follow the ZXSTPLUS3 block which identifies the number of drives.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTDSKFILE public struct ZXSTDSKFILE
{ {
public ushort wFlags; public ushort wFlags;
public byte chDriveNum; public byte chDriveNum;
public int dwUncompressedSize; 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