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