Convert spaces to tabs in ZX Spectrum and AmstradCPC cores
This commit is contained in:
parent
fef746dffa
commit
85be6af3d3
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue