BizHawk/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs

320 lines
13 KiB
C#
Raw Normal View History

using BizHawk.Common;
using BizHawk.Emulation.Common;
2017-11-29 21:32:34 +00:00
using BizHawk.Emulation.Cores.Components.Z80A;
using BizHawk.Emulation.Cores.Properties;
2017-11-29 21:32:34 +00:00
using System;
using System.Collections.Generic;
2017-11-30 09:41:30 +00:00
using System.IO;
2017-11-29 21:32:34 +00:00
using System.Linq;
using BizHawk.Emulation.Cores.Components;
using BizHawk.Emulation.Cores.Sound;
2017-11-29 21:32:34 +00:00
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
2018-06-14 10:31:09 +00:00
/// <summary>
/// ZXHawk: Core Class
/// * Main Initialization *
/// </summary>
2017-11-29 21:32:34 +00:00
[Core(
"ZXHawk",
2018-06-13 14:17:19 +00:00
"Asnivor, Alyosha",
2017-11-29 21:32:34 +00:00
isPorted: false,
2018-06-22 15:53:32 +00:00
isReleased: true)]
public partial class ZXSpectrum : IRegionable, IDriveLight
2017-11-29 21:32:34 +00:00
{
public ZXSpectrum(CoreComm comm, IEnumerable<byte[]> files, List<GameInfo> game, object settings, object syncSettings, bool? deterministic)
2018-04-26 11:54:10 +00:00
{
2017-11-29 21:32:34 +00:00
var ser = new BasicServiceProvider(this);
2018-04-26 11:54:10 +00:00
ServiceProvider = ser;
2017-11-29 21:32:34 +00:00
InputCallbacks = new InputCallbackSystem();
MemoryCallbacks = new MemoryCallbackSystem(new[] { "System Bus" });
2017-11-29 21:32:34 +00:00
CoreComm = comm;
2018-03-05 13:29:34 +00:00
_gameInfo = game;
2017-11-29 21:32:34 +00:00
_cpu = new Z80A();
2017-11-30 09:41:30 +00:00
_tracer = new TraceBuffer { Header = _cpu.TraceHeader };
2018-07-02 14:33:32 +00:00
_files = files?.ToList() ?? new List<byte[]>();
2017-11-30 09:41:30 +00:00
if (settings == null)
settings = new ZXSpectrumSettings();
if (syncSettings == null)
syncSettings = new ZXSpectrumSyncSettings();
PutSyncSettings((ZXSpectrumSyncSettings)syncSettings ?? new ZXSpectrumSyncSettings());
PutSettings((ZXSpectrumSettings)settings ?? new ZXSpectrumSettings());
2018-04-26 11:54:10 +00:00
List<JoystickType> joysticks = new List<JoystickType>();
joysticks.Add(((ZXSpectrumSyncSettings)syncSettings).JoystickType1);
joysticks.Add(((ZXSpectrumSyncSettings)syncSettings).JoystickType2);
joysticks.Add(((ZXSpectrumSyncSettings)syncSettings).JoystickType3);
deterministicEmulation = ((ZXSpectrumSyncSettings)syncSettings).DeterministicEmulation;
if (deterministic != null && deterministic == true)
{
if (deterministicEmulation == false)
{
CoreComm.Notify("Forcing Deterministic Emulation");
}
deterministicEmulation = deterministic.Value;
}
MachineType = SyncSettings.MachineType;
switch (MachineType)
2017-11-29 21:32:34 +00:00
{
case MachineType.ZXSpectrum16:
ControllerDefinition = ZXSpectrumControllerDefinition;
Init(MachineType.ZXSpectrum16, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks);
break;
2017-11-29 21:32:34 +00:00
case MachineType.ZXSpectrum48:
2018-04-26 11:54:10 +00:00
ControllerDefinition = ZXSpectrumControllerDefinition;
Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks);
2017-11-29 21:32:34 +00:00
break;
2017-12-05 10:02:57 +00:00
case MachineType.ZXSpectrum128:
ControllerDefinition = ZXSpectrumControllerDefinition;
Init(MachineType.ZXSpectrum128, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks);
2017-12-05 10:02:57 +00:00
break;
2017-12-05 10:26:06 +00:00
case MachineType.ZXSpectrum128Plus2:
ControllerDefinition = ZXSpectrumControllerDefinition;
Init(MachineType.ZXSpectrum128Plus2, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks);
2017-12-05 10:26:06 +00:00
break;
case MachineType.ZXSpectrum128Plus2a:
ControllerDefinition = ZXSpectrumControllerDefinition;
Init(MachineType.ZXSpectrum128Plus2a, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks);
break;
2017-12-07 15:43:28 +00:00
case MachineType.ZXSpectrum128Plus3:
ControllerDefinition = ZXSpectrumControllerDefinition;
Init(MachineType.ZXSpectrum128Plus3, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks);
2017-12-07 15:43:28 +00:00
break;
2018-12-06 12:58:52 +00:00
case MachineType.Pentagon128:
ControllerDefinition = ZXSpectrumControllerDefinition;
Init(MachineType.Pentagon128, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks);
break;
2017-11-29 21:32:34 +00:00
default:
throw new InvalidOperationException("Machine not yet emulated");
2018-04-26 11:54:10 +00:00
}
2017-11-29 21:32:34 +00:00
_cpu.MemoryCallbacks = MemoryCallbacks;
HardReset = _machine.HardReset;
SoftReset = _machine.SoftReset;
2017-11-29 21:32:34 +00:00
_cpu.FetchMemory = _machine.ReadMemory;
_cpu.ReadMemory = _machine.ReadMemory;
_cpu.WriteMemory = _machine.WriteMemory;
_cpu.ReadHardware = _machine.ReadPort;
_cpu.WriteHardware = _machine.WritePort;
2018-04-26 11:54:10 +00:00
_cpu.FetchDB = _machine.PushBus;
2018-05-31 16:54:57 +00:00
_cpu.OnExecFetch = _machine.CPUMon.OnExecFetch;
2017-11-29 21:32:34 +00:00
ser.Register<ITraceable>(_tracer);
ser.Register<IDisassemblable>(_cpu);
ser.Register<IVideoProvider>(_machine.ULADevice);
// initialize sound mixer and attach the various ISoundProvider devices
SoundMixer = new SyncSoundMixer(targetSampleCount: 882);
SoundMixer.PinSource(_machine.BuzzerDevice, "System Beeper", (int)(32767 / 10));
SoundMixer.PinSource(_machine.TapeBuzzer, "Tape Audio", (int)(32767 / 10));
if (_machine.AYDevice != null)
{
SoundMixer.PinSource(_machine.AYDevice, "AY-3-3912");
}
2018-04-26 11:54:10 +00:00
// set audio device settings
if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AY38912))
{
((AY38912)_machine.AYDevice).PanningConfiguration = ((ZXSpectrumSettings)settings).AYPanConfig;
_machine.AYDevice.Volume = ((ZXSpectrumSettings)settings).AYVolume;
}
2018-04-26 11:54:10 +00:00
if (_machine.BuzzerDevice != null)
{
_machine.BuzzerDevice.Volume = ((ZXSpectrumSettings)settings).EarVolume;
}
if (_machine.TapeBuzzer != null)
{
_machine.TapeBuzzer.Volume = ((ZXSpectrumSettings)settings).TapeVolume;
}
2018-07-02 14:33:32 +00:00
DCFilter dc = new DCFilter(SoundMixer, 512);
ser.Register<ISoundProvider>(dc);
2017-11-29 21:32:34 +00:00
HardReset();
2018-04-26 11:54:10 +00:00
SetupMemoryDomains();
2017-11-29 21:32:34 +00:00
}
2018-04-26 11:54:10 +00:00
public Action HardReset;
public Action SoftReset;
2017-11-29 21:32:34 +00:00
private readonly Z80A _cpu;
private readonly TraceBuffer _tracer;
2018-04-26 11:54:10 +00:00
public IController _controller;
public SpectrumBase _machine;
public MachineType MachineType;
2017-11-29 21:32:34 +00:00
2018-04-26 11:54:10 +00:00
public List<GameInfo> _gameInfo;
public List<GameInfo> _tapeInfo = new List<GameInfo>();
public List<GameInfo> _diskInfo = new List<GameInfo>();
private SyncSoundMixer SoundMixer;
2018-03-05 13:29:34 +00:00
private readonly List<byte[]> _files;
2017-11-29 21:32:34 +00:00
2018-03-08 16:51:25 +00:00
public bool DiagRom = false;
private List<string> diagRoms = new List<string>
{
@"\DiagROM.v28",
@"\zx-diagnostics\testrom.bin"
};
private int diagIndex = 1;
2017-11-30 09:41:30 +00:00
2017-11-29 21:32:34 +00:00
private byte[] GetFirmware(int length, params string[] names)
{
if (DiagRom & File.Exists(Directory.GetCurrentDirectory() + diagRoms[diagIndex]))
2017-11-30 09:41:30 +00:00
{
var rom = File.ReadAllBytes(Directory.GetCurrentDirectory() + diagRoms[diagIndex]);
2017-11-30 09:41:30 +00:00
return rom;
}
// Amstrad licensed ROMs are free to distribute and shipped with BizHawk
byte[] embeddedRom = new byte[length];
bool embeddedFound = true;
switch (names.FirstOrDefault())
{
case "48ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_48_ROM));
break;
case "128ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_128_ROM));
break;
case "PLUS2ROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_plus2_rom));
break;
case "PLUS2AROM":
embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_plus2a_rom));
break;
case "PLUS3ROM":
byte[] r0 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM0_bin));
byte[] r1 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM1_bin));
byte[] r2 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM2_bin));
byte[] r3 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM3_bin));
embeddedRom = r0.Concat(r1).Concat(r2).Concat(r3).ToArray();
break;
default:
embeddedFound = false;
break;
}
if (embeddedFound)
return embeddedRom;
// Embedded ROM not found, maybe this is a peripheral ROM?
2017-11-29 21:32:34 +00:00
var result = names.Select(n => CoreComm.CoreFileProvider.GetFirmware("ZXSpectrum", n, false)).FirstOrDefault(b => b != null && b.Length == length);
if (result == null)
{
throw new MissingFirmwareException($"At least one of these firmwares is required: {string.Join(", ", names)}");
}
return result;
}
private MachineType _machineType;
2017-11-29 21:32:34 +00:00
private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed, List<byte[]> files, List<JoystickType> joys)
2017-11-29 21:32:34 +00:00
{
_machineType = machineType;
2017-11-29 21:32:34 +00:00
// setup the emulated model based on the MachineType
switch (machineType)
{
case MachineType.ZXSpectrum16:
_machine = new ZX16(this, _cpu, borderType, files, joys);
var _systemRom16 = GetFirmware(0x4000, "48ROM");
var romData16 = RomData.InitROM(machineType, _systemRom16);
_machine.InitROM(romData16);
break;
2017-11-29 21:32:34 +00:00
case MachineType.ZXSpectrum48:
_machine = new ZX48(this, _cpu, borderType, files, joys);
2017-11-29 21:32:34 +00:00
var _systemRom = GetFirmware(0x4000, "48ROM");
var romData = RomData.InitROM(machineType, _systemRom);
_machine.InitROM(romData);
break;
2017-12-05 10:02:57 +00:00
case MachineType.ZXSpectrum128:
_machine = new ZX128(this, _cpu, borderType, files, joys);
2017-12-05 10:02:57 +00:00
var _systemRom128 = GetFirmware(0x8000, "128ROM");
var romData128 = RomData.InitROM(machineType, _systemRom128);
_machine.InitROM(romData128);
break;
2017-12-05 10:26:06 +00:00
case MachineType.ZXSpectrum128Plus2:
_machine = new ZX128Plus2(this, _cpu, borderType, files, joys);
2017-12-05 10:26:06 +00:00
var _systemRomP2 = GetFirmware(0x8000, "PLUS2ROM");
var romDataP2 = RomData.InitROM(machineType, _systemRomP2);
_machine.InitROM(romDataP2);
break;
case MachineType.ZXSpectrum128Plus2a:
_machine = new ZX128Plus2a(this, _cpu, borderType, files, joys);
var _systemRomP4 = GetFirmware(0x10000, "PLUS2AROM");
var romDataP4 = RomData.InitROM(machineType, _systemRomP4);
_machine.InitROM(romDataP4);
break;
2017-12-07 15:43:28 +00:00
case MachineType.ZXSpectrum128Plus3:
_machine = new ZX128Plus3(this, _cpu, borderType, files, joys);
2017-12-07 15:43:28 +00:00
var _systemRomP3 = GetFirmware(0x10000, "PLUS3ROM");
var romDataP3 = RomData.InitROM(machineType, _systemRomP3);
_machine.InitROM(romDataP3);
//System.Windows.Forms.MessageBox.Show("+3 is not working at all yet :/");
2017-12-07 15:43:28 +00:00
break;
2018-12-06 12:58:52 +00:00
case MachineType.Pentagon128:
_machine = new Pentagon128(this, _cpu, borderType, files, joys);
var _systemRomPen128 = GetFirmware(0x8000, "PentagonROM");
var _systemRomTrdos = GetFirmware(0x4000, "TRDOSROM");
var conc = _systemRomPen128.Concat(_systemRomTrdos).ToArray();
var romDataPen128 = RomData.InitROM(machineType, conc);
_machine.InitROM(romDataPen128);
break;
2017-11-29 21:32:34 +00:00
}
}
#region IRegionable
public DisplayType Region => DisplayType.PAL;
#endregion
#region IDriveLight
public bool DriveLightEnabled
{
get
{
return true;
}
}
public bool DriveLightOn
{
get
{
if (_machine != null &&
2018-04-26 11:54:10 +00:00
(_machine.TapeDevice != null && _machine.TapeDevice.TapeIsPlaying) ||
(_machine.UPDDiskDevice != null && _machine.UPDDiskDevice.DriveLight))
return true;
return false;
}
}
#endregion
2017-11-29 21:32:34 +00:00
}
}