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